first commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
|
||||
apache_log
|
||||
apache_log/*
|
||||
cache/*
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->in(__DIR__)
|
||||
->name('*.php')
|
||||
// The next two rules are enabled by default, kept for clarity
|
||||
->ignoreDotFiles(true)
|
||||
->ignoreVCS(true)
|
||||
// The pattern is matched against each found filename, thus:
|
||||
// - The "/" is needed to avoid having "vendor" match "Newsvendor.php"
|
||||
// - The presence of "node_modules" here doesn't prevent the Finder from recursing on it, so we merge these paths below at the "exclude()"
|
||||
->notPath($ignoredDirectories = ['cypress/', 'js/', 'locale/', 'node_modules/', 'styles/', 'templates/', 'vendor/'])
|
||||
// Ignore root based directories
|
||||
->exclude(array_merge($ignoredDirectories, ['cache', 'dbscripts', 'docs', 'lib', 'public', 'registry', 'schemas']))
|
||||
// Ignores Git folders
|
||||
->notPath((function () {
|
||||
$recursiveIterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator(__DIR__ . '/plugins', FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS | FilesystemIterator::CURRENT_AS_FILEINFO),
|
||||
RecursiveIteratorIterator::SELF_FIRST
|
||||
);
|
||||
$recursiveIterator->setMaxDepth(1);
|
||||
$gitFolders = new CallbackFilterIterator(
|
||||
$recursiveIterator,
|
||||
fn (SplFileInfo $file) => $recursiveIterator->getDepth() === $recursiveIterator->getMaxDepth()
|
||||
&& $file->isDir()
|
||||
// Covers submodules (.git file) and external repositories (.git directory)
|
||||
&& file_exists("{$file}/.git")
|
||||
);
|
||||
$folders = [];
|
||||
foreach ($gitFolders as $folder) {
|
||||
$folders[] = str_replace(__DIR__ . '/', '', $folder);
|
||||
}
|
||||
return $folders;
|
||||
})());
|
||||
|
||||
$rules = include './lib/pkp/.php_cs_rules';
|
||||
$config = new PhpCsFixer\Config();
|
||||
return $config->setRules($rules)
|
||||
->setFinder($finder);
|
||||
@@ -0,0 +1 @@
|
||||
max_execution_time =6000
|
||||
@@ -0,0 +1,35 @@
|
||||
# Open Journal Systems
|
||||
|
||||
[](https://app.travis-ci.com/pkp/ojs)
|
||||
[](https://scrutinizer-ci.com/g/pkp/ojs/?branch=main)
|
||||
|
||||
Open Journal Systems (OJS) is open source software developed by the [Public Knowledge Project](https://pkp.sfu.ca/) to manage scholarly journals. [Learn More](https://pkp.sfu.ca/software/ojs/)
|
||||
|
||||
## Usage
|
||||
|
||||
Read one of these guides to get started using OJS:
|
||||
|
||||
- Read the [Admin Guide](https://docs.pkp.sfu.ca/admin-guide/) to learn how to install and configure the application from an official release package. Use this guide to deploy to production.
|
||||
- Read the [Getting Started](https://docs.pkp.sfu.ca/dev/documentation/en/getting-started) guide to learn how to install the application from this source repository. Use this guide for local development.
|
||||
|
||||
Visit our [Documentation Hub](https://docs.pkp.sfu.ca/) for user guides, tutorials, and technical documentation.
|
||||
|
||||
## Bugs / Feature Requests
|
||||
|
||||
> ⚠️ If you have found a security risk or vulnerability, please read our [security policy](SECURITY.md).
|
||||
|
||||
All issues should be filed at the [pkp/pkp-lib](https://github.com/pkp/pkp-lib/issues/) repository. Feature requests can be made at our [Community Forum](https://forum.pkp.sfu.ca/). Learn more about how to [report a problem](https://docs.pkp.sfu.ca/dev/contributors/#report-a-problem).
|
||||
|
||||
## Community Code of Conduct
|
||||
|
||||
This repository is a PKP community space. All activities here are governed by [PKP's Code of Conduct](https://pkp.sfu.ca/code-of-conduct/). Please review the Code and help us create a welcoming environment for all participants.
|
||||
|
||||
## Contributions
|
||||
|
||||
Read the [Contributor's Guide](https://docs.pkp.sfu.ca/dev/contributors/) to learn how to make a pull request. This document describes our code formatting guidelines as well as information about how we organize stable branches and submodules.
|
||||
|
||||
## License
|
||||
|
||||
This software is released under the the GNU General Public License. See the file `docs/COPYING` included with this distribution for the terms of this license.
|
||||
|
||||
Third parties are welcome to modify and redistribute OJS in entirety or parts according to the terms of this license. PKP also welcomes patches for improvements or bug fixes to the software.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported | End Of Life |
|
||||
| ------- | ----------------------------------------------------- | ------------- |
|
||||
| 3.5.x | :x: Pre-release | 2026 (est) |
|
||||
| 3.4.x | :heavy_check_mark: Active development | 2025 (est) |
|
||||
| 3.3.x | :heavy_check_mark: Active maintenance | 2026 (est) |
|
||||
| 3.2.x | :x: Not supported | 2023 |
|
||||
| 3.1.x | :x: Not supported | 2022 |
|
||||
| 3.0.x | :x: Not supported | 2022 |
|
||||
| 2.x | :x: Not supported | 2021 |
|
||||
| 1.x | :x: Not supported | 2005 (approx) |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
To report a vulnerability, please contact PKP privately using: pkp.contact@gmail.com
|
||||
|
||||
You can expect a response via email to acknowledge your report within 2 working days.
|
||||
|
||||
PKP will then work to verify the vulnerability and assess the risk. This is typically done within the first week of a report. Once these details are known, PKP will file a Github issue entry with limited details for tracking purposes. This initial report will not include enough information to fully disclose the vulnerability but will serve as a point of reference for development and fixes once they are available.
|
||||
|
||||
When a fix is available, PKP will contact its user community privately via mailing list with details of the fix, and leave a window of typically 2 weeks for community members to patch or upgrade before public disclosure.
|
||||
|
||||
PKP then discloses the vulnerability publicly by updating the Github issue entry with complete details and adding a notice about the vulnerability to the software download page (e.g. https://pkp.sfu.ca/software/ojs). At this point, a CVE and credit for the discovery may be added to the entry.
|
||||
|
||||
Depending on the severity of the issue PKP may back-port fixes to releases that are beyond the formal software end-of-life.
|
||||
|
||||
We aim to have a fix available within a week of notification.
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/_dois/BackendDoiHandler.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 BackendDoiHandler
|
||||
*
|
||||
* @ingroup api_v1_backend
|
||||
*
|
||||
* @brief Handle API requests for backend operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\_dois;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\APIResponse;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\security\Role;
|
||||
use Slim\Http\Request as SlimRequest;
|
||||
|
||||
class BackendDoiHandler extends \PKP\API\v1\_dois\PKPBackendDoiHandler
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_handlerPath = '_dois';
|
||||
$this->_endpoints = array_merge_recursive($this->_endpoints, [
|
||||
'PUT' => [
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . "/issues/{issueId:\d+}",
|
||||
'handler' => [$this, 'editIssue'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . "/galleys/{galleyId:\d+}",
|
||||
'handler' => [$this, 'editGalley'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
]
|
||||
]
|
||||
]);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit galley to add DOI
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function editGalley(SlimRequest $slimRequest, APIResponse $response, array $args): \Slim\Http\Response
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$context = $request->getContext();
|
||||
|
||||
$galley = Repo::galley()->get((int)$args['galleyId']);
|
||||
if (!$galley) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
$publicationId = $galley->getData('publicationId');
|
||||
$publication = Repo::publication()->get((int)$publicationId);
|
||||
$submissionId = $publication->getData('submissionId');
|
||||
$submission = Repo::submission()->get((int) $submissionId);
|
||||
|
||||
if ($submission->getData('contextId') !== $context->getId()) {
|
||||
return $response->withStatus(403)->withJsonError('api.dois.403.editItemOutOfContext');
|
||||
}
|
||||
|
||||
$params = $this->convertStringsToSchema(\PKP\services\PKPSchemaService::SCHEMA_GALLEY, $slimRequest->getParsedBody());
|
||||
|
||||
$doi = Repo::doi()->get((int) $params['doiId']);
|
||||
if (!$doi) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.doiNotFound');
|
||||
}
|
||||
|
||||
Repo::galley()->edit($galley, ['doiId' => $doi->getId()]);
|
||||
|
||||
/** @var \PKP\submission\GenreDAO $genreDao */
|
||||
$genreDao = DAORegistry::getDAO('GenreDAO');
|
||||
$genres = $genreDao->getByContextId($context->getId())->toArray();
|
||||
// Re-fetch submission and publication to reflect changes in galley
|
||||
$submission = Repo::submission()->get((int) $submissionId);
|
||||
$publication = Repo::publication()->get((int) $publicationId);
|
||||
$galley = Repo::galley()->get($galley->getId());
|
||||
|
||||
$galleyProps = Repo::galley()->getSchemaMap($submission, $publication, $genres)->map($galley);
|
||||
|
||||
return $response->withJson($galleyProps, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit issue to add DOI
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function editIssue(SlimRequest $slimRequest, APIResponse $response, array $args): \Slim\Http\Response
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$context = $request->getContext();
|
||||
|
||||
$issue = Repo::issue()->get($args['issueId']);
|
||||
if (!$issue) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
if ($issue->getData('journalId') !== $context->getId()) {
|
||||
return $response->withStatus(403)->withJsonError('api.dois.403.editItemOutOfContext');
|
||||
}
|
||||
|
||||
$params = $this->convertStringsToSchema(\PKP\services\PKPSchemaService::SCHEMA_ISSUE, $slimRequest->getParsedBody());
|
||||
|
||||
$doi = Repo::doi()->get((int) $params['doiId']);
|
||||
if (!$doi) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.doiNotFound');
|
||||
}
|
||||
|
||||
Repo::issue()->edit($issue, ['doiId' => $doi->getId()]);
|
||||
$issue = Repo::issue()->get($issue->getId());
|
||||
|
||||
return $response->withJson(Repo::issue()->getSchemaMap()->map(
|
||||
$issue,
|
||||
$context,
|
||||
$this->getUserGroups($context->getId()),
|
||||
$this->getGenres($context->getId())
|
||||
), 200);
|
||||
}
|
||||
|
||||
protected function getUserGroups(int $contextId): LazyCollection
|
||||
{
|
||||
return Repo::userGroup()->getCollector()
|
||||
->filterByContextIds([$contextId])
|
||||
->getMany();
|
||||
}
|
||||
|
||||
protected function getGenres(int $contextId): array
|
||||
{
|
||||
/** @var \PKP\submission\GenreDAO $genreDao */
|
||||
$genreDao = DAORegistry::getDAO('GenreDAO');
|
||||
return $genreDao->getByContextId($contextId)->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_dois Backend DOI API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/_dois/index.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.
|
||||
*
|
||||
* @ingroup api_v1_dois
|
||||
*
|
||||
* @brief Handle API requests for backend DOI operations.
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\_dois\BackendDoiHandler();
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1__email User API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/_email/index.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.
|
||||
*
|
||||
* @ingroup api_v1__email
|
||||
*
|
||||
* @brief Handle requests for user API functions.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\_email\PKPEmailHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_library Library files API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/library/index.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2003-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @ingroup api_v1_library
|
||||
*
|
||||
* @brief Handle API requests for the publisher and submission library files.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\_library\PKPLibraryHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @file api/v1/_payments/BackendPaymentsSettingsHandler.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 BackendPaymentsSettingsHandler
|
||||
*
|
||||
* @ingroup api_v1_backend
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\_payments;
|
||||
|
||||
class BackendPaymentsSettingsHandler extends \PKP\API\v1\_payments\PKPBackendPaymentsSettingsHandler
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_backend Backend API requests for payments settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/_payments/index.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.
|
||||
*
|
||||
* @ingroup api_v1_backend
|
||||
*
|
||||
* @brief Handle requests for backend API.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\_payments\BackendPaymentsSettingsHandler();
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/_submissions/BackendSubmissionsHandler.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 BackendSubmissionsHandler
|
||||
*
|
||||
* @ingroup api_v1_backend
|
||||
*
|
||||
* @brief Handle API requests for backend operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\_submissions;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\payment\ojs\OJSCompletedPaymentDAO;
|
||||
use APP\payment\ojs\OJSPaymentManager;
|
||||
use APP\submission\Collector;
|
||||
use PKP\core\APIResponse;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\security\authorization\SubmissionAccessPolicy;
|
||||
use PKP\security\Role;
|
||||
use PKP\stageAssignment\StageAssignmentDAO;
|
||||
use Slim\Http\Request;
|
||||
|
||||
class BackendSubmissionsHandler extends \PKP\API\v1\_submissions\PKPBackendSubmissionsHandler
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_endpoints = array_merge_recursive($this->_endpoints, [
|
||||
'PUT' => [
|
||||
[
|
||||
'pattern' => '/{contextPath}/api/{version}/_submissions/{submissionId:\d+}/payment',
|
||||
'handler' => [$this, 'payment'],
|
||||
'roles' => [
|
||||
Role::ROLE_ID_SUB_EDITOR,
|
||||
Role::ROLE_ID_MANAGER,
|
||||
Role::ROLE_ID_SITE_ADMIN,
|
||||
Role::ROLE_ID_ASSISTANT,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPHandler::authorize()
|
||||
*/
|
||||
public function authorize($request, &$args, $roleAssignments)
|
||||
{
|
||||
$routeName = $this->getSlimRequest()->getAttribute('route')->getName();
|
||||
|
||||
if ($routeName === 'payment') {
|
||||
$this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments));
|
||||
}
|
||||
|
||||
return parent::authorize($request, $args, $roleAssignments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of submission payments.
|
||||
*
|
||||
* @param Request $slimRequest Slim request object
|
||||
* @param APIResponse $response object
|
||||
* @param array $args arguments
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public function payment($slimRequest, $response, $args)
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$context = $request->getContext();
|
||||
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
|
||||
|
||||
if (!$submission || !$context || $context->getId() != $submission->getContextId()) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
$paymentManager = Application::getPaymentManager($context);
|
||||
$publicationFeeEnabled = $paymentManager->publicationEnabled();
|
||||
if (!$publicationFeeEnabled) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
$params = $slimRequest->getParsedBody();
|
||||
|
||||
if (empty($params['publicationFeeStatus'])) {
|
||||
return $response->withJson([
|
||||
'publicationFeeStatus' => [__('validator.required')],
|
||||
], 400);
|
||||
}
|
||||
|
||||
$completedPaymentDao = DAORegistry::getDAO('OJSCompletedPaymentDAO'); /** @var OJSCompletedPaymentDAO $completedPaymentDao */
|
||||
$publicationFeePayment = $completedPaymentDao->getByAssoc(null, OJSPaymentManager::PAYMENT_TYPE_PUBLICATION, $submission->getId());
|
||||
|
||||
switch ($params['publicationFeeStatus']) {
|
||||
case 'waived':
|
||||
// Check if a waiver already exists; if so, don't do anything.
|
||||
if ($publicationFeePayment && !$publicationFeePayment->getAmount()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If a fulfillment (nonzero amount) already exists, remove it.
|
||||
if ($publicationFeePayment) {
|
||||
$completedPaymentDao->deleteById($publicationFeePayment->getId());
|
||||
}
|
||||
|
||||
// Record a waived payment.
|
||||
$queuedPayment = $paymentManager->createQueuedPayment(
|
||||
$request,
|
||||
OJSPaymentManager::PAYMENT_TYPE_PUBLICATION,
|
||||
$request->getUser()->getId(),
|
||||
$submission->getId(),
|
||||
0,
|
||||
'' // Zero amount, no currency
|
||||
);
|
||||
$paymentManager->queuePayment($queuedPayment);
|
||||
$paymentManager->fulfillQueuedPayment($request, $queuedPayment, 'ManualPayment');
|
||||
break;
|
||||
case 'paid':
|
||||
// Check if a fulfilled payment already exists; if so, don't do anything.
|
||||
if ($publicationFeePayment && $publicationFeePayment->getAmount()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If a waiver (0 amount) already exists, remove it.
|
||||
if ($publicationFeePayment) {
|
||||
$completedPaymentDao->deleteById($publicationFeePayment->getId());
|
||||
}
|
||||
|
||||
// Record a fulfilled payment.
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
|
||||
$submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleIds($submission->getId(), [Role::ROLE_ID_AUTHOR]);
|
||||
$submitterAssignment = $submitterAssignments->next();
|
||||
$queuedPayment = $paymentManager->createQueuedPayment(
|
||||
$request,
|
||||
OJSPaymentManager::PAYMENT_TYPE_PUBLICATION,
|
||||
$submitterAssignment->getUserId(),
|
||||
$submission->getId(),
|
||||
$context->getSetting('publicationFee'),
|
||||
$context->getSetting('currency')
|
||||
);
|
||||
$paymentManager->queuePayment($queuedPayment);
|
||||
$paymentManager->fulfillQueuedPayment($request, $queuedPayment, 'Waiver');
|
||||
break;
|
||||
case 'unpaid':
|
||||
if ($publicationFeePayment) {
|
||||
$completedPaymentDao->deleteById($publicationFeePayment->getId());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return $response->withJson([
|
||||
'publicationFeeStatus' => [__('validator.required')],
|
||||
], 400);
|
||||
}
|
||||
|
||||
return $response->withJson(true);
|
||||
}
|
||||
|
||||
/** @copydoc PKPSubmissionHandler::getSubmissionCollector() */
|
||||
protected function getSubmissionCollector(array $queryParams): Collector
|
||||
{
|
||||
$collector = parent::getSubmissionCollector($queryParams);
|
||||
|
||||
if (isset($queryParams['issueIds'])) {
|
||||
$collector->filterByIssueIds(
|
||||
array_map('intval', $this->paramToArray($queryParams['issueIds']))
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($queryParams['sectionIds'])) {
|
||||
$collector->filterBySectionIds(
|
||||
array_map('intval', $this->paramToArray($queryParams['sectionIds']))
|
||||
);
|
||||
}
|
||||
|
||||
return $collector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_backend Backend API requests for submissions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/_submissions/index.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.
|
||||
*
|
||||
* @ingroup api_v1_backend
|
||||
*
|
||||
* @brief Handle requests for backend API.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\_submissions\BackendSubmissionsHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_uploadPublicFile Email templates API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/uploadPublicFile/index.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.
|
||||
*
|
||||
* @ingroup api_v1_uploadPublicFile
|
||||
*
|
||||
* @brief Handle API requests for uploadPublicFile.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\_uploadPublicFile\PKPUploadPublicFileHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_announcements Email templates API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/announcements/index.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.
|
||||
*
|
||||
* @ingroup api_v1_announcements
|
||||
*
|
||||
* @brief Handle API requests for announcements.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\announcements\PKPAnnouncementHandler();
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* @file api/v1/contexts/ContextHandler.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 ContextHandler
|
||||
*
|
||||
* @ingroup api_v1_contexts
|
||||
*
|
||||
* @brief Handle API requests for contexts (journals/presses).
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\contexts;
|
||||
|
||||
class ContextHandler extends \PKP\API\v1\contexts\PKPContextHandler
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_contexts Context API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/contexts/index.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.
|
||||
*
|
||||
* @ingroup api_v1_contexts
|
||||
*
|
||||
* @brief Handle API requests for contexts (journals/presses).
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\contexts\ContextHandler();
|
||||
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/dois/DoiHandler.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 DoiHandler
|
||||
*
|
||||
* @ingroup api_v1_dois
|
||||
*
|
||||
* @brief Handle API requests for DOI operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\dois;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Issue;
|
||||
use APP\jobs\doi\DepositIssue;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\APIResponse;
|
||||
use PKP\doi\Doi;
|
||||
use PKP\doi\exceptions\DoiException;
|
||||
use PKP\security\Role;
|
||||
use Slim\Http\Request as SlimRequest;
|
||||
use Slim\Http\Response;
|
||||
|
||||
class DoiHandler extends \PKP\API\v1\dois\PKPDoiHandler
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_handlerPath = 'dois';
|
||||
$this->_endpoints = array_merge_recursive($this->_endpoints, [
|
||||
'POST' => [
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/assignDois',
|
||||
'handler' => [$this, 'assignIssueDois'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN]
|
||||
]
|
||||
],
|
||||
'PUT' => [
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/export',
|
||||
'handler' => [$this, 'exportIssues'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/deposit',
|
||||
'handler' => [$this, 'depositIssues'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/markRegistered',
|
||||
'handler' => [$this, 'markIssuesRegistered'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/markUnregistered',
|
||||
'handler' => [$this, 'markIssuesUnregistered'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/issues/markStale',
|
||||
'handler' => [$this, 'markIssuesStale'],
|
||||
'roles' => [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
],
|
||||
],
|
||||
]);
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export XML for configured DOI registration agency
|
||||
*/
|
||||
public function exportIssues(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve and validate issues
|
||||
$requestIds = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($requestIds)) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.noPubObjectIncluded');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$validIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByPublished(true)
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
$invalidIds = array_diff($requestIds, $validIds);
|
||||
if (count($invalidIds)) {
|
||||
return $response->withStatus(400)->withJsonError('api.dois.400.invalidPubObjectIncluded');
|
||||
}
|
||||
|
||||
/** @var Issue[] $issues */
|
||||
$issues = [];
|
||||
foreach ($requestIds as $id) {
|
||||
$issues[] = Repo::issue()->get($id);
|
||||
}
|
||||
|
||||
if (empty($issues[0])) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.doiNotFound');
|
||||
}
|
||||
|
||||
$agency = $context->getConfiguredDoiAgency();
|
||||
if ($agency === null) {
|
||||
return $response->withStatus(400)->withJsonError('api.dois.400.noRegistrationAgencyConfigured');
|
||||
}
|
||||
|
||||
// Invoke IDoiRegistrationAgency::exportIssues
|
||||
$responseData = $agency->exportIssues($issues, $context);
|
||||
if (!empty($responseData['xmlErrors'])) {
|
||||
return $response->withStatus(400)->withJsonError('api.dois.400.xmlExportFailed');
|
||||
}
|
||||
return $response->withJson(['temporaryFileId' => $responseData['temporaryFileId']], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposit XML for configured DOI registration agency
|
||||
*/
|
||||
public function depositIssues(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve and validate issues
|
||||
$requestIds = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($requestIds)) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.noPubObjectIncluded');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$validIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByPublished(true)
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
$invalidIds = array_diff($requestIds, $validIds);
|
||||
if (count($invalidIds)) {
|
||||
return $response->withStatus(400)->withJsonError('api.dois.400.invalidPubObjectIncluded');
|
||||
}
|
||||
|
||||
$agency = $context->getConfiguredDoiAgency();
|
||||
if ($agency === null) {
|
||||
return $response->withStatus(400)->withJsonError('api.dois.400.noRegistrationAgencyConfigured');
|
||||
}
|
||||
|
||||
$doisToUpdate = [];
|
||||
foreach ($requestIds as $issueId) {
|
||||
dispatch(new DepositIssue($issueId, $context, $agency));
|
||||
array_merge($doisToUpdate, Repo::doi()->getDoisForIssue($issueId));
|
||||
}
|
||||
Repo::doi()->markSubmitted($doisToUpdate);
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark submission DOIs as registered with a DOI registration agency.
|
||||
*/
|
||||
public function markIssuesRegistered(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve issues
|
||||
$requestIds = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($requestIds)) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.noPubObjectIncluded');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$validIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByPublished(true)
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
$invalidIds = array_diff($requestIds, $validIds);
|
||||
if (count($invalidIds)) {
|
||||
$failedDoiActions = array_map(function (int $id) {
|
||||
$issueTitle = Repo::issue()->get($id)?->getIssueIdentification() ?? 'Issue not found';
|
||||
return new DoiException(DoiException::ISSUE_NOT_PUBLISHED, $issueTitle, $issueTitle);
|
||||
}, $invalidIds);
|
||||
|
||||
return $response->withJson(
|
||||
[
|
||||
'failedDoiActions' => array_map(
|
||||
function (DoiException $item) {
|
||||
return $item->getMessage();
|
||||
},
|
||||
$failedDoiActions
|
||||
)
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($requestIds as $id) {
|
||||
$doiIds = Repo::doi()->getDoisForIssue($id);
|
||||
foreach ($doiIds as $doiId) {
|
||||
Repo::doi()->markRegistered($doiId);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark issues DOIs as no longer registered with a DOI registration agency.
|
||||
*/
|
||||
public function markIssuesUnregistered(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve issues
|
||||
$requestIds = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($requestIds)) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.noPubObjectIncluded');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$validIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
$invalidIds = array_diff($requestIds, $validIds);
|
||||
if (count($invalidIds)) {
|
||||
$failedDoiActions = array_map(function (int $id) {
|
||||
$issueTitle = Repo::issue()->get($id)?->getIssueIdentification() ?? 'Issue not found';
|
||||
return new DoiException(DoiException::INCORRECT_ISSUE_CONTEXT, $issueTitle, $issueTitle);
|
||||
}, $invalidIds);
|
||||
|
||||
return $response->withJson(
|
||||
[
|
||||
'failedDoiActions' => array_map(
|
||||
function (DoiException $item) {
|
||||
return $item->getMessage();
|
||||
},
|
||||
$failedDoiActions
|
||||
)
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($requestIds as $id) {
|
||||
$doiIds = Repo::doi()->getDoisForIssue($id);
|
||||
foreach ($doiIds as $doiId) {
|
||||
Repo::doi()->markUnregistered($doiId);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark submission DOIs as stale, indicating a need to be resubmitted to registration agency with updated metadata.
|
||||
*/
|
||||
public function markIssuesStale(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve issues
|
||||
$requestIds = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($requestIds)) {
|
||||
return $response->withStatus(404)->withJsonError('api.dois.404.noPubObjectIncluded');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$validIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByPublished(true)
|
||||
// Items can only be considered stale if they have been deposited/queued for deposit in the first place
|
||||
->filterByDoiStatuses([Doi::STATUS_SUBMITTED, Doi::STATUS_REGISTERED])
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
$invalidIds = array_diff($requestIds, $validIds);
|
||||
if (count($invalidIds)) {
|
||||
$failedDoiActions = array_map(function (int $id) {
|
||||
$issueTitle = Repo::issue()->get($id)?->getIssueIdentification() ?? 'Issue not found';
|
||||
return new DoiException(DoiException::INCORRECT_STALE_STATUS, $issueTitle, $issueTitle);
|
||||
}, $invalidIds);
|
||||
|
||||
return $response->withJson(
|
||||
[
|
||||
'failedDoiActions' => array_map(
|
||||
function (DoiException $item) {
|
||||
return $item->getMessage();
|
||||
},
|
||||
$failedDoiActions
|
||||
)
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($requestIds as $id) {
|
||||
$doiIds = Repo::doi()->getDoisForIssue($id);
|
||||
Repo::doi()->markStale($doiIds);
|
||||
}
|
||||
|
||||
return $response->withStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign DOIs to issue
|
||||
*/
|
||||
public function assignIssueDois(SlimRequest $slimRequest, APIResponse $response, array $args): Response
|
||||
{
|
||||
// Retrieve issues
|
||||
$ids = $slimRequest->getParsedBody()['ids'] ?? [];
|
||||
if (!count($ids)) {
|
||||
return $response->withStatus(404)->withJsonError('api.issue.404.issuesNotFound');
|
||||
}
|
||||
|
||||
$context = $this->getRequest()->getContext();
|
||||
$doiPrefix = $context->getData(Context::SETTING_DOI_PREFIX);
|
||||
if (empty($doiPrefix)) {
|
||||
return $response->withStatus(403)->withJsonError('api.dois.403.prefixRequired');
|
||||
}
|
||||
|
||||
$failedDoiActions = [];
|
||||
|
||||
// Assign DOIs
|
||||
foreach ($ids as $id) {
|
||||
$issue = Repo::issue()->get($id);
|
||||
if ($issue !== null) {
|
||||
$creationFailureResults = Repo::issue()->createDoi($issue);
|
||||
$failedDoiActions = array_merge($failedDoiActions, $creationFailureResults);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($failedDoiActions)) {
|
||||
return $response->withJson(
|
||||
[
|
||||
'failedDoiActions' => array_map(
|
||||
function (DoiException $item) {
|
||||
return $item->getMessage();
|
||||
},
|
||||
$failedDoiActions
|
||||
)
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
return $response->withJson(['failedDoiActions' => $failedDoiActions], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPDoiHandler::getPubObjectHandler()
|
||||
*/
|
||||
protected function getPubObjectHandler(string $type): mixed
|
||||
{
|
||||
$handler = parent::getPubObjectHandler($type);
|
||||
if ($handler !== null) {
|
||||
return $handler;
|
||||
}
|
||||
|
||||
return match ($type) {
|
||||
Repo::doi()::TYPE_ISSUE => Repo::issue(),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_dois DOI API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/dois/index.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.
|
||||
*
|
||||
* @ingroup api_v1_dois
|
||||
*
|
||||
* @brief Handle API requests for DOI operations.
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\dois\DoiHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_emailTemplates Email templates API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/emailTemplates/index.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.
|
||||
*
|
||||
* @ingroup api_v1_emailTemplates
|
||||
*
|
||||
* @brief Handle API requests for emailTemplates.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\emailTemplates\PKPEmailTemplateHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_highlights Highlights API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/highlights/index.php
|
||||
*
|
||||
* Copyright (c) 2014-2023 Simon Fraser University
|
||||
* Copyright (c) 2003-2023 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @ingroup api_v1_highlights
|
||||
*
|
||||
* @brief Handle API requests for highlights.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\highlights\HighlightsHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_institutions Institution API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/institutions/index.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.
|
||||
*
|
||||
* @ingroup api_v1_institutions
|
||||
*
|
||||
* @brief Handle API requests for institutions.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\institutions\PKPInstitutionHandler();
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/issues/IssueHandler.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 IssueHandler
|
||||
*
|
||||
* @ingroup api_v1_issues
|
||||
*
|
||||
* @brief Handle API requests for issues operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\issues;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Collector;
|
||||
use APP\security\authorization\OjsIssueRequiredPolicy;
|
||||
use APP\security\authorization\OjsJournalMustPublishPolicy;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\APIResponse;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\handler\APIHandler;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\security\authorization\ContextAccessPolicy;
|
||||
use PKP\security\authorization\ContextRequiredPolicy;
|
||||
use PKP\security\authorization\UserRolesRequiredPolicy;
|
||||
use PKP\security\Role;
|
||||
use PKP\submission\GenreDAO;
|
||||
use Slim\Http\Request;
|
||||
|
||||
class IssueHandler extends APIHandler
|
||||
{
|
||||
/** @var int The default number of issues to return in one request */
|
||||
public const DEFAULT_COUNT = 20;
|
||||
|
||||
/** @var int The maximum number of issues to return in one request */
|
||||
public const MAX_COUNT = 100;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_handlerPath = 'issues';
|
||||
$roles = [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT, Role::ROLE_ID_REVIEWER, Role::ROLE_ID_AUTHOR];
|
||||
$this->_endpoints = [
|
||||
'GET' => [
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern(),
|
||||
'handler' => [$this, 'getMany'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/current',
|
||||
'handler' => [$this, 'getCurrent'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/{issueId:\d+}',
|
||||
'handler' => [$this, 'get'],
|
||||
'roles' => $roles
|
||||
],
|
||||
]
|
||||
];
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
//
|
||||
// Implement methods from PKPHandler
|
||||
//
|
||||
public function authorize($request, &$args, $roleAssignments)
|
||||
{
|
||||
$routeName = null;
|
||||
$slimRequest = $this->getSlimRequest();
|
||||
|
||||
if (!is_null($slimRequest) && ($route = $slimRequest->getAttribute('route'))) {
|
||||
$routeName = $route->getName();
|
||||
}
|
||||
|
||||
$this->addPolicy(new UserRolesRequiredPolicy($request), true);
|
||||
$this->addPolicy(new ContextRequiredPolicy($request));
|
||||
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
|
||||
$this->addPolicy(new OjsJournalMustPublishPolicy($request));
|
||||
|
||||
if ($routeName === 'get') {
|
||||
$this->addPolicy(new OjsIssueRequiredPolicy($request, $args));
|
||||
}
|
||||
|
||||
return parent::authorize($request, $args, $roleAssignments);
|
||||
}
|
||||
|
||||
//
|
||||
// Public handler methods
|
||||
//
|
||||
/**
|
||||
* Get a collection of issues
|
||||
*
|
||||
* @param Request $slimRequest Slim request object
|
||||
* @param APIResponse $response object
|
||||
* @param array $args arguments
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public function getMany($slimRequest, $response, $args)
|
||||
{
|
||||
$collector = Repo::issue()->getCollector()
|
||||
->limit(self::DEFAULT_COUNT)
|
||||
->offset(0);
|
||||
|
||||
$request = $this->getRequest();
|
||||
$currentUser = $request->getUser();
|
||||
$context = $request->getContext();
|
||||
|
||||
if (!$context) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
// Process query params to format incoming data as needed
|
||||
foreach ($slimRequest->getQueryParams() as $param => $val) {
|
||||
switch ($param) {
|
||||
case 'orderBy':
|
||||
if (in_array($val, [Collector::ORDERBY_DATE_PUBLISHED, Collector::ORDERBY_LAST_MODIFIED, Collector::ORDERBY_SEQUENCE])) {
|
||||
$collector->orderBy($val);
|
||||
}
|
||||
break;
|
||||
|
||||
// Enforce a maximum count to prevent the API from crippling the
|
||||
// server
|
||||
case 'count':
|
||||
$collector->limit(min((int) $val, self::MAX_COUNT));
|
||||
break;
|
||||
|
||||
case 'offset':
|
||||
$collector->offset((int) $val);
|
||||
break;
|
||||
|
||||
// Always convert volume, number and year values to array
|
||||
case 'volumes':
|
||||
case 'volume':
|
||||
case 'numbers':
|
||||
case 'number':
|
||||
case 'years':
|
||||
case 'year':
|
||||
|
||||
// Support deprecated `year`, `number` and `volume` params
|
||||
if (substr($param, -1) !== 's') {
|
||||
$param .= 's';
|
||||
}
|
||||
|
||||
if (is_string($val)) {
|
||||
$val = explode(',', $val);
|
||||
} elseif (!is_array($val)) {
|
||||
$val = [$val];
|
||||
}
|
||||
$values = array_map('intval', $val);
|
||||
switch ($param) {
|
||||
case 'volumes':
|
||||
$collector->filterByVolumes($values);
|
||||
break;
|
||||
case 'numbers':
|
||||
$collector->filterByNumbers($values);
|
||||
break;
|
||||
case 'years':
|
||||
$collector->filterByYears($values);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'isPublished':
|
||||
$collector->filterByPublished((bool) $val);
|
||||
break;
|
||||
|
||||
case 'searchPhrase':
|
||||
$collector->searchPhrase($val);
|
||||
break;
|
||||
case 'doiStatus':
|
||||
$collector->filterByDoiStatuses(array_map('intval', $this->paramToArray($val)));
|
||||
break;
|
||||
case 'hasDois':
|
||||
$collector->filterByHasDois((bool) $val, $context->getEnabledDoiTypes());
|
||||
}
|
||||
}
|
||||
|
||||
$collector->filterByContextIds([$context->getId()]);
|
||||
|
||||
Hook::call('API::issues::params', [&$collector, $slimRequest]);
|
||||
|
||||
// You must be a manager or site admin to access unpublished Issues
|
||||
$isAdmin = $currentUser->hasRole([Role::ROLE_ID_MANAGER], $context->getId()) || $currentUser->hasRole([Role::ROLE_ID_SITE_ADMIN], \PKP\core\PKPApplication::CONTEXT_SITE);
|
||||
if (isset($collector->isPublished) && !$collector->isPublished && !$isAdmin) {
|
||||
return $response->withStatus(403)->withJsonError('api.submissions.403.unpublishedIssues');
|
||||
} elseif (!$isAdmin) {
|
||||
$collector->filterByPublished(true);
|
||||
}
|
||||
|
||||
$issues = $collector->getMany();
|
||||
|
||||
return $response->withJson([
|
||||
'items' => Repo::issue()->getSchemaMap()->summarizeMany($issues, $context)->values(),
|
||||
'itemsMax' => $collector->limit(null)->offset(null)->getCount(),
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current issue
|
||||
*
|
||||
* @param Request $slimRequest Slim request object
|
||||
* @param APIResponse $response object
|
||||
* @param array $args arguments
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public function getCurrent($slimRequest, $response, $args)
|
||||
{
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$issue = Repo::issue()->getCurrent($context->getId());
|
||||
|
||||
if (!$issue) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
$data = Repo::issue()->getSchemaMap()->map(
|
||||
$issue,
|
||||
$context,
|
||||
$this->getUserGroups($context->getId()),
|
||||
$this->getGenres($context->getId())
|
||||
);
|
||||
|
||||
return $response->withJson($data, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single issue
|
||||
*
|
||||
* @param Request $slimRequest Slim request object
|
||||
* @param APIResponse $response object
|
||||
* @param array $args arguments
|
||||
*
|
||||
* @return APIResponse
|
||||
*/
|
||||
public function get($slimRequest, $response, $args)
|
||||
{
|
||||
$context = $this->getRequest()->getContext();
|
||||
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
if (!$issue) {
|
||||
return $response->withStatus(404)->withJsonError('api.404.resourceNotFound');
|
||||
}
|
||||
|
||||
$data = Repo::issue()->getSchemaMap()->map(
|
||||
$issue,
|
||||
$context,
|
||||
$this->getUserGroups($context->getId()),
|
||||
$this->getGenres($context->getId())
|
||||
);
|
||||
|
||||
return $response->withJson($data, 200);
|
||||
}
|
||||
|
||||
protected function getUserGroups(int $contextId): LazyCollection
|
||||
{
|
||||
return Repo::userGroup()->getCollector()
|
||||
->filterByContextIds([$contextId])
|
||||
->getMany();
|
||||
}
|
||||
|
||||
protected function getGenres(int $contextId): array
|
||||
{
|
||||
/** @var GenreDAO $genreDao */
|
||||
$genreDao = DAORegistry::getDAO('GenreDAO');
|
||||
return $genreDao->getByContextId($contextId)->toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_issues Issues API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/issues/index.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.
|
||||
*
|
||||
* @ingroup api_v1_issues
|
||||
*
|
||||
* @brief Handle requests for issues API functions.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\issues\IssueHandler();
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_jobs Queue Jobs API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/jobs/index.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.
|
||||
*
|
||||
* @ingroup api_v1_users
|
||||
*
|
||||
* @brief Handle requests for Queue Jobs API functions.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\jobs\PKPJobHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_mailables Email templates API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/mailables/index.php
|
||||
*
|
||||
* Copyright (c) 2014-2022 Simon Fraser University
|
||||
* Copyright (c) 2003-2022 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @ingroup api_v1_mailables
|
||||
*
|
||||
* @brief Handle API requests for mailables.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\mailables\MailableHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_site Site API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/site/index.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.
|
||||
*
|
||||
* @ingroup api_v1_site
|
||||
*
|
||||
* @brief Handle API requests for the site object.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\site\PKPSiteHandler();
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/stats/StatsEditorialHandler.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 StatsEditorialHandler
|
||||
*
|
||||
* @ingroup api_v1_stats
|
||||
*
|
||||
* @brief Handle API requests for editorial statistics.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\stats\editorial;
|
||||
|
||||
class StatsEditorialHandler extends \PKP\API\v1\stats\editorial\PKPStatsEditorialHandler
|
||||
{
|
||||
/** @var string The name of the section ids query param for this application */
|
||||
public $sectionIdsQueryParam = 'sectionIds';
|
||||
|
||||
public function getSectionIdsQueryParam()
|
||||
{
|
||||
return $this->sectionIdsQueryParam;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_stats Publication statistics API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/stats/index.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.
|
||||
*
|
||||
* @ingroup api_v1_stats
|
||||
*
|
||||
* @brief Handle API requests for publication statistics
|
||||
*
|
||||
*/
|
||||
|
||||
use APP\core\Application;
|
||||
|
||||
$requestPath = Application::get()->getRequest()->getRequestPath();
|
||||
if (strpos($requestPath, '/stats/publications')) {
|
||||
return new \APP\API\v1\stats\publications\StatsPublicationHandler();
|
||||
} elseif (strpos($requestPath, '/stats/editorial')) {
|
||||
return new \APP\API\v1\stats\editorial\StatsEditorialHandler();
|
||||
} elseif (strpos($requestPath, '/stats/users')) {
|
||||
return new \PKP\API\v1\stats\users\PKPStatsUserHandler();
|
||||
} elseif (strpos($requestPath, '/stats/issues')) {
|
||||
return new \APP\API\v1\stats\issues\StatsIssueHandler();
|
||||
} elseif (strpos($requestPath, '/stats/contexts')) {
|
||||
return new \PKP\API\v1\stats\contexts\PKPStatsContextHandler();
|
||||
} elseif (strpos($requestPath, '/stats/sushi')) {
|
||||
return new \APP\API\v1\stats\sushi\StatsSushiHandler();
|
||||
} else {
|
||||
http_response_code('404');
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
'error' => 'api.404.endpointNotFound',
|
||||
'errorMessage' => __('api.404.endpointNotFound'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,463 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/stats/StatsIssueHandler.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 StatsIssueHandler
|
||||
*
|
||||
* @ingroup api_v1_stats
|
||||
*
|
||||
* @brief Handle API requests for issue statistics.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\stats\issues;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use APP\security\authorization\OjsIssueRequiredPolicy;
|
||||
use APP\statistics\StatisticsHelper;
|
||||
use PKP\core\APIResponse;
|
||||
use PKP\handler\APIHandler;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\security\authorization\ContextAccessPolicy;
|
||||
use PKP\security\authorization\PolicySet;
|
||||
use PKP\security\authorization\RoleBasedHandlerOperationPolicy;
|
||||
use PKP\security\authorization\UserRolesRequiredPolicy;
|
||||
use PKP\security\Role;
|
||||
use Slim\Http\Request as SlimHttpRequest;
|
||||
|
||||
class StatsIssueHandler extends APIHandler
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_handlerPath = 'stats/issues';
|
||||
$roles = [Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER /*, Role::ROLE_ID_SUB_EDITOR */];
|
||||
$this->_endpoints = [
|
||||
'GET' => [
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern(),
|
||||
'handler' => [$this, 'getMany'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/timeline',
|
||||
'handler' => [$this, 'getManyTimeline'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/{issueId:\d+}',
|
||||
'handler' => [$this, 'get'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/{issueId:\d+}/timeline',
|
||||
'handler' => [$this, 'getTimeline'],
|
||||
'roles' => $roles
|
||||
],
|
||||
],
|
||||
];
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPHandler::authorize()
|
||||
*/
|
||||
public function authorize($request, &$args, $roleAssignments)
|
||||
{
|
||||
$routeName = null;
|
||||
$slimRequest = $this->getSlimRequest();
|
||||
|
||||
$this->addPolicy(new UserRolesRequiredPolicy($request), true);
|
||||
|
||||
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
|
||||
|
||||
$rolePolicy = new PolicySet(PolicySet::COMBINING_PERMIT_OVERRIDES);
|
||||
foreach ($roleAssignments as $role => $operations) {
|
||||
$rolePolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, $role, $operations));
|
||||
}
|
||||
$this->addPolicy($rolePolicy);
|
||||
|
||||
if (!is_null($slimRequest) && ($route = $slimRequest->getAttribute('route'))) {
|
||||
$routeName = $route->getName();
|
||||
}
|
||||
if (in_array($routeName, ['get', 'getGalley', 'getToc'])) {
|
||||
$this->addPolicy(new OjsIssueRequiredPolicy($request, $args));
|
||||
}
|
||||
|
||||
return parent::authorize($request, $args, $roleAssignments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get usage stats for a set of issues
|
||||
*
|
||||
* Returns total views by toc and all galleys.
|
||||
*/
|
||||
public function getMany(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
$responseCSV = str_contains($slimRequest->getHeaderLine('Accept'), APIResponse::RESPONSE_CSV) ? true : false;
|
||||
|
||||
$defaultParams = [
|
||||
'count' => 30,
|
||||
'offset' => 0,
|
||||
'orderDirection' => StatisticsHelper::STATISTICS_ORDER_DESC,
|
||||
];
|
||||
|
||||
$requestParams = array_merge($defaultParams, $slimRequest->getQueryParams());
|
||||
|
||||
$allowedParams = $this->_processAllowedParams($requestParams, [
|
||||
'dateStart',
|
||||
'dateEnd',
|
||||
'count',
|
||||
'offset',
|
||||
'orderDirection',
|
||||
'searchPhrase',
|
||||
'issueIds',
|
||||
]);
|
||||
|
||||
Hook::call('API::stats::issues::params', [&$allowedParams, $slimRequest]);
|
||||
|
||||
$allowedParams['contextIds'] = [$request->getContext()->getId()];
|
||||
|
||||
$result = $this->_validateStatDates($allowedParams);
|
||||
if ($result !== true) {
|
||||
return $response->withStatus(400)->withJsonError($result);
|
||||
}
|
||||
|
||||
if (!in_array($allowedParams['orderDirection'], [StatisticsHelper::STATISTICS_ORDER_ASC, StatisticsHelper::STATISTICS_ORDER_DESC])) {
|
||||
return $response->withStatus(400)->withJsonError('api.stats.400.invalidOrderDirection');
|
||||
}
|
||||
|
||||
// Identify issues which should be included in the results when a searchPhrase is passed
|
||||
if (!empty($allowedParams['searchPhrase'])) {
|
||||
$allowedIssueIds = empty($allowedParams['issueIds']) ? [] : $allowedParams['issueIds'];
|
||||
$allowedParams['issueIds'] = $this->_processSearchPhrase($allowedParams['searchPhrase'], $allowedIssueIds);
|
||||
|
||||
if (empty($allowedParams['issueIds'])) {
|
||||
$csvColumnNames = $this->_getIssueReportColumnNames();
|
||||
if ($responseCSV) {
|
||||
return $response->withCSV([], $csvColumnNames, 0);
|
||||
} else {
|
||||
return $response->withJson([
|
||||
'items' => [],
|
||||
'itemsMax' => 0,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get a list (count number) of top issues by total (toc + galley) views
|
||||
$statsService = Services::get('issueStats');
|
||||
$totalMetrics = $statsService->getTotals($allowedParams);
|
||||
|
||||
// Get the stats for each issue
|
||||
$items = [];
|
||||
foreach ($totalMetrics as $totalMetric) {
|
||||
$issueId = $totalMetric->issue_id;
|
||||
$dateStart = array_key_exists('dateStart', $allowedParams) ? $allowedParams['dateStart'] : null;
|
||||
$dateEnd = array_key_exists('dateEnd', $allowedParams) ? $allowedParams['dateEnd'] : null;
|
||||
$metricsByType = $statsService->getTotalsByType($issueId, $this->getRequest()->getContext()->getId(), $dateStart, $dateEnd);
|
||||
|
||||
if ($responseCSV) {
|
||||
$items[] = $this->getItemForCSV($issueId, $metricsByType['toc'], $metricsByType['galley']);
|
||||
} else {
|
||||
$items[] = $this->getItemForJSON($issueId, $metricsByType['toc'], $metricsByType['galley']);
|
||||
}
|
||||
}
|
||||
|
||||
$itemsMax = $statsService->getCount($allowedParams);
|
||||
$csvColumnNames = $this->_getIssueReportColumnNames();
|
||||
if ($responseCSV) {
|
||||
return $response->withCSV($items, $csvColumnNames, $itemsMax);
|
||||
} else {
|
||||
return $response->withJson([
|
||||
'items' => $items,
|
||||
'itemsMax' => $itemsMax,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total TOC or issue galley views for a set of issues
|
||||
* in a timeline broken down by month or day
|
||||
*/
|
||||
public function getManyTimeline(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
$responseCSV = str_contains($slimRequest->getHeaderLine('Accept'), APIResponse::RESPONSE_CSV) ? true : false;
|
||||
|
||||
$request = $this->getRequest();
|
||||
|
||||
$defaultParams = [
|
||||
'timelineInterval' => StatisticsHelper::STATISTICS_DIMENSION_MONTH,
|
||||
];
|
||||
|
||||
$requestParams = array_merge($defaultParams, $slimRequest->getQueryParams());
|
||||
|
||||
$allowedParams = $this->_processAllowedParams($requestParams, [
|
||||
'dateStart',
|
||||
'dateEnd',
|
||||
'timelineInterval',
|
||||
'searchPhrase',
|
||||
'type'
|
||||
]);
|
||||
|
||||
Hook::call('API::stats::issues::timeline::params', [&$allowedParams, $slimRequest]);
|
||||
|
||||
if (!$this->isValidTimelineInterval($allowedParams['timelineInterval'])) {
|
||||
return $response->withStatus(400)->withJsonError('api.stats.400.wrongTimelineInterval');
|
||||
}
|
||||
|
||||
$result = $this->_validateStatDates($allowedParams);
|
||||
if ($result !== true) {
|
||||
return $response->withStatus(400)->withJsonError($result);
|
||||
}
|
||||
|
||||
$allowedParams['contextIds'] = [$request->getContext()->getId()];
|
||||
$allowedParams['assocTypes'] = [Application::ASSOC_TYPE_ISSUE];
|
||||
if (array_key_exists('type', $allowedParams) && $allowedParams['type'] == 'files') {
|
||||
$allowedParams['assocTypes'] = [Application::ASSOC_TYPE_ISSUE_GALLEY];
|
||||
};
|
||||
|
||||
// Identify issues which should be included in the results when a searchPhrase is passed
|
||||
if (!empty($allowedParams['searchPhrase'])) {
|
||||
$allowedIssueIds = empty($allowedParams['issueIds']) ? [] : $allowedParams['issueIds'];
|
||||
$allowedParams['issueIds'] = $this->_processSearchPhrase($allowedParams['searchPhrase'], $allowedIssueIds);
|
||||
|
||||
if (empty($allowedParams['issueIds'])) {
|
||||
$dateStart = empty($allowedParams['dateStart']) ? StatisticsHelper::STATISTICS_EARLIEST_DATE : $allowedParams['dateStart'];
|
||||
$dateEnd = empty($allowedParams['dateEnd']) ? date('Ymd', strtotime('yesterday')) : $allowedParams['dateEnd'];
|
||||
$emptyTimeline = Services::get('issueStats')->getEmptyTimelineIntervals($dateStart, $dateEnd, $allowedParams['timelineInterval']);
|
||||
if ($responseCSV) {
|
||||
$csvColumnNames = Services::get('issueStats')->getTimelineReportColumnNames();
|
||||
return $response->withCSV($emptyTimeline, $csvColumnNames, 0);
|
||||
}
|
||||
return $response->withJson($emptyTimeline, 200);
|
||||
}
|
||||
}
|
||||
|
||||
$data = Services::get('issueStats')->getTimeline($allowedParams['timelineInterval'], $allowedParams);
|
||||
if ($responseCSV) {
|
||||
$csvColumnNames = Services::get('issueStats')->getTimelineReportColumnNames();
|
||||
return $response->withCSV($data, $csvColumnNames, count($data));
|
||||
}
|
||||
return $response->withJson($data, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single issue's usage statistics
|
||||
*/
|
||||
public function get(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$allowedParams = $this->_processAllowedParams($slimRequest->getQueryParams(), [
|
||||
'dateStart',
|
||||
'dateEnd',
|
||||
]);
|
||||
|
||||
Hook::call('API::stats::issue::params', [&$allowedParams, $slimRequest]);
|
||||
|
||||
$result = $this->_validateStatDates($allowedParams);
|
||||
if ($result !== true) {
|
||||
return $response->withStatus(400)->withJsonError($result);
|
||||
}
|
||||
|
||||
$statsService = Services::get('issueStats');
|
||||
$dateStart = array_key_exists('dateStart', $allowedParams) ? $allowedParams['dateStart'] : null;
|
||||
$dateEnd = array_key_exists('dateEnd', $allowedParams) ? $allowedParams['dateEnd'] : null;
|
||||
$metricsByType = $statsService->getTotalsByType($issue->getId(), $request->getContext()->getId(), $dateStart, $dateEnd);
|
||||
|
||||
return $response->withJson([
|
||||
'tocViews' => $metricsByType['toc'],
|
||||
'issueGalleyViews' => $metricsByType['galley'],
|
||||
'issue' => Repo::issue()->getSchemaMap()->mapToStats($issue),
|
||||
], 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total TOC or issue galley views for an issue broken down by
|
||||
* month or day
|
||||
*/
|
||||
public function getTimeline(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$defaultParams = [
|
||||
'timelineInterval' => StatisticsHelper::STATISTICS_DIMENSION_MONTH,
|
||||
];
|
||||
|
||||
$requestParams = array_merge($defaultParams, $slimRequest->getQueryParams());
|
||||
|
||||
$allowedParams = $this->_processAllowedParams($requestParams, [
|
||||
'dateStart',
|
||||
'dateEnd',
|
||||
'timelineInterval',
|
||||
'type'
|
||||
]);
|
||||
|
||||
Hook::call('API::stats::issue::timeline::params', [&$allowedParams, $slimRequest]);
|
||||
|
||||
$allowedParams['contextIds'] = [$request->getContext()->getId()];
|
||||
$allowedParams['issueIds'] = [$issue->getId()];
|
||||
$allowedParams['assocTypes'] = [Application::ASSOC_TYPE_ISSUE];
|
||||
if (array_key_exists('type', $allowedParams) && $allowedParams['type'] == 'files') {
|
||||
$allowedParams['assocTypes'] = [Application::ASSOC_TYPE_ISSUE_GALLEY];
|
||||
};
|
||||
|
||||
if (!$this->isValidTimelineInterval($allowedParams['timelineInterval'])) {
|
||||
return $response->withStatus(400)->withJsonError('api.stats.400.wrongTimelineInterval');
|
||||
}
|
||||
|
||||
$result = $this->_validateStatDates($allowedParams);
|
||||
if ($result !== true) {
|
||||
return $response->withStatus(400)->withJsonError($result);
|
||||
}
|
||||
|
||||
$statsService = Services::get('issueStats');
|
||||
$data = $statsService->getTimeline($allowedParams['timelineInterval'], $allowedParams);
|
||||
return $response->withJson($data, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to filter and sanitize the request params
|
||||
*
|
||||
* Only allows the specified params through and enforces variable
|
||||
* type where needed.
|
||||
*/
|
||||
protected function _processAllowedParams(array $requestParams, array $allowedParams): array
|
||||
{
|
||||
$returnParams = [];
|
||||
foreach ($requestParams as $requestParam => $value) {
|
||||
if (!in_array($requestParam, $allowedParams)) {
|
||||
continue;
|
||||
}
|
||||
switch ($requestParam) {
|
||||
case 'dateStart':
|
||||
case 'dateEnd':
|
||||
case 'timelineInterval':
|
||||
case 'searchPhrase':
|
||||
case 'type':
|
||||
$returnParams[$requestParam] = $value;
|
||||
break;
|
||||
|
||||
case 'count':
|
||||
$returnParams[$requestParam] = min(100, (int) $value);
|
||||
break;
|
||||
|
||||
case 'offset':
|
||||
$returnParams[$requestParam] = (int) $value;
|
||||
break;
|
||||
|
||||
case 'orderDirection':
|
||||
$returnParams[$requestParam] = strtoupper($value);
|
||||
break;
|
||||
|
||||
case 'issueIds':
|
||||
if (is_string($value) && strpos($value, ',') > -1) {
|
||||
$value = explode(',', $value);
|
||||
} elseif (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
$returnParams[$requestParam] = array_map('intval', $value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $returnParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to get the issueIds param when a searchPhase
|
||||
* param is also passed.
|
||||
*
|
||||
* If the searchPhrase and issueIds params were both passed in the
|
||||
* request, then we only return IDs that match both conditions.
|
||||
*/
|
||||
protected function _processSearchPhrase(string $searchPhrase, array $issueIds = []): array
|
||||
{
|
||||
$searchPhraseIssueIds = Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([Application::get()->getRequest()->getContext()->getId()])
|
||||
->filterByPublished(true)
|
||||
->searchPhrase($searchPhrase)
|
||||
->getIds()
|
||||
->toArray();
|
||||
|
||||
if (!empty($issueIds)) {
|
||||
return array_intersect($issueIds, $searchPhraseIssueIds);
|
||||
}
|
||||
return $searchPhraseIssueIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column names for the issue CSV report
|
||||
*/
|
||||
protected function _getIssueReportColumnNames(): array
|
||||
{
|
||||
return [
|
||||
__('common.id'),
|
||||
__('editor.issues.issueIdentification'),
|
||||
__('stats.total'),
|
||||
__('stats.views'),
|
||||
__('stats.downloads')
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CSV row with issue metrics
|
||||
*/
|
||||
protected function getItemForCSV(int $issueId, int $tocViews, int $issueGalleyViews): array
|
||||
{
|
||||
$totalViews = $tocViews + $issueGalleyViews;
|
||||
// Get issue identification for display
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
$identification = $issue->getIssueIdentification();
|
||||
return [
|
||||
$issueId,
|
||||
$identification,
|
||||
$totalViews,
|
||||
$tocViews,
|
||||
$issueGalleyViews
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JSON data with issue metrics
|
||||
*/
|
||||
protected function getItemForJSON(int $issueId, int $tocViews, int $issueGalleyViews): array
|
||||
{
|
||||
$totalViews = $tocViews + $issueGalleyViews;
|
||||
// Get basic issue details for display
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
$issueProps = Repo::issue()->getSchemaMap()->mapToStats($issue);
|
||||
return [
|
||||
'totalViews' => $totalViews,
|
||||
'tocViews' => $tocViews,
|
||||
'issueGalleyViews' => $issueGalleyViews,
|
||||
'issue' => $issueProps,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the timeline interval is valid
|
||||
*/
|
||||
protected function isValidTimelineInterval(string $interval): bool
|
||||
{
|
||||
return in_array($interval, [
|
||||
StatisticsHelper::STATISTICS_DIMENSION_DAY,
|
||||
StatisticsHelper::STATISTICS_DIMENSION_MONTH
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/stats/publications/StatsPublicationHandler.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 StatsPublicationHandler
|
||||
*
|
||||
* @ingroup api_v1_stats
|
||||
*
|
||||
* @brief Handle API requests for publication statistics.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\stats\publications;
|
||||
|
||||
class StatsPublicationHandler extends \PKP\API\v1\stats\publications\PKPStatsPublicationHandler
|
||||
{
|
||||
/** @var string The name of the section ids query param for this application */
|
||||
public $sectionIdsQueryParam = 'sectionIds';
|
||||
|
||||
public function getSectionIdsQueryParam()
|
||||
{
|
||||
return $this->sectionIdsQueryParam;
|
||||
}
|
||||
|
||||
protected function getManyAllowedParams()
|
||||
{
|
||||
$params = parent::getManyAllowedParams();
|
||||
$params[] = 'issueIds';
|
||||
return $params;
|
||||
}
|
||||
|
||||
protected function getManyTimelineAllowedParams()
|
||||
{
|
||||
$params = parent::getManyTimelineAllowedParams();
|
||||
$params[] = 'issueIds';
|
||||
return $params;
|
||||
}
|
||||
|
||||
protected function _processParam(string $requestParam, mixed $value): array
|
||||
{
|
||||
if ($requestParam == 'issueIds') {
|
||||
$returnParams = [];
|
||||
if (is_string($value) && str_contains($value, ',')) {
|
||||
$value = explode(',', $value);
|
||||
} elseif (!is_array($value)) {
|
||||
$value = [$value];
|
||||
}
|
||||
$returnParams[$requestParam] = array_map('intval', $value);
|
||||
} else {
|
||||
$returnParams = parent::_processParam($requestParam, $value);
|
||||
}
|
||||
return $returnParams;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/stats/sushi/StatsSushiHandler.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 StatsSushiHandler
|
||||
*
|
||||
* @ingroup api_v1_stats
|
||||
*
|
||||
* @brief Handle API requests for COUNTER R5 SUSHI statistics.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\stats\sushi;
|
||||
|
||||
use APP\sushi\IR;
|
||||
use APP\sushi\IR_A1;
|
||||
use APP\sushi\TR;
|
||||
use APP\sushi\TR_J3;
|
||||
use PKP\core\APIResponse;
|
||||
use Slim\Http\Request as SlimHttpRequest;
|
||||
|
||||
class StatsSushiHandler extends \PKP\API\v1\stats\sushi\PKPStatsSushiHandler
|
||||
{
|
||||
/**
|
||||
* Get this API's endpoints definitions
|
||||
*/
|
||||
protected function getGETDefinitions(array $roles = null): array
|
||||
{
|
||||
return array_merge(
|
||||
parent::getGETDefinitions($roles),
|
||||
[
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/reports/tr',
|
||||
'handler' => [$this, 'getReportsTR'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/reports/tr_j3',
|
||||
'handler' => [$this, 'getReportsTRJ3'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/reports/ir',
|
||||
'handler' => [$this, 'getReportsIR'],
|
||||
'roles' => $roles
|
||||
],
|
||||
[
|
||||
'pattern' => $this->getEndpointPattern() . '/reports/ir_a1',
|
||||
'handler' => [$this, 'getReportsIRA1'],
|
||||
'roles' => $roles
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNTER 'Title Master Report' [TR].
|
||||
* A customizable report detailing activity at the journal level
|
||||
* that allows the user to apply filters and select other configuration options for the report.
|
||||
*/
|
||||
public function getReportsTR(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
return $this->getReportResponse(new TR(), $slimRequest, $response, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNTER 'Journal Usage by Access Type' [TR_J3].
|
||||
* This is a Standard View of Title Master Report that reports on usage of journal content for all Metric_Types broken down by Access_Type.
|
||||
*/
|
||||
public function getReportsTRJ3(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
return $this->getReportResponse(new TR_J3(), $slimRequest, $response, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNTER 'Item Master Report' [IR].
|
||||
* A customizable report detailing activity at the article level
|
||||
* that allows the user to apply filters and select other configuration options for the report.
|
||||
*/
|
||||
public function getReportsIR(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
return $this->getReportResponse(new IR(), $slimRequest, $response, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* COUNTER 'Journal Article Requests' [IR_A1].
|
||||
* This is a Standard View of Item Master Report that reports on journal article requests at the article level.
|
||||
*/
|
||||
public function getReportsIRA1(SlimHttpRequest $slimRequest, APIResponse $response, array $args): APIResponse
|
||||
{
|
||||
return $this->getReportResponse(new IR_A1(), $slimRequest, $response, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application specific list of reports supported by the API
|
||||
*/
|
||||
protected function getReportList(): array
|
||||
{
|
||||
return array_merge(parent::getReportList(), [
|
||||
[
|
||||
'Report_Name' => 'Title Master Report',
|
||||
'Report_ID' => 'TR',
|
||||
'Release' => '5',
|
||||
'Report_Description' => __('sushi.reports.tr.description'),
|
||||
'Path' => 'reports/tr'
|
||||
],
|
||||
[
|
||||
'Report_Name' => 'Journal Usage by Access Type',
|
||||
'Report_ID' => 'TR_J3',
|
||||
'Release' => '5',
|
||||
'Report_Description' => __('sushi.reports.tr_j3.description'),
|
||||
'Path' => 'reports/tr_j3'
|
||||
],
|
||||
[
|
||||
'Report_Name' => 'Item Master Report',
|
||||
'Report_ID' => 'IR',
|
||||
'Release' => '5',
|
||||
'Report_Description' => __('sushi.reports.ir.description'),
|
||||
'Path' => 'reports/ir'
|
||||
],
|
||||
[
|
||||
'Report_Name' => 'Journal Article Requests',
|
||||
'Report_ID' => 'IR_A1',
|
||||
'Release' => '5',
|
||||
'Report_Description' => __('sushi.reports.ir_a1.description'),
|
||||
'Path' => 'reports/ir_a1'
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/submissions/SubmissionHandler.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 SubmissionHandler
|
||||
*
|
||||
* @ingroup api_v1_submission
|
||||
*
|
||||
* @brief Handle API requests for submission operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\submissions;
|
||||
|
||||
use APP\submission\Collector;
|
||||
|
||||
class SubmissionHandler extends \PKP\API\v1\submissions\PKPSubmissionHandler
|
||||
{
|
||||
/** @copydoc PKPSubmissionHandler::getSubmissionCollector() */
|
||||
protected function getSubmissionCollector(array $queryParams): Collector
|
||||
{
|
||||
$collector = parent::getSubmissionCollector($queryParams);
|
||||
|
||||
if (isset($queryParams['issueIds'])) {
|
||||
$collector->filterByIssueIds(
|
||||
array_map('intval', $this->paramToArray($queryParams['issueIds']))
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($queryParams['sectionIds'])) {
|
||||
$collector->filterBySectionIds(
|
||||
array_map('intval', $this->paramToArray($queryParams['sectionIds']))
|
||||
);
|
||||
}
|
||||
|
||||
return $collector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_submissions Submission API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/submissions/index.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.
|
||||
*
|
||||
* @ingroup api_v1_submissions
|
||||
*
|
||||
* @brief Handle requests for submission API functions.
|
||||
*
|
||||
*/
|
||||
$urlParts = explode('/', trim($_SERVER['PATH_INFO'], '/'));
|
||||
if (count($urlParts) >= 6 && $urlParts[5] == 'files') {
|
||||
return new \PKP\API\v1\submissions\PKPSubmissionFileHandler();
|
||||
} else {
|
||||
return new \APP\API\v1\submissions\SubmissionHandler();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_temporaryFiles Temporary file upload API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/temporaryFiles/index.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.
|
||||
*
|
||||
* @ingroup api_v1_temporaryFiles
|
||||
*
|
||||
* @brief Handle API requests for temporary file uploading.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\temporaryFiles\PKPTemporaryFilesHandler();
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file api/v1/users/UserHandler.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 UserHandler
|
||||
*
|
||||
* @ingroup api_v1_users
|
||||
*
|
||||
* @brief Handle API requests for user operations.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\API\v1\users;
|
||||
|
||||
class UserHandler extends \PKP\API\v1\users\PKPUserHandler
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup api_v1_users User API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/users/index.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.
|
||||
*
|
||||
* @ingroup api_v1_users
|
||||
*
|
||||
* @brief Handle requests for user API functions.
|
||||
*
|
||||
*/
|
||||
|
||||
return new \APP\API\v1\users\UserHandler();
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup api_v1_vocabs Controlled vocabulary API requests
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file api/v1/vocabs/index.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.
|
||||
*
|
||||
* @ingroup api_v1_vocabs
|
||||
*
|
||||
* @brief Handle API requests for vocabs.
|
||||
*/
|
||||
|
||||
return new \PKP\API\v1\vocabs\PKPVocabHandler();
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/article/ArticleTombstoneManager.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 ArticleTombstoneManager
|
||||
*
|
||||
* @ingroup article
|
||||
*
|
||||
* @brief Class defining basic operations for article tombstones.
|
||||
*/
|
||||
|
||||
namespace APP\article;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\oai\ojs\OAIDAO;
|
||||
use APP\submission\Submission;
|
||||
use PKP\config\Config;
|
||||
use PKP\context\Context;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class ArticleTombstoneManager
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function insertArticleTombstone($article, $journal, $section)
|
||||
{
|
||||
$tombstoneDao = DAORegistry::getDAO('DataObjectTombstoneDAO'); /** @var \PKP\tombstone\DataObjectTombstoneDAO $tombstoneDao */
|
||||
// delete article tombstone -- to ensure that there aren't more than one tombstone for this article
|
||||
$tombstoneDao->deleteByDataObjectId($article->getId());
|
||||
// insert article tombstone
|
||||
$setSpec = OAIDAO::setSpec($journal, $section);
|
||||
$oaiIdentifier = 'oai:' . Config::getVar('oai', 'repository_id') . ':' . 'article/' . $article->getId();
|
||||
$OAISetObjectsIds = [
|
||||
Application::ASSOC_TYPE_JOURNAL => $journal->getId(),
|
||||
Application::ASSOC_TYPE_SECTION => $section->getId(),
|
||||
];
|
||||
|
||||
$articleTombstone = $tombstoneDao->newDataObject();
|
||||
$articleTombstone->setDataObjectId($article->getId());
|
||||
$articleTombstone->stampDateDeleted();
|
||||
$articleTombstone->setSetSpec($setSpec);
|
||||
$articleTombstone->setSetName($section->getLocalizedTitle());
|
||||
$articleTombstone->setOAIIdentifier($oaiIdentifier);
|
||||
$articleTombstone->setOAISetObjectsIds($OAISetObjectsIds);
|
||||
$tombstoneDao->insertObject($articleTombstone);
|
||||
|
||||
if (Hook::call('ArticleTombstoneManager::insertArticleTombstone', [&$articleTombstone, &$article, &$journal, &$section])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert tombstone for every published submission
|
||||
*/
|
||||
public function insertTombstonesByContext(Context $context)
|
||||
{
|
||||
$submissions = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByStatus([Submission::STATUS_PUBLISHED])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$section = Repo::section()->get($submission->getSectionId());
|
||||
$this->insertArticleTombstone($submission, $context, $section);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete tombstones for published submissions in this context
|
||||
*/
|
||||
public function deleteTombstonesByContextId(int $contextId)
|
||||
{
|
||||
$tombstoneDao = DAORegistry::getDAO('DataObjectTombstoneDAO'); /** @var \PKP\tombstone\DataObjectTombstoneDAO $tombstoneDao */
|
||||
$submissions = Repo::submission()->getCollector()
|
||||
->filterByContextIds([$contextId])
|
||||
->filterByStatus([Submission::STATUS_PUBLISHED])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$tombstoneDao->deleteByDataObjectId($submission->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\article\ArticleTombstoneManager', '\ArticleTombstoneManager');
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/author/Author.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 Author
|
||||
*
|
||||
* @ingroup author
|
||||
*
|
||||
* @brief Article author metadata class.
|
||||
*/
|
||||
|
||||
namespace APP\author;
|
||||
|
||||
class Author extends \PKP\author\Author
|
||||
{
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
// Required for import/export toolset
|
||||
class_alias('\APP\author\Author', '\Author');
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/author/DAO.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 DAO
|
||||
*
|
||||
* @ingroup author
|
||||
*
|
||||
* @see Author
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Author objects.
|
||||
*/
|
||||
|
||||
namespace APP\author;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\journal\Journal;
|
||||
use APP\journal\JournalDAO;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\db\DAOResultFactory;
|
||||
use PKP\db\DBResultRange;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\identity\Identity;
|
||||
use PKP\submission\PKPSubmission;
|
||||
|
||||
class DAO extends \PKP\author\DAO
|
||||
{
|
||||
/**
|
||||
* Retrieve all published authors for a journal by the first letter of the family name.
|
||||
* Authors will be sorted by (family, given). Note that if journalId is null,
|
||||
* alphabetized authors for all enabled journals are returned.
|
||||
* If authors have the same given names, first names and affiliations in all journal locales,
|
||||
* as well as country and email (optional), they are considered to be the same.
|
||||
*
|
||||
* @param int $journalId Optional journal ID to restrict results to
|
||||
* @param string $initial An initial a family name must begin with, "-" for authors with no family names
|
||||
* @param ?DBResultRange $rangeInfo Range information
|
||||
* @param bool $includeEmail Whether or not to include the email in the select distinct
|
||||
*
|
||||
* @return DAOResultFactory<Author> Authors ordered by last name, given name
|
||||
*
|
||||
* @deprecated 3.4.0.0
|
||||
*
|
||||
*/
|
||||
public function getAuthorsAlphabetizedByJournal($journalId = null, $initial = null, $rangeInfo = null, $includeEmail = false)
|
||||
{
|
||||
$locale = Locale::getLocale();
|
||||
$params = [
|
||||
Identity::IDENTITY_SETTING_GIVENNAME, $locale,
|
||||
Identity::IDENTITY_SETTING_GIVENNAME,
|
||||
Identity::IDENTITY_SETTING_FAMILYNAME, $locale,
|
||||
Identity::IDENTITY_SETTING_FAMILYNAME,
|
||||
'issueId',
|
||||
];
|
||||
if (isset($journalId)) {
|
||||
$params[] = $journalId;
|
||||
}
|
||||
|
||||
$supportedLocales = [];
|
||||
if ($journalId !== null) {
|
||||
/** @var JournalDAO */
|
||||
$journalDao = DAORegistry::getDAO('JournalDAO');
|
||||
/** @var Journal */
|
||||
$journal = $journalDao->getById($journalId);
|
||||
$supportedLocales = $journal->getSupportedLocales();
|
||||
} else {
|
||||
$site = Application::get()->getRequest()->getSite();
|
||||
$supportedLocales = $site->getSupportedLocales();
|
||||
}
|
||||
$supportedLocalesCount = count($supportedLocales);
|
||||
$sqlJoinAuthorSettings = $sqlColumnsAuthorSettings = $initialSql = '';
|
||||
if (isset($initial)) {
|
||||
$initialSql = ' AND (';
|
||||
}
|
||||
$index = 0;
|
||||
foreach ($supportedLocales as $locale) {
|
||||
$localeStr = str_replace('@', '_', $locale);
|
||||
$sqlColumnsAuthorSettings .= ",
|
||||
COALESCE(asg{$index}.setting_value, ''), ' ',
|
||||
COALESCE(asf{$index}.setting_value, ''), ' ',
|
||||
COALESCE(SUBSTRING(asa{$index}.setting_value FROM 1 FOR 255), ''), ' '
|
||||
";
|
||||
$sqlJoinAuthorSettings .= "
|
||||
LEFT JOIN author_settings asg{$index} ON (asg{$index}.author_id = aa.author_id AND asg{$index}.setting_name = '" . Identity::IDENTITY_SETTING_GIVENNAME . "' AND asg{$index}.locale = '{$locale}')
|
||||
LEFT JOIN author_settings asf{$index} ON (asf{$index}.author_id = aa.author_id AND asf{$index}.setting_name = '" . Identity::IDENTITY_SETTING_FAMILYNAME . "' AND asf{$index}.locale = '{$locale}')
|
||||
LEFT JOIN author_settings asa{$index} ON (asa{$index}.author_id = aa.author_id AND asa{$index}.setting_name = 'affiliation' AND asa{$index}.locale = '{$locale}')
|
||||
";
|
||||
if (isset($initial)) {
|
||||
if ($initial == '-') {
|
||||
$initialSql .= "(asf{$index}.setting_value IS NULL OR asf{$index}.setting_value = '')";
|
||||
if ($index < $supportedLocalesCount - 1) {
|
||||
$initialSql .= ' AND ';
|
||||
}
|
||||
} else {
|
||||
$params[] = PKPString::strtolower($initial) . '%';
|
||||
$initialSql .= "LOWER(asf{$index}.setting_value) LIKE LOWER(?)";
|
||||
if ($index < $supportedLocalesCount - 1) {
|
||||
$initialSql .= ' OR ';
|
||||
}
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
if (isset($initial)) {
|
||||
$initialSql .= ')';
|
||||
}
|
||||
|
||||
$result = $this->deprecatedDao->retrieveRange(
|
||||
$sql = 'SELECT a.*, ug.show_title, s.locale,
|
||||
COALESCE(agl.setting_value, agpl.setting_value) AS author_given,
|
||||
CASE WHEN agl.setting_value <> \'\' THEN afl.setting_value ELSE afpl.setting_value END AS author_family
|
||||
FROM authors a
|
||||
JOIN user_groups ug ON (a.user_group_id = ug.user_group_id)
|
||||
JOIN publications p ON (p.publication_id = a.publication_id)
|
||||
JOIN submissions s ON (s.current_publication_id = p.publication_id)
|
||||
LEFT JOIN author_settings agl ON (a.author_id = agl.author_id AND agl.setting_name = ? AND agl.locale = ?)
|
||||
LEFT JOIN author_settings agpl ON (a.author_id = agpl.author_id AND agpl.setting_name = ? AND agpl.locale = s.locale)
|
||||
LEFT JOIN author_settings afl ON (a.author_id = afl.author_id AND afl.setting_name = ? AND afl.locale = ?)
|
||||
LEFT JOIN author_settings afpl ON (a.author_id = afpl.author_id AND afpl.setting_name = ? AND afpl.locale = s.locale)
|
||||
JOIN (
|
||||
SELECT
|
||||
MIN(aa.author_id) as author_id,
|
||||
CONCAT(
|
||||
' . ($includeEmail ? 'aa.email, \' \', ' : '') . '
|
||||
ac.setting_value,
|
||||
\' \'
|
||||
' . $sqlColumnsAuthorSettings . '
|
||||
) as names
|
||||
FROM authors aa
|
||||
JOIN publications pp ON (pp.publication_id = aa.publication_id)
|
||||
LEFT JOIN publication_settings ppss ON (ppss.publication_id = pp.publication_id)
|
||||
JOIN submissions ss ON (ss.submission_id = pp.submission_id AND ss.current_publication_id = pp.publication_id AND ss.status = ' . PKPSubmission::STATUS_PUBLISHED . ')
|
||||
JOIN journals j ON (ss.context_id = j.journal_id)
|
||||
JOIN issues i ON (ppss.setting_name = ? AND ppss.setting_value = CAST(i.issue_id AS CHAR(20)) AND i.published = 1)
|
||||
LEFT JOIN author_settings ac ON (ac.author_id = aa.author_id AND ac.setting_name = \'country\')
|
||||
' . $sqlJoinAuthorSettings . '
|
||||
WHERE j.enabled = 1
|
||||
' . (isset($journalId) ? ' AND j.journal_id = ?' : '')
|
||||
. $initialSql . '
|
||||
GROUP BY names
|
||||
) as t1 ON (t1.author_id = a.author_id)
|
||||
ORDER BY author_family, author_given',
|
||||
$params,
|
||||
$rangeInfo
|
||||
);
|
||||
|
||||
return new DAOResultFactory($result, $this, 'fromRow', [], $sql, $params, $rangeInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/StatsIssuePage.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 StatsIssuePage
|
||||
*
|
||||
* @ingroup classes_controllers_stats
|
||||
*
|
||||
* @brief A class to prepare the data object for the issue statistics
|
||||
* UI component
|
||||
*/
|
||||
|
||||
namespace APP\components;
|
||||
|
||||
use PKP\components\PKPStatsComponent;
|
||||
use PKP\statistics\PKPStatisticsHelper;
|
||||
|
||||
class StatsIssuePage 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.issues.countOfTotal'),
|
||||
'betweenDatesLabel' => __('stats.downloadReport.betweenDates'),
|
||||
'allDatesLabel' => __('stats.dateRange.allDates'),
|
||||
'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,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldArchivingPn.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 FieldArchivingPn
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief An extension of the FieldOptions for the configuration setting which
|
||||
* allows a user to enable/disable the PKP Preservation Network plugin, and
|
||||
* access the settings, from a form-like view in the distribution settings.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
|
||||
class FieldArchivingPn extends FieldOptions
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-archiving-pn';
|
||||
|
||||
/** @var string The message to show in a modal when the link is clicked. */
|
||||
public $terms = '';
|
||||
|
||||
/** @var string The message to show when the plugin is disabled. */
|
||||
public $disablePluginSuccess = '';
|
||||
|
||||
/** @var string The message to show when the plugin was enabled.. */
|
||||
public $enablePluginSuccess = '';
|
||||
|
||||
/** @var string The URL to enable the PLN plugin. */
|
||||
public $enablePluginUrl = '';
|
||||
|
||||
/** @var string The URL to disable the PLN plugin. */
|
||||
public $disablePluginUrl = '';
|
||||
|
||||
/** @var string The URL to load the PN plugin settings. */
|
||||
public $settingsUrl = '';
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['terms'] = $this->terms;
|
||||
$config['disablePluginSuccess'] = $this->disablePluginSuccess;
|
||||
$config['enablePluginSuccess'] = $this->enablePluginSuccess;
|
||||
$config['enablePluginUrl'] = $this->enablePluginUrl;
|
||||
$config['disablePluginUrl'] = $this->disablePluginUrl;
|
||||
$config['settingsUrl'] = $this->settingsUrl;
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldSelectIssue.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 FieldSelectIssue
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief An extension of the FieldSelect for selecting an issue.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms;
|
||||
|
||||
use APP\core\Application;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
|
||||
class FieldSelectIssue extends FieldSelect
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-select-issue';
|
||||
|
||||
/** @var int One of the PKPSubmission::STATUS_ constants */
|
||||
public $publicationStatus;
|
||||
|
||||
/**
|
||||
* @copydoc Field::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
$config['publicationStatus'] = $this->publicationStatus;
|
||||
|
||||
$issueUrlPlaceholder = Application::get()->getRequest()->getDispatcher()->url(
|
||||
Application::get()->getRequest(),
|
||||
Application::ROUTE_PAGE,
|
||||
null,
|
||||
'issue',
|
||||
'view',
|
||||
'__issueId__'
|
||||
);
|
||||
|
||||
$config['assignLabel'] = __('publication.assignToissue');
|
||||
$config['assignedNoticeBase'] = __('publication.assignedToIssue', ['issueUrl' => $issueUrlPlaceholder]);
|
||||
$config['changeIssueLabel'] = __('publication.changeIssue');
|
||||
$config['publishedNoticeBase'] = __('publication.publishedIn', ['issueUrl' => $issueUrlPlaceholder]);
|
||||
$config['scheduledNoticeBase'] = __('publication.scheduledIn', ['issueUrl' => $issueUrlPlaceholder]);
|
||||
$config['unscheduledNotice'] = __('publication.unscheduledIn');
|
||||
$config['unscheduleLabel'] = __('publication.unschedule');
|
||||
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/FieldSelectIssues.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 FieldSelectIssues
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A text field to search for and select issues.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms;
|
||||
|
||||
use PKP\components\forms\FieldBaseAutosuggest;
|
||||
|
||||
class FieldSelectIssues extends FieldBaseAutosuggest
|
||||
{
|
||||
/** @copydoc Field::$component */
|
||||
public $component = 'field-select-issues';
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/AccessForm.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 AccessForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the terms under which a journal will
|
||||
* allow access to its published content.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use APP\journal\Journal;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_ACCESS', 'access');
|
||||
define('SUBSCRIPTION_OPEN_ACCESS_DELAY_MIN', '1');
|
||||
define('SUBSCRIPTION_OPEN_ACCESS_DELAY_MAX', '60');
|
||||
|
||||
class AccessForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ACCESS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param Journal $context Journal to change settings for
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$validDelayedOpenAccessDuration[] = ['value' => 0, 'label' => __('common.disabled')];
|
||||
for ($i = SUBSCRIPTION_OPEN_ACCESS_DELAY_MIN; $i <= SUBSCRIPTION_OPEN_ACCESS_DELAY_MAX; $i++) {
|
||||
$validDelayedOpenAccessDuration[] = [
|
||||
'value' => $i,
|
||||
'label' => __('manager.subscriptionPolicies.xMonths', ['x' => $i]),
|
||||
];
|
||||
}
|
||||
|
||||
$this->addField(new FieldOptions('publishingMode', [
|
||||
'label' => __('manager.distribution.publishingMode'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => Journal::PUBLISHING_MODE_OPEN, 'label' => __('manager.distribution.publishingMode.openAccess')],
|
||||
['value' => Journal::PUBLISHING_MODE_SUBSCRIPTION, 'label' => __('manager.distribution.publishingMode.subscription')],
|
||||
['value' => Journal::PUBLISHING_MODE_NONE, 'label' => __('manager.distribution.publishingMode.none')],
|
||||
],
|
||||
'value' => $context->getData('publishingMode'),
|
||||
]))
|
||||
->addField(new FieldSelect('delayedOpenAccessDuration', [
|
||||
'label' => __('about.delayedOpenAccess'),
|
||||
'options' => $validDelayedOpenAccessDuration,
|
||||
'value' => $context->getData('delayedOpenAccessDuration'),
|
||||
'showWhen' => ['publishingMode', Journal::PUBLISHING_MODE_SUBSCRIPTION],
|
||||
]))
|
||||
->addField(new FieldOptions('enableOai', [
|
||||
'label' => __('manager.setup.enableOai'),
|
||||
'description' => __('manager.setup.enableOai.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('common.enable')],
|
||||
['value' => false, 'label' => __('common.disable')],
|
||||
],
|
||||
'value' => $context->getData('enableOai'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/AppearanceAdvancedForm.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 AppearanceAdvancedForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for general website appearance setup, such as uploading
|
||||
* a logo.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPAppearanceAdvancedForm;
|
||||
|
||||
class AppearanceAdvancedForm extends PKPAppearanceAdvancedForm
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/AppearanceSetupForm.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 AppearanceSetupForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for general website appearance setup, such as uploading
|
||||
* a logo.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPAppearanceSetupForm;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
|
||||
class AppearanceSetupForm extends PKPAppearanceSetupForm
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPAppearanceSetupForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $baseUrl, $temporaryFileApiUrl, $uploadImageUrl)
|
||||
{
|
||||
parent::__construct($action, $locales, $context, $baseUrl, $temporaryFileApiUrl, $uploadImageUrl);
|
||||
|
||||
$this->addField(new FieldUploadImage('journalThumbnail', [
|
||||
'label' => __('manager.setup.journalThumbnail'),
|
||||
'tooltip' => __('manager.setup.journalThumbnail.description'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context->getData('journalThumbnail'),
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]), [FIELD_POSITION_AFTER, 'pageHeaderLogoImage']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/ArchivingLockssForm.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 ArchivingLockssForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the LOCKSS and CLOCKSS settings.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use APP\journal\Journal;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_ARCHIVING_LOCKSS', 'archivingLockss');
|
||||
|
||||
class ArchivingLockssForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ARCHIVING_LOCKSS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param Journal $context Journal or Press to change settings for
|
||||
* @param string $lockssUrl URL to the publisher manifest page for LOCKSS
|
||||
* @param string $clockssUrl URL to the publisher manifest page for CLOCKSS
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $lockssUrl, $clockssUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
$this->addField(new FieldOptions('enableLockss', [
|
||||
'label' => __('manager.setup.lockssTitle'),
|
||||
'description' => __('manager.setup.lockssLicenseDescription'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('manager.setup.lockssEnable', ['lockssUrl' => $lockssUrl]),
|
||||
],
|
||||
],
|
||||
'value' => (bool) $context->getData('enableLockss'),
|
||||
]))
|
||||
->addField(new FieldOptions('enableClockss', [
|
||||
'label' => __('manager.setup.clockssTitle'),
|
||||
'description' => __('manager.setup.clockssLicenseDescription'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __('manager.setup.clockssEnable', ['clockssUrl' => $clockssUrl]),
|
||||
],
|
||||
],
|
||||
'value' => (bool) $context->getData('enableClockss'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/ContextForm.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 ContextForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief Add OJS-specific fields to the context add/edit form.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPContextForm;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldText;
|
||||
|
||||
class ContextForm extends PKPContextForm
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPContextForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $locales, $baseUrl, $context)
|
||||
{
|
||||
parent::__construct($action, $locales, $baseUrl, $context);
|
||||
|
||||
$this->addField(new FieldText('abbreviation', [
|
||||
'label' => __('manager.setup.journalAbbreviation'),
|
||||
'isMultilingual' => true,
|
||||
'value' => $context ? $context->getData('abbreviation') : null,
|
||||
]), [FIELD_POSITION_AFTER, 'acronym'])
|
||||
->addField(new FieldOptions('enabled', [
|
||||
'label' => __('common.enable'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('admin.journals.enableJournalInstructions')],
|
||||
],
|
||||
'value' => $context ? (bool) $context->getData('enabled') : false,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/DoiSetupSettingsForm.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 DoiSetupSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for enabling and configuring DOI settings for a given context
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\context\PKPDoiSetupSettingsForm;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\context\Context;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class DoiSetupSettingsForm extends PKPDoiSetupSettingsForm
|
||||
{
|
||||
public function __construct(string $action, array $locales, Context $context)
|
||||
{
|
||||
parent::__construct($action, $locales, $context);
|
||||
|
||||
$this->objectTypeOptions = [
|
||||
[
|
||||
'value' => Repo::doi()::TYPE_PUBLICATION,
|
||||
'label' => __('article.articles'),
|
||||
'allowedBy' => [],
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::TYPE_ISSUE,
|
||||
'label' => __('issue.issues'),
|
||||
'allowedBy' => [],
|
||||
],
|
||||
[
|
||||
'value' => Repo::doi()::TYPE_REPRESENTATION,
|
||||
'label' => __('doi.manager.settings.galleysWithDescription'),
|
||||
'allowedBy' => [],
|
||||
]
|
||||
];
|
||||
Hook::call('DoiSetupSettingsForm::getObjectTypes', [&$this->objectTypeOptions]);
|
||||
if ($this->enabledRegistrationAgency === null) {
|
||||
$filteredOptions = $this->objectTypeOptions;
|
||||
} else {
|
||||
$filteredOptions = array_filter($this->objectTypeOptions, function ($option) {
|
||||
return in_array($this->enabledRegistrationAgency, $option['allowedBy']);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$this->addField(new FieldOptions(Context::SETTING_ENABLED_DOI_TYPES, [
|
||||
'label' => __('doi.manager.settings.doiObjects'),
|
||||
'description' => __('doi.manager.settings.doiObjectsRequired'),
|
||||
'groupId' => self::DOI_SETTINGS_GROUP,
|
||||
'options' => $filteredOptions,
|
||||
'value' => $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ? $context->getData(Context::SETTING_ENABLED_DOI_TYPES) : [],
|
||||
]), [FIELD_POSITION_BEFORE, Context::SETTING_DOI_PREFIX])
|
||||
->addField(new FieldText(Repo::doi()::CUSTOM_ISSUE_PATTERN, [
|
||||
'label' => __('issue.issues'),
|
||||
'groupId' => self::DOI_CUSTOM_SUFFIX_GROUP,
|
||||
'value' => $context->getData(Repo::doi()::CUSTOM_ISSUE_PATTERN),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/LicenseForm.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 LicenseForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief Add OJS-specific details to the license settings forms
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPLicenseForm;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
|
||||
class LicenseForm extends PKPLicenseForm
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_LICENSE;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* @copydoc PKPLicenseForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $locales, $context)
|
||||
{
|
||||
parent::__construct($action, $locales, $context);
|
||||
|
||||
$this->addField(new FieldOptions('copyrightYearBasis', [
|
||||
'label' => __('submission.copyrightYear'),
|
||||
'description' => __('manager.distribution.copyrightYearBasis.description'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => 'issue', 'label' => __('manager.distribution.copyrightYearBasis.issue')],
|
||||
['value' => 'submission', 'label' => __('manager.distribution.copyrightYearBasis.submission')],
|
||||
],
|
||||
'value' => $context->getData('copyrightYearBasis'),
|
||||
]), [FIELD_POSITION_AFTER, 'licenseUrl']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/MastheadForm.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 MastheadForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief Add OJS-specific fields to the masthead form.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPMastheadForm;
|
||||
use PKP\components\forms\FieldText;
|
||||
|
||||
class MastheadForm extends PKPMastheadForm
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPMastheadForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $locales, $context, $imageUploadUrl)
|
||||
{
|
||||
parent::__construct($action, $locales, $context, $imageUploadUrl);
|
||||
|
||||
$this->addField(new FieldText('abbreviation', [
|
||||
'label' => __('manager.setup.journalAbbreviation'),
|
||||
'isMultilingual' => true,
|
||||
'groupId' => 'identity',
|
||||
'value' => $context->getData('abbreviation'),
|
||||
]))
|
||||
->addField(new FieldText('publisherInstitution', [
|
||||
'label' => __('manager.setup.publisher'),
|
||||
'groupId' => 'publishing',
|
||||
'value' => $context->getData('publisherInstitution'),
|
||||
]))
|
||||
->addField(new FieldText('publisherUrl', [
|
||||
'label' => __('common.url'),
|
||||
'groupId' => 'publishing',
|
||||
'value' => $context->getData('publisherUrl'),
|
||||
]))
|
||||
->addField(new FieldText('onlineIssn', [
|
||||
'label' => __('manager.setup.onlineIssn'),
|
||||
'size' => 'small',
|
||||
'groupId' => 'publishing',
|
||||
'value' => $context->getData('onlineIssn'),
|
||||
]))
|
||||
->addField(new FieldText('printIssn', [
|
||||
'label' => __('manager.setup.printIssn'),
|
||||
'size' => 'small',
|
||||
'groupId' => 'publishing',
|
||||
'value' => $context->getData('printIssn'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/MetadataSettingsForm.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 MetadataSettingsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief Add OJS-specific fields to the masthead form.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPMetadataSettingsForm;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
|
||||
class MetadataSettingsForm extends PKPMetadataSettingsForm
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPMetadataSettingsForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $context)
|
||||
{
|
||||
parent::__construct($action, $context);
|
||||
|
||||
$this->addField(new FieldOptions('enablePublisherId', [
|
||||
'label' => __('submission.publisherId'),
|
||||
'description' => __('submission.publisherId.description'),
|
||||
'options' => [
|
||||
[
|
||||
'value' => 'publication',
|
||||
'label' => __('submission.publisherId.enable', ['objects' => __('submission.publications')]),
|
||||
],
|
||||
[
|
||||
'value' => 'galley',
|
||||
'label' => __('submission.publisherId.enable', ['objects' => __('submission.layout.galleys')]),
|
||||
],
|
||||
[
|
||||
'value' => 'issue',
|
||||
'label' => __('submission.publisherId.enable', ['objects' => __('issue.issues')]),
|
||||
],
|
||||
[
|
||||
'value' => 'issueGalley',
|
||||
'label' => __('submission.publisherId.enable', ['objects' => __('editor.issues.galleys')]),
|
||||
],
|
||||
],
|
||||
'value' => $context->getData('enablePublisherId') ? $context->getData('enablePublisherId') : [],
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/ReviewGuidanceForm.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 ReviewGuidanceForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the guidance a reviewer should receive.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPReviewGuidanceForm;
|
||||
|
||||
class ReviewGuidanceForm extends PKPReviewGuidanceForm
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/context/UserAccessForm.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 UserAccessForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief Add OJS-specific fields to the users and roles access settings form.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\context;
|
||||
|
||||
use PKP\components\forms\context\PKPUserAccessForm;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
|
||||
class UserAccessForm extends PKPUserAccessForm
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPUserAccessForm::__construct()
|
||||
*/
|
||||
public function __construct($action, $context)
|
||||
{
|
||||
parent::__construct($action, $context);
|
||||
|
||||
$this->addField(new FieldOptions('restrictArticleAccess', [
|
||||
'label' => __('manager.setup.siteAccess.viewContent'),
|
||||
'value' => (bool) $context->getData('restrictArticleAccess'),
|
||||
'options' => [
|
||||
['value' => true, 'label' => __('manager.setup.restrictArticleAccess')],
|
||||
],
|
||||
]), [FIELD_POSITION_AFTER, 'restrictSiteAccess']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/counter/CounterReportForm.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 CounterReportForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for setting a counter report
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\counter;
|
||||
|
||||
use APP\sushi\IR;
|
||||
use APP\sushi\IR_A1;
|
||||
use APP\sushi\PR;
|
||||
use APP\sushi\PR_P1;
|
||||
use APP\sushi\TR;
|
||||
use APP\sushi\TR_J3;
|
||||
use PKP\components\forms\counter\PKPCounterReportForm;
|
||||
|
||||
class CounterReportForm extends PKPCounterReportForm
|
||||
{
|
||||
public function setReportFields(): void
|
||||
{
|
||||
$formFieldsPR = PR::getReportSettingsFormFields();
|
||||
$this->reportFields['PR'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsPR);
|
||||
|
||||
$formFieldsPR_P1 = PR_P1::getReportSettingsFormFields();
|
||||
$this->reportFields['PR_P1'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsPR_P1);
|
||||
|
||||
$formFieldsTR = TR::getReportSettingsFormFields();
|
||||
$this->reportFields['TR'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsTR);
|
||||
|
||||
$formFieldsTR_J3 = TR_J3::getReportSettingsFormFields();
|
||||
$this->reportFields['TR_J3'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsTR_J3);
|
||||
|
||||
$formFieldsIR = IR::getReportSettingsFormFields();
|
||||
$this->reportFields['IR'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsIR);
|
||||
|
||||
$formFieldsIR_A1 = IR_A1::getReportSettingsFormFields();
|
||||
$this->reportFields['IR_A1'] = array_map(function ($field) {
|
||||
$field->groupId = 'default';
|
||||
return $field;
|
||||
}, $formFieldsIR_A1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/decision/RequestPaymentDecisionForm.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 RequestPaymentDecisionForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form to request or waive an APC payment when making an editorial decision
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\decision;
|
||||
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\context\Context;
|
||||
|
||||
define('FORM_REQUEST_PAYMENT_DECISION', 'requestPaymentDecision');
|
||||
|
||||
class RequestPaymentDecisionForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_REQUEST_PAYMENT_DECISION;
|
||||
|
||||
/** @copydoc FormComponent::$action */
|
||||
public $action = FormComponent::ACTION_EMIT;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct(Context $context)
|
||||
{
|
||||
$this->addField(new FieldOptions('requestPayment', [
|
||||
'label' => __('common.payment'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
[
|
||||
'value' => true,
|
||||
'label' => __(
|
||||
'payment.requestPublicationFee',
|
||||
['feeAmount' => $context->getData('publicationFee') . ' ' . $context->getData('currency')]
|
||||
),
|
||||
],
|
||||
[
|
||||
'value' => false,
|
||||
'label' => __('payment.waive'),
|
||||
],
|
||||
],
|
||||
'value' => true,
|
||||
'groupId' => 'default',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/AssignToIssueForm.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 AssignToIssueForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for setting a publication's issue.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\publication;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_ASSIGN_TO_ISSUE', 'assignToIssue');
|
||||
|
||||
class AssignToIssueForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ASSIGN_TO_ISSUE;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param \APP\publication\Publication $publication The publication to change settings for
|
||||
* @param \APP\journal\Journal $publicationContext The context of the publication
|
||||
*/
|
||||
public function __construct($action, $publication, $publicationContext)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
// Issue options
|
||||
$issueOptions = [['value' => '', 'label' => '']];
|
||||
|
||||
$unpublishedIssues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$publicationContext->getId()])
|
||||
->filterByPublished(false)
|
||||
->getMany();
|
||||
|
||||
if ($unpublishedIssues->count() > 0) {
|
||||
$issueOptions[] = ['value' => '', 'label' => '--- ' . __('editor.issues.futureIssues') . ' ---'];
|
||||
foreach ($unpublishedIssues as $issue) {
|
||||
$issueOptions[] = [
|
||||
'value' => (int) $issue->getId(),
|
||||
'label' => htmlspecialchars($issue->getIssueIdentification()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$publishedIssues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$publicationContext->getId()])
|
||||
->filterByPublished(true)
|
||||
->getMany();
|
||||
|
||||
if ($publishedIssues->count() > 0) {
|
||||
$issueOptions[] = ['value' => '', 'label' => '--- ' . __('editor.issues.backIssues') . ' ---'];
|
||||
foreach ($publishedIssues as $issue) {
|
||||
$issueOptions[] = [
|
||||
'value' => (int) $issue->getId(),
|
||||
'label' => htmlspecialchars($issue->getIssueIdentification()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->addField(new FieldSelect('issueId', [
|
||||
'label' => __('issue.issue'),
|
||||
'options' => $issueOptions,
|
||||
'value' => $publication->getData('issueId') ? $publication->getData('issueId') : 0,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/IssueEntryForm.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 IssueEntryForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for setting a publication's issue, section, categories,
|
||||
* pages, etc.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\publication;
|
||||
|
||||
use APP\components\forms\FieldSelectIssue;
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\FieldSelect;
|
||||
use PKP\components\forms\FieldText;
|
||||
use PKP\components\forms\FieldUploadImage;
|
||||
use PKP\components\forms\FormComponent;
|
||||
|
||||
define('FORM_ISSUE_ENTRY', 'issueEntry');
|
||||
|
||||
class IssueEntryForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_ISSUE_ENTRY;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param array $locales Supported locales
|
||||
* @param \APP\publication\Publication $publication The publication to change settings for
|
||||
* @param \APP\journal\Journal $publicationContext The context of the publication
|
||||
* @param string $baseUrl Site's base URL. Used for image previews.
|
||||
* @param string $temporaryFileApiUrl URL to upload files to
|
||||
*/
|
||||
public function __construct($action, $locales, $publication, $publicationContext, $baseUrl, $temporaryFileApiUrl)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->locales = $locales;
|
||||
|
||||
// Issue options
|
||||
$issueOptions = [['value' => '', 'label' => '']];
|
||||
|
||||
$unpublishedIssues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$publicationContext->getId()])
|
||||
->filterByPublished(false)
|
||||
->getMany()
|
||||
->toArray();
|
||||
|
||||
if (count($unpublishedIssues)) {
|
||||
$issueOptions[] = ['value' => '', 'label' => '--- ' . __('editor.issues.futureIssues') . ' ---'];
|
||||
foreach ($unpublishedIssues as $issue) {
|
||||
$issueOptions[] = [
|
||||
'value' => (int) $issue->getId(),
|
||||
'label' => htmlspecialchars($issue->getIssueIdentification()),
|
||||
];
|
||||
}
|
||||
}
|
||||
$publishedIssues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$publicationContext->getId()])
|
||||
->filterByPublished(true)
|
||||
->getMany()
|
||||
->toArray();
|
||||
|
||||
if (count($publishedIssues)) {
|
||||
$issueOptions[] = ['value' => '', 'label' => '--- ' . __('editor.issues.backIssues') . ' ---'];
|
||||
foreach ($publishedIssues as $issue) {
|
||||
$issueOptions[] = [
|
||||
'value' => (int) $issue->getId(),
|
||||
'label' => htmlspecialchars($issue->getIssueIdentification()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Section options
|
||||
$sections = Repo::section()->getSectionList($publicationContext->getId());
|
||||
$sectionOptions = [];
|
||||
foreach ($sections as $section) {
|
||||
$sectionOptions[] = [
|
||||
'label' => (($section['group']) ? __('publication.inactiveSection', ['section' => $section['title']]) : $section['title']),
|
||||
'value' => (int) $section['id'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->addField(new FieldSelectIssue('issueId', [
|
||||
'label' => __('issue.issue'),
|
||||
'options' => $issueOptions,
|
||||
'publicationStatus' => $publication->getData('status'),
|
||||
'value' => $publication->getData('issueId') ? $publication->getData('issueId') : 0,
|
||||
]))
|
||||
->addField(new FieldSelect('sectionId', [
|
||||
'label' => __('section.section'),
|
||||
'options' => $sectionOptions,
|
||||
'value' => (int) $publication->getData('sectionId'),
|
||||
]));
|
||||
|
||||
// Categories
|
||||
$categoryOptions = [];
|
||||
$categories = Repo::category()->getCollector()
|
||||
->filterByContextIds([$publicationContext->getId()])
|
||||
->getMany()
|
||||
->toArray();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$label = $category->getLocalizedTitle();
|
||||
if ($category->getParentId()) {
|
||||
$label = $categories[$category->getParentId()]->getLocalizedTitle() . ' > ' . $label;
|
||||
}
|
||||
$categoryOptions[] = [
|
||||
'value' => (int) $category->getId(),
|
||||
'label' => $label,
|
||||
];
|
||||
}
|
||||
if (!empty($categoryOptions)) {
|
||||
$this->addField(new FieldOptions('categoryIds', [
|
||||
'label' => __('submission.submit.placement.categories'),
|
||||
'value' => $publication->getData('categoryIds'),
|
||||
'options' => $categoryOptions,
|
||||
]));
|
||||
}
|
||||
|
||||
$this->addField(new FieldUploadImage('coverImage', [
|
||||
'label' => __('editor.article.coverImage'),
|
||||
'value' => $publication->getData('coverImage'),
|
||||
'isMultilingual' => true,
|
||||
'baseUrl' => $baseUrl,
|
||||
'options' => [
|
||||
'url' => $temporaryFileApiUrl,
|
||||
],
|
||||
]))
|
||||
->addField(new FieldText('pages', [
|
||||
'label' => __('editor.issues.pages'),
|
||||
'value' => $publication->getData('pages'),
|
||||
]))
|
||||
->addField(new FieldText('urlPath', [
|
||||
'label' => __('publication.urlPath'),
|
||||
'description' => __('publication.urlPath.description'),
|
||||
'value' => $publication->getData('urlPath'),
|
||||
]))
|
||||
->addField(new FieldText('datePublished', [
|
||||
'label' => __('publication.datePublished'),
|
||||
'description' => __('publication.datePublished.description'),
|
||||
'value' => $publication->getData('datePublished'),
|
||||
'size' => 'small',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/PublishForm.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 PublishForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for confirming a publication's issue before publishing.
|
||||
* It may also be used for scheduling a publication in an issue for later
|
||||
* publication.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\publication;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\PKPString;
|
||||
|
||||
define('FORM_PUBLISH', 'publish');
|
||||
|
||||
class PublishForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_PUBLISH;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/** @var \APP\publication\Publication */
|
||||
public $publication;
|
||||
|
||||
/** @var \APP\journal\Journal */
|
||||
public $submissionContext;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param \APP\publication\Publication $publication The publication to change settings for
|
||||
* @param \APP\journal\Journal $submissionContext journal or press
|
||||
* @param array $requirementErrors A list of pre-publication requirements that are not met.
|
||||
*/
|
||||
public function __construct($action, $publication, $submissionContext, $requirementErrors)
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->errors = $requirementErrors;
|
||||
$this->publication = $publication;
|
||||
$this->submissionContext = $submissionContext;
|
||||
|
||||
// Set separate messages and buttons if publication requirements have passed
|
||||
if (empty($requirementErrors)) {
|
||||
$msg = __('publication.publish.confirmation');
|
||||
$submitLabel = __('publication.publish');
|
||||
if ($publication->getData('issueId')) {
|
||||
$issue = Repo::issue()->get($publication->getData('issueId'));
|
||||
if ($issue) {
|
||||
if ($issue->getData('published')) {
|
||||
$msg = __('publication.publish.confirmation.backIssue', ['issue' => htmlspecialchars($issue->getIssueIdentification())]);
|
||||
} else {
|
||||
$msg = __('publication.publish.confirmation.futureIssue', ['issue' => htmlspecialchars($issue->getIssueIdentification())]);
|
||||
$submitLabel = __('editor.submission.schedulePublication');
|
||||
}
|
||||
}
|
||||
}
|
||||
// If a publication date has already been set and the date has passed this will
|
||||
// be published immediately regardless of the issue assignment
|
||||
if ($publication->getData('datePublished') && $publication->getData('datePublished') <= Core::getCurrentDate()) {
|
||||
$timestamp = strtotime($publication->getData('datePublished'));
|
||||
$dateFormatLong = PKPString::convertStrftimeFormat($submissionContext->getLocalizedDateFormatLong());
|
||||
$msg = __(
|
||||
'publication.publish.confirmation.datePublishedInPast',
|
||||
[
|
||||
'datePublished' => date($dateFormatLong, $timestamp),
|
||||
]
|
||||
);
|
||||
$submitLabel = __('publication.publish');
|
||||
}
|
||||
$this->addPage([
|
||||
'id' => 'default',
|
||||
'submitButton' => [
|
||||
'label' => $submitLabel,
|
||||
],
|
||||
]);
|
||||
} else {
|
||||
$msg = '<p>' . __('publication.publish.requirements') . '</p>';
|
||||
$msg .= '<ul>';
|
||||
foreach ($requirementErrors as $error) {
|
||||
$msg .= '<li>' . $error . '</li>';
|
||||
}
|
||||
$msg .= '</ul>';
|
||||
$this->addPage([
|
||||
'id' => 'default',
|
||||
]);
|
||||
}
|
||||
|
||||
$this->addGroup([
|
||||
'id' => 'default',
|
||||
'pageId' => 'default',
|
||||
])
|
||||
->addField(new FieldHTML('validation', [
|
||||
'description' => $msg,
|
||||
'groupId' => 'default',
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/publication/SubmissionPaymentsForm.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 SubmissionPaymentsForm
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A form for managing submission fees.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\publication;
|
||||
|
||||
use APP\payment\ojs\OJSCompletedPaymentDAO;
|
||||
use APP\payment\ojs\OJSPaymentManager;
|
||||
use PKP\components\forms\FieldRadioInput;
|
||||
use PKP\components\forms\FormComponent;
|
||||
use PKP\db\DAORegistry;
|
||||
|
||||
define('FORM_SUBMISSION_PAYMENTS', 'submissionPayments');
|
||||
|
||||
class SubmissionPaymentsForm extends FormComponent
|
||||
{
|
||||
/** @copydoc FormComponent::$id */
|
||||
public $id = FORM_SUBMISSION_PAYMENTS;
|
||||
|
||||
/** @copydoc FormComponent::$method */
|
||||
public $method = 'PUT';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $action URL to submit the form to
|
||||
* @param \APP\submission\Submission $submission The submission to inspect payment status of
|
||||
* @param \APP\journal\Journal $submissionContext The context of the submission
|
||||
*/
|
||||
public function __construct($action, $submission, $submissionContext)
|
||||
{
|
||||
$this->action = $action;
|
||||
|
||||
$completedPaymentDao = DAORegistry::getDAO('OJSCompletedPaymentDAO'); /** @var OJSCompletedPaymentDAO $completedPaymentDao */
|
||||
$publicationFeePayment = $completedPaymentDao->getByAssoc(null, OJSPaymentManager::PAYMENT_TYPE_PUBLICATION, $submission->getId());
|
||||
|
||||
$this->addField(new FieldRadioInput('publicationFeeStatus', [
|
||||
'label' => __('payment.type.publication'),
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
['value' => 'waived', 'label' => __('payment.waived')],
|
||||
['value' => 'paid', 'label' => __('payment.paid')],
|
||||
['value' => 'unpaid', 'label' => __('payment.unpaid')],
|
||||
],
|
||||
'value' => $publicationFeePayment
|
||||
? ($publicationFeePayment->getAmount() ? 'paid' : 'waived')
|
||||
: 'unpaid'
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/submission/ReconfigureSubmission.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 ReconfigureSubmission
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief A preset form for configuring the submission wizard, such as the
|
||||
* submission's section or language, after the submission was started.
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\submission;
|
||||
|
||||
use APP\publication\Publication;
|
||||
use APP\section\Section;
|
||||
use APP\submission\Submission;
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\submission\ReconfigureSubmission as BaseReconfigureSubmission;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\PKPString;
|
||||
|
||||
class ReconfigureSubmission extends BaseReconfigureSubmission
|
||||
{
|
||||
public array $sections;
|
||||
|
||||
/**
|
||||
* @param Section[] $sections
|
||||
*/
|
||||
public function __construct(string $action, Submission $submission, Publication $publication, Context $context, array $sections)
|
||||
{
|
||||
parent::__construct($action, $submission, $publication, $context);
|
||||
|
||||
$this->sections = $sections;
|
||||
|
||||
if (count($this->sections) > 1) {
|
||||
$this->addSectionsField();
|
||||
}
|
||||
}
|
||||
|
||||
protected function addSectionsField(): void
|
||||
{
|
||||
$this->addField(new FieldOptions('sectionId', [
|
||||
'type' => 'radio',
|
||||
'label' => __('section.section'),
|
||||
'description' => __('author.submit.journalSectionDescription'),
|
||||
'options' => $this->getSectionOptions(),
|
||||
'isRequired' => true,
|
||||
'value' => $this->publication->getData('sectionId'),
|
||||
]));
|
||||
|
||||
foreach ($this->sections as $section) {
|
||||
if (!trim(PKPString::html2text($section->getLocalizedPolicy()))) {
|
||||
continue;
|
||||
}
|
||||
$this->addField(new FieldHTML('sectionDescription' . $section->getId(), [
|
||||
'label' => $section->getLocalizedTitle(),
|
||||
'description' => $section->getLocalizedPolicy(),
|
||||
'showWhen' => ['sectionId', $section->getId()],
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert sections to options prop for a FieldOption
|
||||
*/
|
||||
protected function getSectionOptions(): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($this->sections as $section) {
|
||||
$options[] = [
|
||||
'value' => $section->getId(),
|
||||
'label' => $section->getLocalizedTitle(),
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/components/form/submission/StartSubmission.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 StartSubmission
|
||||
*
|
||||
* @ingroup classes_controllers_form
|
||||
*
|
||||
* @brief The form to begin the submission wizard
|
||||
*/
|
||||
|
||||
namespace APP\components\forms\submission;
|
||||
|
||||
use APP\section\Section;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use PKP\components\forms\FieldHTML;
|
||||
use PKP\components\forms\FieldOptions;
|
||||
use PKP\components\forms\submission\StartSubmission as BaseStartSubmission;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\PKPString;
|
||||
|
||||
class StartSubmission extends BaseStartSubmission
|
||||
{
|
||||
/**
|
||||
* @param Section[] $sections The sections that this user can submit to
|
||||
*/
|
||||
public function __construct(string $action, Context $context, Enumerable $userGroups, array $sections)
|
||||
{
|
||||
parent::__construct($action, $context, $userGroups);
|
||||
|
||||
if (count($sections) === 1) {
|
||||
$this->addHiddenField('sectionId', $sections[0]->getId());
|
||||
} else {
|
||||
$this->addField(new FieldOptions('sectionId', [
|
||||
'type' => 'radio',
|
||||
'label' => __('section.section'),
|
||||
'description' => __('author.submit.journalSectionDescription'),
|
||||
'options' => $this->getSectionOptions($sections),
|
||||
'value' => '',
|
||||
'isRequired' => true,
|
||||
]), [FIELD_POSITION_AFTER, 'title']);
|
||||
|
||||
foreach ($sections as $section) {
|
||||
if (!trim(PKPString::html2text($section->getLocalizedPolicy()))) {
|
||||
continue;
|
||||
}
|
||||
$this->addField(new FieldHTML('sectionDescription' . $section->getId(), [
|
||||
'label' => $section->getLocalizedTitle(),
|
||||
'description' => $section->getLocalizedPolicy(),
|
||||
'showWhen' => ['sectionId', $section->getId()],
|
||||
]), [FIELD_POSITION_AFTER, 'sectionId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert sections to options prop for a FieldOption
|
||||
*
|
||||
* @param Section[] $sections
|
||||
*/
|
||||
protected function getSectionOptions(array $sections): array
|
||||
{
|
||||
$options = [];
|
||||
foreach ($sections as $section) {
|
||||
$options[] = [
|
||||
'value' => $section->getId(),
|
||||
'label' => $section->getLocalizedTitle(),
|
||||
];
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/components/listPanels/DoiListPanel.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 DoiListPanel
|
||||
*
|
||||
* @ingroup classes_components_list
|
||||
*
|
||||
* @brief A ListPanel component for viewing and editing DOIs
|
||||
*/
|
||||
|
||||
namespace APP\components\listPanels;
|
||||
|
||||
use APP\components\forms\FieldSelectIssues;
|
||||
use APP\core\Application;
|
||||
use APP\template\TemplateManager;
|
||||
use PKP\components\listPanels\PKPDoiListPanel;
|
||||
use PKP\core\PKPApplication;
|
||||
|
||||
class DoiListPanel extends PKPDoiListPanel
|
||||
{
|
||||
/** @var bool Whether objects being passed to DOI List Panel are submissions or not */
|
||||
public $isSubmission = true;
|
||||
|
||||
/** @var boolean Whether to show issue filters */
|
||||
public $includeIssuesFilter = false;
|
||||
|
||||
/**
|
||||
* Add any application-specific config to the list panel setup
|
||||
*/
|
||||
protected function setAppConfig(array &$config): void
|
||||
{
|
||||
if ($this->isSubmission) {
|
||||
$config['executeActionApiUrl'] = $this->doiApiUrl . '/submissions';
|
||||
} else {
|
||||
$config['executeActionApiUrl'] = $this->doiApiUrl . '/issues';
|
||||
// Overwrite default submission published statuses for issue-specific ones
|
||||
$config['publishedStatuses'] = [
|
||||
'name' => 'isPublished',
|
||||
'published' => 1,
|
||||
'unpublished' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->includeIssuesFilter) {
|
||||
$request = Application::get()->getRequest();
|
||||
$issueAutosuggestField = new FieldSelectIssues('issueIds', [
|
||||
'label' => __('issue.issues'),
|
||||
'value' => [],
|
||||
'apiUrl' => $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $request->getContext()->getPath(), 'issues'),
|
||||
]);
|
||||
$config['filters'][] = [
|
||||
'filters' => [
|
||||
[
|
||||
'title' => __('issue.issues'),
|
||||
'param' => 'issueIds',
|
||||
'value' => [],
|
||||
'filterType' => 'pkp-filter-autosuggest',
|
||||
'component' => 'field-select-issues',
|
||||
'autosuggestProps' => $issueAutosuggestField->getConfig(),
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// Provide required locale keys
|
||||
$request = Application::get()->getRequest();
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->setLocaleKeys([
|
||||
'article.article',
|
||||
'issue.issue'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
/**
|
||||
* @file components/listPanels/SubmissionsListPanel.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 SubmissionsListPanel
|
||||
*
|
||||
* @ingroup classes_components_listPanels
|
||||
*
|
||||
* @brief Instantiates and manages a UI component to list submissions.
|
||||
*/
|
||||
|
||||
namespace APP\components\listPanels;
|
||||
|
||||
use APP\components\forms\FieldSelectIssues;
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use PKP\components\forms\FieldAutosuggestPreset;
|
||||
use PKP\components\listPanels\PKPSubmissionsListPanel;
|
||||
|
||||
class SubmissionsListPanel extends PKPSubmissionsListPanel
|
||||
{
|
||||
/** @var bool Whether to show inactive section filters */
|
||||
public $includeActiveSectionFiltersOnly = false;
|
||||
|
||||
/** @var bool Whether to show issue filters */
|
||||
public $includeIssuesFilter = false;
|
||||
|
||||
/**
|
||||
* @copydoc PKPSubmissionsListPanel::getConfig()
|
||||
*/
|
||||
public function getConfig()
|
||||
{
|
||||
$config = parent::getConfig();
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
if ($request->getContext()) {
|
||||
$config['filters'][] = $this->getSectionFilters($this->includeActiveSectionFiltersOnly);
|
||||
}
|
||||
|
||||
if ($this->includeIssuesFilter) {
|
||||
$issueAutosuggestField = new FieldSelectIssues('issueIds', [
|
||||
'label' => __('issue.issues'),
|
||||
'value' => [],
|
||||
'apiUrl' => $request->getDispatcher()->url($request, Application::ROUTE_API, $request->getContext()->getPath(), 'issues'),
|
||||
]);
|
||||
$config['filters'][] = [
|
||||
'filters' => [
|
||||
[
|
||||
'title' => __('issue.issues'),
|
||||
'param' => 'issueIds',
|
||||
'value' => [],
|
||||
'filterType' => 'pkp-filter-autosuggest',
|
||||
'component' => 'field-select-issues',
|
||||
'autosuggestProps' => $issueAutosuggestField->getConfig(),
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of workflow stages supported by the current app
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getWorkflowStages()
|
||||
{
|
||||
return [
|
||||
[
|
||||
'param' => 'stageIds',
|
||||
'value' => WORKFLOW_STAGE_ID_SUBMISSION,
|
||||
'title' => __('manager.publication.submissionStage'),
|
||||
],
|
||||
[
|
||||
'param' => 'stageIds',
|
||||
'value' => WORKFLOW_STAGE_ID_EXTERNAL_REVIEW,
|
||||
'title' => __('manager.publication.reviewStage'),
|
||||
],
|
||||
[
|
||||
'param' => 'stageIds',
|
||||
'value' => WORKFLOW_STAGE_ID_EDITING,
|
||||
'title' => __('submission.copyediting'),
|
||||
],
|
||||
[
|
||||
'param' => 'stageIds',
|
||||
'value' => WORKFLOW_STAGE_ID_PRODUCTION,
|
||||
'title' => __('manager.publication.productionStage'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the sections for passing as filters
|
||||
*
|
||||
* @param bool $excludeInactive show inactive section filters or not
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSectionFilters($excludeInactive = false)
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
$context = $request->getContext();
|
||||
|
||||
$sections = Repo::section()->getSectionList($context->getId(), $excludeInactive);
|
||||
|
||||
// Use an autosuggest field if the list of submissions is too long
|
||||
if (count($sections) > 5) {
|
||||
$autosuggestField = new FieldAutosuggestPreset('sectionIds', [
|
||||
'label' => __('section.sections'),
|
||||
'value' => [],
|
||||
'options' => array_values(array_map(function ($section) {
|
||||
return [
|
||||
'value' => (int) $section['id'],
|
||||
'label' => $section['title'],
|
||||
];
|
||||
}, $sections)),
|
||||
]);
|
||||
return [
|
||||
'filters' => [
|
||||
[
|
||||
'title' => __('section.sections'),
|
||||
'param' => 'sectionIds',
|
||||
'filterType' => 'pkp-filter-autosuggest',
|
||||
'component' => 'field-autosuggest-preset',
|
||||
'value' => [],
|
||||
'autosuggestProps' => $autosuggestField->getConfig(),
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'heading' => __('section.sections'),
|
||||
'filters' => array_map(function ($section) {
|
||||
return [
|
||||
'param' => 'sectionIds',
|
||||
'value' => (int) $section['id'],
|
||||
'title' => $section['title'],
|
||||
];
|
||||
}, $sections),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,744 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup controllers_grid_issues Issues Grid
|
||||
* The Issues Grid implements the management interface allowing editors to
|
||||
* manage future and archived issues.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file controllers/grid/issues/IssueGridHandler.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 IssueGridHandler
|
||||
*
|
||||
* @ingroup controllers_grid_issues
|
||||
*
|
||||
* @brief Handle issues grid requests.
|
||||
*/
|
||||
|
||||
namespace APP\controllers\grid\issues;
|
||||
|
||||
use APP\controllers\grid\issues\form\IssueAccessForm;
|
||||
use APP\controllers\grid\issues\form\IssueForm;
|
||||
use APP\controllers\grid\pubIds\form\AssignPublicIdentifiersForm;
|
||||
use APP\controllers\tab\pubIds\form\PublicIdentifiersForm;
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use APP\facades\Repo;
|
||||
use APP\file\PublicFileManager;
|
||||
use APP\issue\Collector;
|
||||
use APP\jobs\notifications\IssuePublishedNotifyUsers;
|
||||
use APP\notification\Notification;
|
||||
use APP\notification\NotificationManager;
|
||||
use APP\publication\Publication;
|
||||
use APP\security\authorization\OjsIssueRequiredPolicy;
|
||||
use APP\submission\Submission;
|
||||
use APP\template\TemplateManager;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use PKP\controllers\grid\GridColumn;
|
||||
use PKP\controllers\grid\GridHandler;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\file\TemporaryFileManager;
|
||||
use PKP\mail\Mailer;
|
||||
use PKP\notification\NotificationSubscriptionSettingsDAO;
|
||||
use PKP\notification\PKPNotification;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\security\authorization\ContextAccessPolicy;
|
||||
use PKP\security\Role;
|
||||
|
||||
class IssueGridHandler extends GridHandler
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->addRoleAssignment(
|
||||
[Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
|
||||
[
|
||||
'fetchGrid', 'fetchRow',
|
||||
'addIssue', 'editIssue', 'editIssueData', 'updateIssue',
|
||||
'uploadFile', 'deleteCoverImage',
|
||||
'issueToc',
|
||||
'issueGalleys',
|
||||
'deleteIssue', 'publishIssue', 'unpublishIssue', 'setCurrentIssue',
|
||||
'identifiers', 'updateIdentifiers', 'clearPubId', 'clearIssueObjectsPubIds',
|
||||
'access', 'updateAccess',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Implement template methods from PKPHandler
|
||||
//
|
||||
/**
|
||||
* @copydoc PKPHandler::authorize()
|
||||
*/
|
||||
public function authorize($request, &$args, $roleAssignments)
|
||||
{
|
||||
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
|
||||
|
||||
// If a signoff ID was specified, authorize it.
|
||||
if ($request->getUserVar('issueId')) {
|
||||
$this->addPolicy(new OjsIssueRequiredPolicy($request, $args));
|
||||
}
|
||||
|
||||
return parent::authorize($request, $args, $roleAssignments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc GridHandler::initialize()
|
||||
*
|
||||
* @param null|mixed $args
|
||||
*/
|
||||
public function initialize($request, $args = null)
|
||||
{
|
||||
parent::initialize($request, $args);
|
||||
|
||||
// Grid columns.
|
||||
$issueGridCellProvider = new IssueGridCellProvider();
|
||||
|
||||
// Issue identification
|
||||
$this->addColumn(
|
||||
new GridColumn(
|
||||
'identification',
|
||||
'issue.issue',
|
||||
null,
|
||||
null,
|
||||
$issueGridCellProvider
|
||||
)
|
||||
);
|
||||
|
||||
$this->_addCenterColumns($issueGridCellProvider);
|
||||
|
||||
// Number of articles
|
||||
$this->addColumn(
|
||||
new GridColumn(
|
||||
'numArticles',
|
||||
'editor.issues.numArticles',
|
||||
null,
|
||||
null,
|
||||
$issueGridCellProvider
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private function to add central columns to the grid.
|
||||
* May be overridden by subclasses.
|
||||
*
|
||||
* @param IssueGridCellProvider $issueGridCellProvider
|
||||
*/
|
||||
protected function _addCenterColumns($issueGridCellProvider)
|
||||
{
|
||||
// Default implementation does nothing.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the row handler - override the default row handler
|
||||
*
|
||||
* @return IssueGridRow
|
||||
*/
|
||||
protected function getRowInstance()
|
||||
{
|
||||
return new IssueGridRow();
|
||||
}
|
||||
|
||||
//
|
||||
// Public operations
|
||||
//
|
||||
/**
|
||||
* An action to add a new issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*/
|
||||
public function addIssue($args, $request)
|
||||
{
|
||||
// Calling editIssueData with an empty ID will add
|
||||
// a new issue.
|
||||
return $this->editIssueData($args, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to edit an issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function editIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
if ($issue) {
|
||||
$templateMgr->assign('issueId', $issue->getId());
|
||||
}
|
||||
$publisherIdEnabled = in_array('issue', (array) $request->getContext()->getData('enablePublisherId'));
|
||||
$pubIdPlugins = PluginRegistry::getPlugins('pubIds');
|
||||
if ($publisherIdEnabled || count($pubIdPlugins)) {
|
||||
$templateMgr->assign('enableIdentifiers', true);
|
||||
}
|
||||
return new JSONMessage(true, $templateMgr->fetch('controllers/grid/issues/issue.tpl'));
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to edit an issue's identifying data
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function editIssueData($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$issueForm = new IssueForm($issue);
|
||||
$issueForm->initData();
|
||||
return new JSONMessage(true, $issueForm->fetch($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to upload an issue file. Used for issue cover images.
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function uploadFile($args, $request)
|
||||
{
|
||||
$user = $request->getUser();
|
||||
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
$temporaryFile = $temporaryFileManager->handleUpload('uploadedFile', $user->getId());
|
||||
if ($temporaryFile) {
|
||||
$json = new JSONMessage(true);
|
||||
$json->setAdditionalAttributes([
|
||||
'temporaryFileId' => $temporaryFile->getId()
|
||||
]);
|
||||
return $json;
|
||||
} else {
|
||||
return new JSONMessage(false, __('common.uploadFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an uploaded cover image.
|
||||
*
|
||||
* @param array $args
|
||||
* `coverImage` string Filename of the cover image to be deleted.
|
||||
* `issueId` int Id of the issue this cover image is attached to
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function deleteCoverImage($args, $request)
|
||||
{
|
||||
assert(!empty($args['coverImage']) && !empty($args['issueId']));
|
||||
|
||||
// Check if the passed filename matches the filename for this issue's
|
||||
// cover page.
|
||||
$issue = Repo::issue()->get((int) $args['issueId']);
|
||||
$context = $request->getContext();
|
||||
if ($issue->getJournalId() != $context->getId()) {
|
||||
return new JSONMessage(false, __('editor.issues.removeCoverImageOnDifferentContextNowAllowed'));
|
||||
}
|
||||
|
||||
$locale = Locale::getLocale();
|
||||
if ($args['coverImage'] != $issue->getCoverImage($locale)) {
|
||||
return new JSONMessage(false, __('editor.issues.removeCoverImageFileNameMismatch'));
|
||||
}
|
||||
|
||||
$file = $args['coverImage'];
|
||||
|
||||
// Remove cover image and alt text from issue settings
|
||||
$issue->setCoverImage('', $locale);
|
||||
$issue->setCoverImageAltText('', $locale);
|
||||
Repo::issue()->edit($issue, []);
|
||||
// Remove the file
|
||||
$publicFileManager = new PublicFileManager();
|
||||
if ($publicFileManager->removeContextFile($issue->getJournalId(), $file)) {
|
||||
$json = new JSONMessage(true);
|
||||
$json->setEvent('fileDeleted');
|
||||
return $json;
|
||||
} else {
|
||||
return new JSONMessage(false, __('editor.issues.removeCoverImageFileNotFound'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update an issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function updateIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$issueForm = new IssueForm($issue);
|
||||
$issueForm->readInputData();
|
||||
|
||||
if ($issueForm->validate()) {
|
||||
$issueForm->execute();
|
||||
$notificationManager = new NotificationManager();
|
||||
$notificationManager->createTrivialNotification($request->getUser()->getId());
|
||||
return DAO::getDataChangedEvent();
|
||||
} else {
|
||||
return new JSONMessage(true, $issueForm->fetch($request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to edit an issue's access settings
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function access($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$issueAccessForm = new IssueAccessForm($issue);
|
||||
$issueAccessForm->initData();
|
||||
return new JSONMessage(true, $issueAccessForm->fetch($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an issue's access settings
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function updateAccess($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
|
||||
$issueAccessForm = new IssueAccessForm($issue);
|
||||
$issueAccessForm->readInputData();
|
||||
|
||||
if ($issueAccessForm->validate()) {
|
||||
$issueAccessForm->execute();
|
||||
$notificationManager = new NotificationManager();
|
||||
$notificationManager->createTrivialNotification($request->getUser()->getId());
|
||||
return DAO::getDataChangedEvent();
|
||||
} else {
|
||||
return new JSONMessage(true, $issueAccessForm->fetch($request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*/
|
||||
public function deleteIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
if (!$issue || !$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
$journal = $request->getJournal();
|
||||
|
||||
if ($issue->getJournalId() != $journal->getId()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
// remove all published submissions and return original articles to editing queue
|
||||
$submissions = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$issue->getData('journalId')])
|
||||
->filterByIssueIds([$issue->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$publications = $submission->getData('publications');
|
||||
foreach ($publications as $publication) {
|
||||
if ($publication->getData('issueId') === (int) $issue->getId()) {
|
||||
Repo::publication()->edit($publication, ['issueId' => '', 'status' => Submission::STATUS_QUEUED]);
|
||||
}
|
||||
}
|
||||
$newSubmission = Repo::submission()->get($submission->getId());
|
||||
Repo::submission()->updateStatus($newSubmission);
|
||||
}
|
||||
|
||||
Repo::issue()->delete($issue);
|
||||
$currentIssue = Repo::issue()->getCurrent($issue->getJournalId());
|
||||
if ($currentIssue != null && $issue->getId() == $currentIssue->getId()) {
|
||||
$issues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$journal->getId()])
|
||||
->filterByPublished(true)
|
||||
->orderBy(Collector::ORDERBY_PUBLISHED_ISSUES)
|
||||
->getMany();
|
||||
if ($issue = $issues->first()) {
|
||||
Repo::issue()->updateCurrent($journal->getId(), $issue);
|
||||
}
|
||||
}
|
||||
|
||||
return DAO::getDataChangedEvent($issue->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* An action to edit issue pub ids
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function identifiers($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$form = new PublicIdentifiersForm($issue);
|
||||
$form->initData();
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update issue pub ids
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function updateIdentifiers($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$form = new PublicIdentifiersForm($issue);
|
||||
$form->readInputData();
|
||||
if ($form->validate()) {
|
||||
$form->execute();
|
||||
return DAO::getDataChangedEvent($issue->getId());
|
||||
} else {
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear issue pub id
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function clearPubId($args, $request)
|
||||
{
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$form = new PublicIdentifiersForm($issue);
|
||||
$form->clearPubId($request->getUserVar('pubIdPlugIn'));
|
||||
$json = new JSONMessage(true);
|
||||
$json->setEvent('reloadTab', [['tabsSelector' => '#editIssueTabs', 'tabSelector' => '#identifiersTab']]);
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear issue objects pub ids
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function clearIssueObjectsPubIds($args, $request)
|
||||
{
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$form = new PublicIdentifiersForm($issue);
|
||||
$form->clearIssueObjectsPubIds($request->getUserVar('pubIdPlugIn'));
|
||||
return new JSONMessage(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the table of contents
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function issueToc($args, $request)
|
||||
{
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$templateMgr->assign('issue', $issue);
|
||||
return new JSONMessage(true, $templateMgr->fetch('controllers/grid/issues/issueToc.tpl'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the issue galleys page.
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*
|
||||
* @return JSONMessage JSON object
|
||||
*/
|
||||
public function issueGalleys($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$dispatcher = $request->getDispatcher();
|
||||
return $templateMgr->fetchAjax(
|
||||
'issueGalleysGridContainer',
|
||||
$dispatcher->url(
|
||||
$request,
|
||||
PKPApplication::ROUTE_COMPONENT,
|
||||
null,
|
||||
'grid.issueGalleys.IssueGalleyGridHandler',
|
||||
'fetchGrid',
|
||||
null,
|
||||
['issueId' => $issue->getId()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*/
|
||||
public function publishIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$context = $request->getContext();
|
||||
$contextId = $context->getId();
|
||||
$wasPublished = $issue->getPublished();
|
||||
|
||||
if (!$wasPublished) {
|
||||
$confirmationText = __('editor.issues.confirmPublish');
|
||||
$formTemplate = $this->getAssignPublicIdentifiersFormTemplate();
|
||||
$assignPublicIdentifiersForm = new AssignPublicIdentifiersForm($formTemplate, $issue, true, $confirmationText);
|
||||
if (!$request->getUserVar('confirmed')) {
|
||||
// Display assign pub ids modal
|
||||
$assignPublicIdentifiersForm->initData();
|
||||
return new JSONMessage(true, $assignPublicIdentifiersForm->fetch($request));
|
||||
}
|
||||
// Assign pub ids
|
||||
$assignPublicIdentifiersForm->readInputData();
|
||||
if (!$assignPublicIdentifiersForm->validate()) {
|
||||
return new JSONMessage(true, $assignPublicIdentifiersForm->fetch($request));
|
||||
}
|
||||
$assignPublicIdentifiersForm->execute();
|
||||
Repo::issue()->createDoi($issue);
|
||||
}
|
||||
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
$issue->setPublished(1);
|
||||
$issue->setDatePublished(Core::getCurrentDate());
|
||||
|
||||
// If subscriptions with delayed open access are enabled then
|
||||
// update open access date according to open access delay policy
|
||||
if ($context->getData('publishingMode') == \APP\journal\Journal::PUBLISHING_MODE_SUBSCRIPTION && ($delayDuration = $context->getData('delayedOpenAccessDuration'))) {
|
||||
$delayYears = (int)floor($delayDuration / 12);
|
||||
$delayMonths = (int)fmod($delayDuration, 12);
|
||||
|
||||
$curYear = date('Y');
|
||||
$curMonth = date('n');
|
||||
$curDay = date('j');
|
||||
|
||||
$delayOpenAccessYear = $curYear + $delayYears + (int)floor(($curMonth + $delayMonths) / 12);
|
||||
$delayOpenAccessMonth = (int)fmod($curMonth + $delayMonths, 12);
|
||||
|
||||
$issue->setAccessStatus(\APP\issue\Issue::ISSUE_ACCESS_SUBSCRIPTION);
|
||||
$issue->setOpenAccessDate(date('Y-m-d H:i:s', mktime(0, 0, 0, $delayOpenAccessMonth, $curDay, $delayOpenAccessYear)));
|
||||
}
|
||||
|
||||
Hook::call('IssueGridHandler::publishIssue', [&$issue]);
|
||||
|
||||
Repo::issue()->updateCurrent($contextId, $issue);
|
||||
|
||||
if (!$wasPublished) {
|
||||
Repo::doi()->issueUpdated($issue);
|
||||
|
||||
// Publish all related publications
|
||||
// Include published submissions in order to support cases where two
|
||||
// versions of the same submission are published in distinct issues. In
|
||||
// such cases, the submission will be STATUS_PUBLISHED but the
|
||||
// publication will be STATUS_SCHEDULED.
|
||||
$submissions = Repo::submission()->getCollector()
|
||||
->filterByContextIds([$issue->getJournalId()])
|
||||
->filterByIssueIds([$issue->getId()])
|
||||
->filterByStatus([Submission::STATUS_SCHEDULED, Submission::STATUS_PUBLISHED])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) { /** @var Submission $submission */
|
||||
$publications = $submission->getData('publications');
|
||||
|
||||
foreach ($publications as $publication) { /** @var Publication $publication */
|
||||
if ($publication->getData('status') === Submission::STATUS_SCHEDULED && $publication->getData('issueId') === (int) $issue->getId()) {
|
||||
Repo::publication()->publish($publication);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send a notification to associated users if selected and context is publishing content online with OJS
|
||||
if ($request->getUserVar('sendIssueNotification') && $context->getData('publishingMode') != \APP\journal\Journal::PUBLISHING_MODE_NONE) {
|
||||
// Notify users
|
||||
/** @var NotificationSubscriptionSettingsDAO $notificationSubscriptionSettingsDao */
|
||||
$notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO');
|
||||
|
||||
$userIdsToNotify = $notificationSubscriptionSettingsDao->getSubscribedUserIds(
|
||||
[NotificationSubscriptionSettingsDAO::BLOCKED_NOTIFICATION_KEY],
|
||||
[Notification::NOTIFICATION_TYPE_PUBLISHED_ISSUE],
|
||||
[$contextId]
|
||||
);
|
||||
|
||||
$userIdsToMail = $notificationSubscriptionSettingsDao->getSubscribedUserIds(
|
||||
[
|
||||
NotificationSubscriptionSettingsDAO::BLOCKED_NOTIFICATION_KEY,
|
||||
NotificationSubscriptionSettingsDAO::BLOCKED_EMAIL_NOTIFICATION_KEY
|
||||
],
|
||||
[Notification::NOTIFICATION_TYPE_PUBLISHED_ISSUE],
|
||||
[$contextId]
|
||||
);
|
||||
|
||||
$userIdsToNotifyAndMail = $userIdsToNotify->intersect($userIdsToMail);
|
||||
$userIdsToNotify = $userIdsToNotify->diff($userIdsToMail);
|
||||
|
||||
$jobs = [];
|
||||
foreach ($userIdsToNotify->chunk(PKPNotification::NOTIFICATION_CHUNK_SIZE_LIMIT) as $notifyUserIds) {
|
||||
$jobs[] = new IssuePublishedNotifyUsers(
|
||||
$notifyUserIds,
|
||||
$contextId,
|
||||
$issue,
|
||||
Locale::getLocale(),
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($userIdsToNotifyAndMail->chunk(Mailer::BULK_EMAIL_SIZE_LIMIT) as $mailUserIds) {
|
||||
$jobs[] = new IssuePublishedNotifyUsers(
|
||||
$mailUserIds,
|
||||
$contextId,
|
||||
$issue,
|
||||
Locale::getLocale(),
|
||||
$request->getUser()
|
||||
);
|
||||
}
|
||||
Bus::batch($jobs)->dispatch();
|
||||
}
|
||||
|
||||
$json = DAO::getDataChangedEvent();
|
||||
$json->setGlobalEvent('issuePublished', ['id' => $issue->getId()]);
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublish a previously-published issue
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*/
|
||||
public function unpublishIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$journal = $request->getJournal();
|
||||
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
// NB: Data set via params because setData('datePublished', null)
|
||||
// removes the entry into _data rather than updating 'datePublished' to null.
|
||||
$updateParams = [
|
||||
'published' => 0,
|
||||
'datePublished' => null
|
||||
];
|
||||
|
||||
Hook::call('IssueGridHandler::unpublishIssue', [&$issue]);
|
||||
|
||||
Repo::issue()->edit($issue, $updateParams);
|
||||
Repo::issue()->updateCurrent($request->getContext()->getId());
|
||||
|
||||
Repo::doi()->issueUpdated($issue);
|
||||
|
||||
// insert article tombstones for all articles
|
||||
$submissions = Repo::submission()->getCollector()
|
||||
->filterByContextIds([$issue->getJournalId()])
|
||||
->filterByIssueIds([$issue->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) { /** @var Submission $submission */
|
||||
$publications = $submission->getData('publications');
|
||||
foreach ($publications as $publication) { /** @var Publication $publication */
|
||||
if ($publication->getData('status') === Submission::STATUS_PUBLISHED && $publication->getData('issueId') === (int) $issue->getId()) {
|
||||
// Republish the publication in the issue, now that it's status has changed,
|
||||
// to ensure the publication's status is restored to Submission::STATUS_SCHEDULED
|
||||
// rather than Submission::STATUS_QUEUED
|
||||
Repo::publication()->unpublish($publication);
|
||||
Repo::publication()->publish($publication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$json = DAO::getDataChangedEvent($issue->getId());
|
||||
$json->setGlobalEvent('issueUnpublished', ['id' => $issue->getId()]);
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Issue as current
|
||||
*
|
||||
* @param array $args
|
||||
* @param Request $request
|
||||
*/
|
||||
public function setCurrentIssue($args, $request)
|
||||
{
|
||||
$issue = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ISSUE);
|
||||
$journal = $request->getJournal();
|
||||
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
|
||||
Repo::issue()->updateCurrent($journal->getId(), $issue);
|
||||
|
||||
$dispatcher = $request->getDispatcher();
|
||||
return DAO::getDataChangedEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template for the assign public identifiers form.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAssignPublicIdentifiersFormTemplate()
|
||||
{
|
||||
return 'controllers/grid/pubIds/form/assignPublicIdentifiersForm.tpl';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file controllers/grid/users/author/form/AuthorForm.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 AuthorForm
|
||||
*
|
||||
* @ingroup controllers_grid_users_author_form
|
||||
*
|
||||
* @deprecated 3.4
|
||||
*
|
||||
* @brief Form for adding/editing a author
|
||||
*/
|
||||
|
||||
namespace APP\controllers\grid\users\author\form;
|
||||
|
||||
use PKP\controllers\grid\users\author\form\PKPAuthorForm;
|
||||
|
||||
class AuthorForm extends PKPAuthorForm
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/AppServiceProvider.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 AppServiceProvider
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief Resolves requests for application classes such as the request handler
|
||||
* to support dependency injection
|
||||
*/
|
||||
|
||||
namespace APP\core;
|
||||
|
||||
use PKP\core\PKPRequest;
|
||||
|
||||
class AppServiceProvider extends \PKP\core\AppServiceProvider
|
||||
{
|
||||
/**
|
||||
* @copydoc \PKP\core\AppServiceProvider::register()
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
parent::register();
|
||||
|
||||
$this->app->bind(Request::class, PKPRequest::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/Application.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 Application
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @see PKPApplication
|
||||
*
|
||||
* @brief Class describing this application.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace APP\core;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\journal\JournalDAO;
|
||||
use APP\payment\ojs\OJSPaymentManager;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\security\Role;
|
||||
use PKP\submission\RepresentationDAOInterface;
|
||||
|
||||
class Application extends PKPApplication
|
||||
{
|
||||
public const ASSOC_TYPE_ARTICLE = self::ASSOC_TYPE_SUBMISSION; // DEPRECATED but needed by filter framework;
|
||||
public const ASSOC_TYPE_GALLEY = self::ASSOC_TYPE_REPRESENTATION;
|
||||
|
||||
public const ASSOC_TYPE_JOURNAL = 0x0000100;
|
||||
public const ASSOC_TYPE_ISSUE = 0x0000103;
|
||||
public const ASSOC_TYPE_ISSUE_GALLEY = 0x0000105;
|
||||
|
||||
public const CONTEXT_JOURNAL = 1; // not used?
|
||||
|
||||
public const REQUIRES_XSL = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
if (!PKP_STRICT_MODE) {
|
||||
foreach ([
|
||||
'REQUIRES_XSL',
|
||||
'ASSOC_TYPE_ARTICLE',
|
||||
'ASSOC_TYPE_GALLEY',
|
||||
'ASSOC_TYPE_JOURNAL',
|
||||
'ASSOC_TYPE_ISSUE',
|
||||
'ASSOC_TYPE_ISSUE_GALLEY',
|
||||
'CONTEXT_JOURNAL',
|
||||
] as $constantName) {
|
||||
if (!defined($constantName)) {
|
||||
define($constantName, constant('self::' . $constantName));
|
||||
}
|
||||
}
|
||||
if (!class_exists('\Application')) {
|
||||
class_alias('\APP\core\Application', '\Application');
|
||||
}
|
||||
}
|
||||
|
||||
// Add application locales
|
||||
Locale::registerPath(BASE_SYS_DIR . '/locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the application context.
|
||||
*/
|
||||
public function getContextName(): string
|
||||
{
|
||||
return 'journal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the symbolic name of this application
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName()
|
||||
{
|
||||
return 'ojs2';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale key for the name of this application.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNameKey()
|
||||
{
|
||||
return('common.software');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL to the XML descriptor for the current version of this
|
||||
* application.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVersionDescriptorUrl()
|
||||
{
|
||||
return 'https://pkp.sfu.ca/ojs/xml/ojs-version.xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of DAOName => full.class.Path for this application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDAOMap()
|
||||
{
|
||||
return array_merge(parent::getDAOMap(), [
|
||||
'ArticleSearchDAO' => 'APP\search\ArticleSearchDAO',
|
||||
'IndividualSubscriptionDAO' => 'APP\subscription\IndividualSubscriptionDAO',
|
||||
'InstitutionalSubscriptionDAO' => 'APP\subscription\InstitutionalSubscriptionDAO',
|
||||
'IssueGalleyDAO' => 'APP\issue\IssueGalleyDAO',
|
||||
'IssueFileDAO' => 'APP\issue\IssueFileDAO',
|
||||
'JournalDAO' => 'APP\journal\JournalDAO',
|
||||
'MetricsDAO' => 'APP\statistics\MetricsDAO',
|
||||
'OAIDAO' => 'APP\oai\ojs\OAIDAO',
|
||||
'OJSCompletedPaymentDAO' => 'APP\payment\ojs\OJSCompletedPaymentDAO',
|
||||
'SubscriptionDAO' => 'APP\subscription\SubscriptionDAO',
|
||||
'SubscriptionTypeDAO' => 'APP\subscription\SubscriptionTypeDAO',
|
||||
'TemporaryTotalsDAO' => 'APP\statistics\TemporaryTotalsDAO',
|
||||
'TemporaryItemInvestigationsDAO' => 'APP\statistics\TemporaryItemInvestigationsDAO',
|
||||
'TemporaryItemRequestsDAO' => 'APP\statistics\TemporaryItemRequestsDAO',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of plugin categories for this application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPluginCategories()
|
||||
{
|
||||
return [
|
||||
// NB: Meta-data plug-ins are first in the list as this
|
||||
// will make them load (and install) first.
|
||||
// This is necessary as several other plug-in categories
|
||||
// depend on meta-data. This is a very rudimentary type of
|
||||
// dependency management for plug-ins.
|
||||
'metadata',
|
||||
'blocks',
|
||||
'gateways',
|
||||
'generic',
|
||||
'importexport',
|
||||
'oaiMetadataFormats',
|
||||
'paymethod',
|
||||
'pubIds',
|
||||
'reports',
|
||||
'themes'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the top-level context DAO.
|
||||
*
|
||||
* @return JournalDAO
|
||||
*/
|
||||
public static function getContextDAO()
|
||||
{
|
||||
/** @var JournalDAO */
|
||||
$dao = DAORegistry::getDAO('JournalDAO');
|
||||
return $dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the representation DAO.
|
||||
*
|
||||
* @return \PKP\galley\DAO&RepresentationDAOInterface
|
||||
*/
|
||||
public static function getRepresentationDAO(): RepresentationDAOInterface
|
||||
{
|
||||
return Repo::galley()->dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a SubmissionSearchIndex instance.
|
||||
*/
|
||||
public static function getSubmissionSearchIndex()
|
||||
{
|
||||
return new \APP\search\ArticleSearchIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a SubmissionSearchDAO instance.
|
||||
*/
|
||||
public static function getSubmissionSearchDAO()
|
||||
{
|
||||
return DAORegistry::getDAO('ArticleSearchDAO');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stages used by the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getApplicationStages()
|
||||
{
|
||||
// We leave out WORKFLOW_STAGE_ID_PUBLISHED since it technically is not a 'stage'.
|
||||
return [
|
||||
WORKFLOW_STAGE_ID_SUBMISSION,
|
||||
WORKFLOW_STAGE_ID_EXTERNAL_REVIEW,
|
||||
WORKFLOW_STAGE_ID_EDITING,
|
||||
WORKFLOW_STAGE_ID_PRODUCTION
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context type for this application.
|
||||
*
|
||||
* @return int Application::ASSOC_TYPE_...
|
||||
*/
|
||||
public static function getContextAssocType()
|
||||
{
|
||||
return self::ASSOC_TYPE_JOURNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file directory array map used by the application.
|
||||
*/
|
||||
public static function getFileDirectories()
|
||||
{
|
||||
return ['context' => '/journals/', 'submission' => '/articles/'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPApplication::getRoleNames()
|
||||
*
|
||||
* @param null|mixed $roleIds
|
||||
*/
|
||||
public static function getRoleNames($contextOnly = false, $roleIds = null)
|
||||
{
|
||||
$roleNames = parent::getRoleNames($contextOnly, $roleIds);
|
||||
if (!$roleIds || in_array(Role::ROLE_ID_SUBSCRIPTION_MANAGER, $roleIds)) {
|
||||
$roleNames[Role::ROLE_ID_SUBSCRIPTION_MANAGER] = 'user.role.subscriptionManager';
|
||||
}
|
||||
return $roleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the payment manager.
|
||||
*
|
||||
* @param \APP\journal\Journal $context
|
||||
*
|
||||
* @return OJSPaymentManager
|
||||
*/
|
||||
public static function getPaymentManager($context)
|
||||
{
|
||||
return new OJSPaymentManager($context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/PageRouter.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 PageRouter
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief Class providing OJS-specific page routing.
|
||||
*/
|
||||
|
||||
namespace APP\core;
|
||||
|
||||
class PageRouter extends \PKP\core\PKPPageRouter
|
||||
{
|
||||
/**
|
||||
* get the cacheable pages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCacheablePages()
|
||||
{
|
||||
return ['about', 'announcement', 'help', 'index', 'information', 'issue', ''];
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\core\PageRouter', '\PageRouter');
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/Request.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 Request
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @brief @verbatim Class providing operations associated with HTTP requests.
|
||||
* Requests are assumed to be in the format http://host.tld/index.php/<journal_id>/<page_name>/<operation_name>/<arguments...>
|
||||
* <journal_id> is assumed to be "index" for top-level site requests. @endverbatim
|
||||
*/
|
||||
|
||||
namespace APP\core;
|
||||
|
||||
use APP\journal\Journal;
|
||||
use PKP\core\PKPRequest;
|
||||
|
||||
class Request extends PKPRequest
|
||||
{
|
||||
/**
|
||||
* @see PKPPageRouter::getContext()
|
||||
*/
|
||||
public function getJournal(): ?Journal
|
||||
{
|
||||
return $this->getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
*
|
||||
* @see PKPPageRouter::getContext()
|
||||
*/
|
||||
public function getContext(): ?Journal
|
||||
{
|
||||
return parent::getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated
|
||||
*
|
||||
* @see PKPPageRouter::url()
|
||||
*
|
||||
* @param null|mixed $journalPath
|
||||
* @param null|mixed $page
|
||||
* @param null|mixed $op
|
||||
* @param null|mixed $path
|
||||
* @param null|mixed $params
|
||||
* @param null|mixed $anchor
|
||||
*/
|
||||
public function url(
|
||||
$journalPath = null,
|
||||
$page = null,
|
||||
$op = null,
|
||||
$path = null,
|
||||
$params = null,
|
||||
$anchor = null,
|
||||
$escape = false
|
||||
) {
|
||||
return $this->_delegateToRouter(
|
||||
'url',
|
||||
$journalPath,
|
||||
$page,
|
||||
$op,
|
||||
$path,
|
||||
$params,
|
||||
$anchor,
|
||||
$escape
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/core/Services.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 Services
|
||||
*
|
||||
* @ingroup core
|
||||
*
|
||||
* @see Core
|
||||
*
|
||||
* @brief Pimple Dependency Injection Container.
|
||||
*/
|
||||
|
||||
namespace APP\core;
|
||||
|
||||
use APP\services\OJSServiceProvider;
|
||||
|
||||
class Services extends \PKP\core\PKPServices
|
||||
{
|
||||
/**
|
||||
* container initialization
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$this->container->register(new OJSServiceProvider());
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\core\Services', '\Services');
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup decision Decision
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/decision/Decision.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 Decision
|
||||
*
|
||||
* @ingroup decision
|
||||
*
|
||||
* @see DAO
|
||||
*
|
||||
* @brief An editorial decision taken on a submission, such as to accept, decline or request revisions.
|
||||
*/
|
||||
|
||||
namespace APP\decision;
|
||||
|
||||
use PKP\decision\Decision as BaseDecision;
|
||||
|
||||
class Decision extends BaseDecision
|
||||
{
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
// Some constants are not redefined here because they never existed as global constants
|
||||
define('SUBMISSION_EDITOR_DECISION_EXTERNAL_REVIEW', Decision::EXTERNAL_REVIEW);
|
||||
define('SUBMISSION_EDITOR_DECISION_ACCEPT', Decision::ACCEPT);
|
||||
define('SUBMISSION_EDITOR_DECISION_DECLINE', Decision::DECLINE);
|
||||
define('SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS', Decision::PENDING_REVISIONS);
|
||||
define('SUBMISSION_EDITOR_DECISION_RESUBMIT', Decision::RESUBMIT);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/decision/Repository.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 Repository
|
||||
*
|
||||
* @brief A repository to find and manage editorial decisions.
|
||||
*/
|
||||
|
||||
namespace APP\decision;
|
||||
|
||||
use APP\decision\types\Accept;
|
||||
use APP\decision\types\SkipExternalReview;
|
||||
use APP\notification\Notification;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use PKP\decision\types\BackFromCopyediting;
|
||||
use PKP\decision\types\BackFromProduction;
|
||||
use PKP\decision\types\CancelReviewRound;
|
||||
use PKP\decision\types\Decline;
|
||||
use PKP\decision\types\InitialDecline;
|
||||
use PKP\decision\types\NewExternalReviewRound;
|
||||
use PKP\decision\types\RecommendAccept;
|
||||
use PKP\decision\types\RecommendDecline;
|
||||
use PKP\decision\types\RecommendResubmit;
|
||||
use PKP\decision\types\RecommendRevisions;
|
||||
use PKP\decision\types\RequestRevisions;
|
||||
use PKP\decision\types\Resubmit;
|
||||
use PKP\decision\types\RevertDecline;
|
||||
use PKP\decision\types\RevertInitialDecline;
|
||||
use PKP\decision\types\SendExternalReview;
|
||||
use PKP\decision\types\SendToProduction;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class Repository extends \PKP\decision\Repository
|
||||
{
|
||||
/** The valid decision types */
|
||||
protected ?Collection $decisionTypes;
|
||||
|
||||
public function getDecisionTypes(): Collection
|
||||
{
|
||||
if (!isset($this->decisionTypes)) {
|
||||
$decisionTypes = new Collection([
|
||||
new Accept(),
|
||||
new Decline(),
|
||||
new InitialDecline(),
|
||||
new NewExternalReviewRound(),
|
||||
new RecommendAccept(),
|
||||
new RecommendDecline(),
|
||||
new RecommendResubmit(),
|
||||
new RecommendRevisions(),
|
||||
new Resubmit(),
|
||||
new RequestRevisions(),
|
||||
new RevertDecline(),
|
||||
new RevertInitialDecline(),
|
||||
new SendExternalReview(),
|
||||
new SendToProduction(),
|
||||
new SkipExternalReview(),
|
||||
new BackFromProduction(),
|
||||
new BackFromCopyediting(),
|
||||
new CancelReviewRound(),
|
||||
]);
|
||||
Hook::call('Decision::types', [$decisionTypes]);
|
||||
$this->decisionTypes = $decisionTypes;
|
||||
}
|
||||
|
||||
return $this->decisionTypes;
|
||||
}
|
||||
|
||||
public function getDeclineDecisionTypes(): array
|
||||
{
|
||||
return [
|
||||
new InitialDecline(),
|
||||
new Decline(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function getReviewNotificationTypes(): array
|
||||
{
|
||||
return [Notification::NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS];
|
||||
}
|
||||
|
||||
public function getDecisionTypesMadeByRecommendingUsers(int $stageId): array
|
||||
{
|
||||
$recommendatorsAvailableDecisions = [];
|
||||
switch($stageId) {
|
||||
case WORKFLOW_STAGE_ID_SUBMISSION:
|
||||
$recommendatorsAvailableDecisions = [
|
||||
new SendExternalReview()
|
||||
];
|
||||
}
|
||||
|
||||
Hook::call('Workflow::RecommendatorDecisions', [&$recommendatorsAvailableDecisions, $stageId]);
|
||||
|
||||
return $recommendatorsAvailableDecisions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/decision/types/Accept.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 Accept
|
||||
*
|
||||
* @brief Extend the Accept decision to support APC payments
|
||||
*/
|
||||
|
||||
namespace APP\decision\types;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\decision\Decision;
|
||||
use APP\decision\types\traits\RequestPayment;
|
||||
use APP\submission\Submission;
|
||||
use Illuminate\Validation\Validator;
|
||||
use PKP\context\Context;
|
||||
use PKP\decision\Steps;
|
||||
use PKP\decision\types\Accept as TypesAccept;
|
||||
use PKP\submission\reviewRound\ReviewRound;
|
||||
use PKP\user\User;
|
||||
|
||||
class Accept extends TypesAccept
|
||||
{
|
||||
use RequestPayment;
|
||||
|
||||
public function validate(array $props, Submission $submission, Context $context, Validator $validator, ?int $reviewRoundId = null)
|
||||
{
|
||||
parent::validate($props, $submission, $context, $validator, $reviewRoundId);
|
||||
|
||||
if (!isset($props['actions'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ((array) $props['actions'] as $index => $action) {
|
||||
$actionErrorKey = 'actions.' . $index;
|
||||
switch ($action['id']) {
|
||||
case $this->ACTION_PAYMENT:
|
||||
$this->validatePaymentAction($action, $actionErrorKey, $validator, $context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function runAdditionalActions(Decision $decision, Submission $submission, User $editor, Context $context, array $actions)
|
||||
{
|
||||
parent::runAdditionalActions($decision, $submission, $editor, $context, $actions);
|
||||
|
||||
foreach ($actions as $action) {
|
||||
switch ($action['id']) {
|
||||
case self::ACTION_PAYMENT:
|
||||
$this->requestPayment($submission, $editor, $context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getSteps(Submission $submission, Context $context, User $editor, ?ReviewRound $reviewRound): Steps
|
||||
{
|
||||
$steps = parent::getSteps($submission, $context, $editor, $reviewRound);
|
||||
|
||||
// Request payment if configured
|
||||
$paymentManager = Application::getPaymentManager($context);
|
||||
if ($paymentManager->publicationEnabled()) {
|
||||
$steps->addStep($this->getPaymentForm($context), true);
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/decision/types/SkipExternalReview.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 SkipExternalReview
|
||||
*
|
||||
* @brief Extend the skip review decision to handle APC payments.
|
||||
*/
|
||||
|
||||
namespace APP\decision\types;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\decision\Decision;
|
||||
use APP\decision\types\traits\RequestPayment;
|
||||
use APP\submission\Submission;
|
||||
use Illuminate\Validation\Validator;
|
||||
use PKP\context\Context;
|
||||
use PKP\decision\Steps;
|
||||
use PKP\decision\types\SkipExternalReview as PKPSkipExternalReview;
|
||||
use PKP\submission\reviewRound\ReviewRound;
|
||||
use PKP\user\User;
|
||||
|
||||
class SkipExternalReview extends PKPSkipExternalReview
|
||||
{
|
||||
use RequestPayment;
|
||||
|
||||
public function validate(array $props, Submission $submission, Context $context, Validator $validator, ?int $reviewRoundId = null)
|
||||
{
|
||||
parent::validate($props, $submission, $context, $validator, $reviewRoundId);
|
||||
|
||||
if (!isset($props['actions'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ((array) $props['actions'] as $index => $action) {
|
||||
$actionErrorKey = 'actions.' . $index;
|
||||
switch ($action['id']) {
|
||||
case self::ACTION_PAYMENT:
|
||||
$this->validatePaymentAction($action, $actionErrorKey, $validator, $context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function runAdditionalActions(Decision $decision, Submission $submission, User $editor, Context $context, array $actions)
|
||||
{
|
||||
parent::runAdditionalActions($decision, $submission, $editor, $context, $actions);
|
||||
|
||||
foreach ($actions as $action) {
|
||||
switch ($action['id']) {
|
||||
case self::ACTION_PAYMENT:
|
||||
$this->requestPayment($submission, $editor, $context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getSteps(Submission $submission, Context $context, User $editor, ?ReviewRound $reviewRound): Steps
|
||||
{
|
||||
$steps = parent::getSteps($submission, $context, $editor, $reviewRound);
|
||||
|
||||
// Request payment if configured
|
||||
$paymentManager = Application::getPaymentManager($context);
|
||||
if ($paymentManager->publicationEnabled()) {
|
||||
$steps->addStep($this->getPaymentForm($context), true);
|
||||
}
|
||||
|
||||
return $steps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/decision/types/traits/RequestPayment.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 decision
|
||||
*
|
||||
* @brief Helper functions for decisions that may request a payment
|
||||
*/
|
||||
|
||||
namespace APP\decision\types\traits;
|
||||
|
||||
use APP\components\forms\decision\RequestPaymentDecisionForm;
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\mail\mailables\PaymentRequest;
|
||||
use APP\notification\Notification;
|
||||
use APP\notification\NotificationManager;
|
||||
use APP\payment\ojs\OJSPaymentManager;
|
||||
use APP\submission\Submission;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Validation\Validator;
|
||||
use PKP\context\Context;
|
||||
use PKP\decision\steps\Form;
|
||||
use PKP\user\User;
|
||||
|
||||
trait RequestPayment
|
||||
{
|
||||
protected string $ACTION_PAYMENT = 'payment';
|
||||
|
||||
/**
|
||||
* Get the form to request or waive payment
|
||||
*/
|
||||
protected function getPaymentForm(Context $context): Form
|
||||
{
|
||||
return new Form(
|
||||
$this->ACTION_PAYMENT,
|
||||
__('editor.article.payment.requestPayment'),
|
||||
'',
|
||||
new RequestPaymentDecisionForm($context)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the decision action to request or waive payment
|
||||
*/
|
||||
protected function validatePaymentAction(array $action, string $actionErrorKey, Validator $validator, Context $context)
|
||||
{
|
||||
$paymentManager = Application::getPaymentManager($context);
|
||||
if (!$paymentManager->publicationEnabled()) {
|
||||
$validator->errors()->add($actionErrorKey . '.requestPayment', __('payment.requestPublicationFee.notEnabled'));
|
||||
} elseif (!isset($action['requestPayment'])) {
|
||||
$validator->errors()->add($actionErrorKey . '.requestPayment', __('validator.required'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request payment from authors
|
||||
*/
|
||||
protected function requestPayment(Submission $submission, User $editor, Context $context)
|
||||
{
|
||||
$paymentManager = Application::getPaymentManager($context);
|
||||
$queuedPayment = $paymentManager->createQueuedPayment(
|
||||
Application::get()->getRequest(),
|
||||
OJSPaymentManager::PAYMENT_TYPE_PUBLICATION,
|
||||
$editor->getId(),
|
||||
$submission->getId(),
|
||||
$context->getData('publicationFee'),
|
||||
$context->getData('currency')
|
||||
);
|
||||
$paymentManager->queuePayment($queuedPayment);
|
||||
|
||||
// Notify authors that this needs payment.
|
||||
$notificationMgr = new NotificationManager();
|
||||
$authorIds = $this->getAssignedAuthorIds($submission);
|
||||
foreach ($authorIds as $authorId) {
|
||||
$notificationMgr->createNotification(
|
||||
Application::get()->getRequest(),
|
||||
$authorId,
|
||||
Notification::NOTIFICATION_TYPE_PAYMENT_REQUIRED,
|
||||
$context->getId(),
|
||||
Application::ASSOC_TYPE_QUEUED_PAYMENT,
|
||||
$queuedPayment->getId(),
|
||||
Notification::NOTIFICATION_LEVEL_TASK
|
||||
);
|
||||
|
||||
$mailable = new PaymentRequest($context, $submission, $queuedPayment);
|
||||
$template = Repo::emailTemplate()->getByKey($context->getId(), $mailable::getEmailTemplateKey());
|
||||
$mailable->from($context->getData('contactEmail'), $context->getData('contactName'))
|
||||
->recipients([Repo::user()->get($authorId)])
|
||||
->subject($template->getLocalizedData('subject'))
|
||||
->body($template->getLocalizedData('body'));
|
||||
|
||||
Mail::send($mailable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/doi/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 doi
|
||||
*
|
||||
* @see Doi
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Doi objects.
|
||||
*/
|
||||
|
||||
namespace APP\doi;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PKP\context\Context;
|
||||
use PKP\doi\Doi;
|
||||
use PKP\submission\PKPSubmission;
|
||||
|
||||
class DAO extends \PKP\doi\DAO
|
||||
{
|
||||
/**
|
||||
* Gets all depositable submission IDs along with all associated DOI IDs for use in DOI bulk deposit jobs.
|
||||
* This method is used to collect all valid submissions/IDs in a single query specifically for use with
|
||||
* queued jobs for depositing DOIs with a registration agency.
|
||||
*
|
||||
*/
|
||||
public function getAllDepositableSubmissionIds(Context $context): Collection
|
||||
{
|
||||
$enabledDoiTypes = $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? [];
|
||||
|
||||
$q = DB::table($this->table, 'd')
|
||||
->leftJoin('publications as p', 'd.doi_id', '=', 'p.doi_id')
|
||||
->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
|
||||
->where('d.context_id', '=', $context->getId())
|
||||
->where(function (Builder $q) use ($enabledDoiTypes) {
|
||||
// Publication DOIs
|
||||
$q->when(in_array(Repo::doi()::TYPE_PUBLICATION, $enabledDoiTypes), function (Builder $q) {
|
||||
$q->whereIn('d.doi_id', function (Builder $q) {
|
||||
$q->select('p.doi_id')
|
||||
->from('publications', 'p')
|
||||
->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
|
||||
->whereColumn('p.publication_id', '=', 's.current_publication_id')
|
||||
->whereNotNull('p.doi_id')
|
||||
->where('p.status', '=', PKPSubmission::STATUS_PUBLISHED);
|
||||
});
|
||||
})
|
||||
// Galley DOIs
|
||||
->when(in_array(Repo::doi()::TYPE_REPRESENTATION, $enabledDoiTypes), function (Builder $q) {
|
||||
$q->orWhereIn('d.doi_id', function (Builder $q) {
|
||||
$q->select('g.doi_id')
|
||||
->from('publication_galleys', 'g')
|
||||
->leftJoin('publications as p', 'g.publication_id', '=', 'p.publication_id')
|
||||
->leftJoin('submissions as s', 'p.publication_id', '=', 's.current_publication_id')
|
||||
->whereColumn('p.publication_id', '=', 's.current_publication_id')
|
||||
->whereNotNull('g.doi_id')
|
||||
->where('p.status', '=', PKPSubmission::STATUS_PUBLISHED);
|
||||
});
|
||||
});
|
||||
});
|
||||
$q->whereIn('d.status', [Doi::STATUS_UNREGISTERED, Doi::STATUS_ERROR, Doi::STATUS_STALE]);
|
||||
return $q->get(['s.submission_id', 'd.doi_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all depositable issues IDs along with all associated DOI IDs for use in DOI bulk deposit jobs.
|
||||
* This method is used to collect all valid issues/IDs in a single query specifically for use with
|
||||
* queued jobs for depositing DOIs with a registration agency.
|
||||
*
|
||||
*/
|
||||
public function getAllDepositableIssueIds(Context $context): Collection
|
||||
{
|
||||
$q = DB::table($this->table, 'd')
|
||||
->leftJoin('issues as i', 'i.doi_id', '=', 'd.doi_id')
|
||||
->where('i.journal_id', '=', $context->getId())
|
||||
->whereNotNull('i.doi_id')
|
||||
->where('i.published', '=', 1)
|
||||
->whereIn('d.status', [Doi::STATUS_UNREGISTERED, Doi::STATUS_ERROR, Doi::STATUS_STALE]);
|
||||
|
||||
return $q->get(['i.issue_id', 'i.doi_id']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/doi/Repository.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 Repository
|
||||
*
|
||||
* @brief A repository to find and manage DOIs.
|
||||
*/
|
||||
|
||||
namespace APP\doi;
|
||||
|
||||
use APP\core\Request;
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Issue;
|
||||
use APP\jobs\doi\DepositIssue;
|
||||
use APP\journal\Journal;
|
||||
use APP\journal\JournalDAO;
|
||||
use APP\plugins\PubIdPlugin;
|
||||
use APP\publication\Publication;
|
||||
use APP\submission\Submission;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\DataObject;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\doi\Collector;
|
||||
use PKP\doi\exceptions\DoiException;
|
||||
use PKP\galley\Galley;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\submission\Representation;
|
||||
|
||||
class Repository extends \PKP\doi\Repository
|
||||
{
|
||||
public const TYPE_ISSUE = 'issue';
|
||||
|
||||
public const CUSTOM_ISSUE_PATTERN = 'doiIssueSuffixPattern';
|
||||
|
||||
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
||||
{
|
||||
parent::__construct($dao, $request, $schemaService);
|
||||
}
|
||||
|
||||
public function getCollector(): Collector
|
||||
{
|
||||
return App::makeWith(Collector::class, ['dao' => $this->dao]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DOI for the given publication.
|
||||
*
|
||||
* @throws DoiException
|
||||
*/
|
||||
public function mintPublicationDoi(Publication $publication, Submission $submission, Context $context): int
|
||||
{
|
||||
// Default suffix does not rely on any other metadata
|
||||
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
||||
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
||||
}
|
||||
|
||||
// If not using default suffix, additional checks are required
|
||||
$issueId = $publication->getData('issueId');
|
||||
if ($issueId === null) {
|
||||
throw new DoiException(
|
||||
DoiException::PUBLICATION_MISSING_ISSUE,
|
||||
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
||||
$publication->getLocalizedFullTitle()
|
||||
);
|
||||
}
|
||||
|
||||
$issue = Repo::issue()->get($publication->getData('issueId'));
|
||||
if ($issue === null) {
|
||||
throw new DoiException(
|
||||
DoiException::PUBLICATION_MISSING_ISSUE,
|
||||
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
||||
$publication->getLocalizedFullTitle()
|
||||
);
|
||||
} elseif ($issue && $context->getId() != $issue->getJournalId()) {
|
||||
throw new DoiException(
|
||||
DoiException::PUBLICATION_MISSING_ISSUE,
|
||||
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
||||
$publication->getLocalizedFullTitle()
|
||||
);
|
||||
}
|
||||
|
||||
$doiSuffix = $this->generateSuffixPattern($publication, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue, $submission);
|
||||
|
||||
return $this->mintAndStoreDoi($context, $doiSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DOI for the given galley
|
||||
*
|
||||
* @throws DoiException
|
||||
*/
|
||||
public function mintGalleyDoi(Galley $galley, Publication $publication, Submission $submission, Context $context): int
|
||||
{
|
||||
// Default suffix does not rely on any other metadata
|
||||
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
||||
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
||||
}
|
||||
|
||||
// If not using default suffix, additional checks are required
|
||||
$issue = Repo::issue()->getBySubmissionId($submission->getId());
|
||||
|
||||
if ($issue === null) {
|
||||
throw new DoiException(
|
||||
DoiException::REPRESENTATION_MISSING_ISSUE,
|
||||
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
||||
$galley->getLabel()
|
||||
);
|
||||
} elseif ($issue && $context->getId() != $issue->getJournalId()) {
|
||||
throw new DoiException(
|
||||
DoiException::REPRESENTATION_MISSING_ISSUE,
|
||||
$submission->getCurrentPublication()->getLocalizedFullTitle(),
|
||||
$galley->getLabel()
|
||||
);
|
||||
}
|
||||
|
||||
$doiSuffix = $this->generateSuffixPattern($galley, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue, $submission, $galley);
|
||||
|
||||
return $this->mintAndStoreDoi($context, $doiSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DOI for the given Issue
|
||||
*
|
||||
* @throws DoiException
|
||||
*/
|
||||
public function mintIssueDoi(Issue $issue, Context $context): int
|
||||
{
|
||||
if ($context->getId() != $issue->getJournalId()) {
|
||||
throw new DoiException(
|
||||
DoiException::INCORRECT_ISSUE_CONTEXT,
|
||||
$issue->getLocalizedTitle(),
|
||||
$issue->getLocalizedTitle()
|
||||
);
|
||||
}
|
||||
|
||||
// Default suffix does not rely on any other metadata
|
||||
if ($context->getData(Context::SETTING_DOI_SUFFIX_TYPE) === Repo::doi()::SUFFIX_DEFAULT) {
|
||||
return $this->mintAndStoreDoi($context, $this->generateDefaultSuffix());
|
||||
}
|
||||
|
||||
// If not using default suffix, use pattern generator
|
||||
$doiSuffix = $this->generateSuffixPattern($issue, $context, $context->getData(Context::SETTING_DOI_SUFFIX_TYPE), $issue);
|
||||
|
||||
return $this->mintAndStoreDoi($context, $doiSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles updating issue DOI status when metadata changes
|
||||
*
|
||||
*/
|
||||
public function issueUpdated(Issue $issue)
|
||||
{
|
||||
$doiIds = Repo::doi()->getDoisForIssue($issue->getId());
|
||||
$this->dao->markStale($doiIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a suffix using a provided pattern type
|
||||
*
|
||||
* @param string $patternType Repo::doi()::CUSTOM_SUFFIX_* constants
|
||||
*
|
||||
*/
|
||||
protected function generateSuffixPattern(
|
||||
DataObject $object,
|
||||
Context $context,
|
||||
string $patternType,
|
||||
?Issue $issue = null,
|
||||
?Submission $submission = null,
|
||||
?Representation $representation = null
|
||||
): string {
|
||||
$doiSuffix = '';
|
||||
switch ($patternType) {
|
||||
case self::SUFFIX_CUSTOM_PATTERN:
|
||||
$pubIdSuffixPattern = $this->getPubIdSuffixPattern($object, $context);
|
||||
$doiSuffix = PubIdPlugin::generateCustomPattern($context, $pubIdSuffixPattern, $object, $issue, $submission, $representation);
|
||||
break;
|
||||
case self::SUFFIX_MANUAL:
|
||||
break;
|
||||
}
|
||||
|
||||
return $doiSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all DOI IDs related to a submission
|
||||
*
|
||||
* @return array<int> DOI IDs
|
||||
*/
|
||||
public function getDoisForSubmission(int $submissionId): array
|
||||
{
|
||||
$doiIds = Collection::make();
|
||||
|
||||
$submission = Repo::submission()->get($submissionId);
|
||||
/** @var Publication[] $publications */
|
||||
$publications = $submission->getData('publications');
|
||||
|
||||
|
||||
/** @var JournalDAO $contextDao */
|
||||
$contextDao = DAORegistry::getDAO('JournalDAO');
|
||||
/** @var Journal $context */
|
||||
$context = $contextDao->getById($submission->getData('contextId'));
|
||||
|
||||
foreach ($publications as $publication) {
|
||||
$publicationDoiId = $publication->getData('doiId');
|
||||
if (!empty($publicationDoiId) && $context->isDoiTypeEnabled(self::TYPE_PUBLICATION)) {
|
||||
$doiIds->add($publicationDoiId);
|
||||
}
|
||||
|
||||
// Galleys
|
||||
$galleys = Repo::galley()->getCollector()
|
||||
->filterByPublicationIds(['publicationIds' => $publication->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($galleys as $galley) {
|
||||
$galleyDoiId = $galley->getData('doiId');
|
||||
if (!empty($galleyDoiId) && $context->isDoiTypeEnabled(self::TYPE_REPRESENTATION)) {
|
||||
$doiIds->add($galleyDoiId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $doiIds->unique()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all DOIs associated with an issue
|
||||
* NB: Assumes only enabled DOI types are allowed
|
||||
*
|
||||
* @param bool $enabledDoiTypesOnly
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array<int> DOI IDs
|
||||
*
|
||||
*/
|
||||
public function getDoisForIssue(int $issueId, $enabledDoiTypesOnly = false): array
|
||||
{
|
||||
$doiIds = [];
|
||||
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
$issueDoiId = $issue->getData('doiId');
|
||||
|
||||
/** @var JournalDAO $contextDao */
|
||||
$contextDao = DAORegistry::getDAO('JournalDAO');
|
||||
/** @var Journal $context */
|
||||
$context = $contextDao->getById($issue->getData('journalId'));
|
||||
|
||||
if (!empty($issueDoiId)) {
|
||||
if ($enabledDoiTypesOnly == false || ($enabledDoiTypesOnly && $context->isDoiTypeEnabled(self::TYPE_ISSUE))) {
|
||||
$doiIds[] = $issueDoiId;
|
||||
}
|
||||
}
|
||||
return $doiIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules DOI deposits with the active registration agency for all valid and
|
||||
* unregistered/stale publication items. Items are added as a queued job to be
|
||||
* completed asynchronously.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public function depositAll(Context $context)
|
||||
{
|
||||
parent::depositAll($context);
|
||||
if (in_array(Repo::doi()::TYPE_ISSUE, $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? [])) {
|
||||
// If there is no configured registration agency, nothing can be deposited.
|
||||
$agency = $context->getConfiguredDoiAgency();
|
||||
if (!$agency) {
|
||||
return;
|
||||
}
|
||||
/** @var DAO */
|
||||
$dao = $this->dao;
|
||||
$issuesCollection = $dao->getAllDepositableIssueIds($context);
|
||||
$issueData = $issuesCollection->reduce(function ($carry, $item) {
|
||||
$carry['issueIds'][] = $item->issue_id;
|
||||
$carry['doiIds'][] = $item->doi_id;
|
||||
return $carry;
|
||||
}, ['issueIds' => [], 'doiIds' => []]);
|
||||
|
||||
// Schedule/queue jobs for issues
|
||||
foreach ($issueData['issueIds'] as $issueId) {
|
||||
dispatch(new DepositIssue($issueId, $context, $agency));
|
||||
}
|
||||
|
||||
// Mark issue DOIs as submitted
|
||||
Repo::doi()->markSubmitted($issueData['doiIds']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a DOI object is referenced by ID on any pub objects for a given pub object type.
|
||||
*
|
||||
* @param string $pubObjectType One of Repo::doi()::TYPE_* constants
|
||||
*/
|
||||
public function isAssigned(int $doiId, string $pubObjectType): bool
|
||||
{
|
||||
$isAssigned = match ($pubObjectType) {
|
||||
Repo::doi()::TYPE_REPRESENTATION => Repo::galley()
|
||||
->getCollector()
|
||||
->filterByDoiIds([$doiId])
|
||||
->getIds()
|
||||
->count(),
|
||||
default => false,
|
||||
};
|
||||
|
||||
return $isAssigned || parent::isAssigned($doiId, $pubObjectType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get app-specific DOI type constants to check when scheduling deposit for submissions
|
||||
*/
|
||||
protected function getValidSubmissionDoiTypes(): array
|
||||
{
|
||||
return [
|
||||
self::TYPE_PUBLICATION,
|
||||
self::TYPE_REPRESENTATION,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets legacy, user-generated suffix pattern associated with object type and context
|
||||
*
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function getPubIdSuffixPattern(DataObject $object, Context $context)
|
||||
{
|
||||
if ($object instanceof Issue) {
|
||||
return $context->getData(Repo::doi()::CUSTOM_ISSUE_PATTERN);
|
||||
} elseif ($object instanceof Representation) {
|
||||
return $context->getData(Repo::doi()::CUSTOM_REPRESENTATION_PATTERN);
|
||||
} else {
|
||||
return $context->getData(Repo::doi()::CUSTOM_PUBLICATION_PATTERN);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/emailTemplate/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 email templates to the database.
|
||||
*/
|
||||
|
||||
namespace APP\emailTemplate;
|
||||
|
||||
class DAO extends \PKP\emailTemplate\DAO
|
||||
{
|
||||
/**
|
||||
* Renames app-specific email template variables during installation
|
||||
*/
|
||||
protected function variablesToRename(): array
|
||||
{
|
||||
return [
|
||||
'contextName' => 'journalName',
|
||||
'contextUrl' => 'journalUrl',
|
||||
'contextSignature' => 'journalSignature',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/facades/Repo.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 Repo
|
||||
*
|
||||
* @brief Extends the base Repo facade with any overrides for OJS
|
||||
*/
|
||||
|
||||
namespace APP\facades;
|
||||
|
||||
use APP\decision\Repository as DecisionRepository;
|
||||
use APP\doi\Repository as DoiRepository;
|
||||
use APP\issue\Repository as IssueRepository;
|
||||
use APP\mail\Repository as MailRepository;
|
||||
use APP\publication\Repository as PublicationRepository;
|
||||
use APP\section\Repository as SectionRepository;
|
||||
use APP\submission\Repository as SubmissionRepository;
|
||||
use APP\submissionFile\Repository as SubmissionFileRepository;
|
||||
use APP\user\Repository as UserRepository;
|
||||
use PKP\facades\Repo as BaseRepo;
|
||||
use PKP\galley\Repository as GalleyRepository;
|
||||
use PKP\highlight\Repository as HighlightRepository;
|
||||
|
||||
class Repo extends BaseRepo
|
||||
{
|
||||
public static function doi(): DoiRepository
|
||||
{
|
||||
return app(DoiRepository::class);
|
||||
}
|
||||
|
||||
public static function decision(): DecisionRepository
|
||||
{
|
||||
return app(DecisionRepository::class);
|
||||
}
|
||||
|
||||
public static function galley(): GalleyRepository
|
||||
{
|
||||
return app(GalleyRepository::class);
|
||||
}
|
||||
|
||||
public static function highlight(): HighlightRepository
|
||||
{
|
||||
return app(HighlightRepository::class);
|
||||
}
|
||||
|
||||
public static function issue(): IssueRepository
|
||||
{
|
||||
return app(IssueRepository::class);
|
||||
}
|
||||
|
||||
public static function publication(): PublicationRepository
|
||||
{
|
||||
return app(PublicationRepository::class);
|
||||
}
|
||||
|
||||
public static function section(): SectionRepository
|
||||
{
|
||||
return app(SectionRepository::class);
|
||||
}
|
||||
|
||||
public static function submission(): SubmissionRepository
|
||||
{
|
||||
return app(SubmissionRepository::class);
|
||||
}
|
||||
|
||||
public static function submissionFile(): SubmissionFileRepository
|
||||
{
|
||||
return app(SubmissionFileRepository::class);
|
||||
}
|
||||
|
||||
public static function user(): UserRepository
|
||||
{
|
||||
return app(UserRepository::class);
|
||||
}
|
||||
|
||||
public static function mailable(): MailRepository
|
||||
{
|
||||
return app(MailRepository::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/file/IssueFileManager.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 IssueFileManager
|
||||
*
|
||||
* @ingroup file
|
||||
*
|
||||
* @brief Class defining operations for issue file management.
|
||||
*
|
||||
* Issue directory structure:
|
||||
* [issue id]/public
|
||||
*/
|
||||
|
||||
namespace APP\file;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\IssueFile;
|
||||
use APP\issue\IssueFileDAO;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\Core;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\file\TemporaryFile;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class IssueFileManager extends FileManager
|
||||
{
|
||||
/** @var string the path to location of the files */
|
||||
public $_filesDir = null;
|
||||
|
||||
/** @var int the associated issue ID */
|
||||
public $_issueId = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Create a manager for handling issue files.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function __construct($issueId)
|
||||
{
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
assert(isset($issue));
|
||||
|
||||
$this->setIssueId($issueId);
|
||||
$this->setFilesDir(Config::getVar('files', 'files_dir') . '/journals/' . $issue->getJournalId() . '/issues/' . $issueId . '/');
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issue files directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFilesDir()
|
||||
{
|
||||
return $this->_filesDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the issue files directory.
|
||||
*
|
||||
* @param string $filesDir
|
||||
*/
|
||||
public function setFilesDir($filesDir)
|
||||
{
|
||||
$this->_filesDir = $filesDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issue ID.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIssueId()
|
||||
{
|
||||
return $this->_issueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the issue ID.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function setIssueId($issueId)
|
||||
{
|
||||
$this->_issueId = (int) $issueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an issue file by ID.
|
||||
*
|
||||
* @param int $fileId
|
||||
*
|
||||
* @return bool if successful
|
||||
*/
|
||||
public function deleteById($fileId)
|
||||
{
|
||||
$issueFileDao = DAORegistry::getDAO('IssueFileDAO'); /** @var IssueFileDAO $issueFileDao */
|
||||
$issueFile = $issueFileDao->getById($fileId);
|
||||
|
||||
if (parent::deleteByPath($this->getFilesDir() . $this->contentTypeToPath($issueFile->getContentType()) . '/' . $issueFile->getServerFileName())) {
|
||||
$issueFileDao->deleteById($fileId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the entire tree of files belonging to an issue.
|
||||
*/
|
||||
public function deleteIssueTree()
|
||||
{
|
||||
parent::rmtree($this->getFilesDir());
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file.
|
||||
*
|
||||
* @param int $fileId the file id of the file to download
|
||||
* @param bool $inline print file as inline instead of attachment, optional
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function downloadById($fileId, $inline = false)
|
||||
{
|
||||
$issueFileDao = DAORegistry::getDAO('IssueFileDAO'); /** @var IssueFileDAO $issueFileDao */
|
||||
$issueFile = $issueFileDao->getById($fileId);
|
||||
|
||||
if ($issueFile) {
|
||||
$fileType = $issueFile->getFileType();
|
||||
$filePath = $this->getFilesDir() . $this->contentTypeToPath($issueFile->getContentType()) . '/' . $issueFile->getServerFileName();
|
||||
|
||||
return parent::downloadByPath($filePath, $fileType, $inline, $issueFile->getOriginalFileName());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return directory path based on issue content type (used for naming files).
|
||||
*
|
||||
* @param int $contentType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function contentTypeToPath($contentType)
|
||||
{
|
||||
switch ($contentType) {
|
||||
case IssueFile::ISSUE_FILE_PUBLIC: return 'public';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return abbreviation based on issue content type (used for naming files).
|
||||
*
|
||||
* @param int $contentType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function contentTypeToAbbrev($contentType)
|
||||
{
|
||||
switch ($contentType) {
|
||||
case IssueFile::ISSUE_FILE_PUBLIC: return 'PB';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an issue galley based on a temporary file.
|
||||
*
|
||||
* @param TemporaryFile $temporaryFile
|
||||
* @param int $contentType Issue file content type
|
||||
*
|
||||
* @return ?IssueFile|false the resulting issue file
|
||||
*/
|
||||
public function fromTemporaryFile($temporaryFile, $contentType = IssueFile::ISSUE_FILE_PUBLIC)
|
||||
{
|
||||
$result = null;
|
||||
if (Hook::call('IssueFileManager::fromTemporaryFile', [&$temporaryFile, &$contentType, &$result])) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$issueId = $this->getIssueId();
|
||||
$issueFileDao = DAORegistry::getDAO('IssueFileDAO'); /** @var IssueFileDAO $issueFileDao */
|
||||
|
||||
$contentTypePath = $this->contentTypeToPath($contentType);
|
||||
$dir = $this->getFilesDir() . $contentTypePath . '/';
|
||||
|
||||
$issueFile = $issueFileDao->newDataObject();
|
||||
$issueFile->setIssueId($issueId);
|
||||
$issueFile->setDateUploaded($temporaryFile->getDateUploaded());
|
||||
$issueFile->setDateModified(Core::getCurrentDate());
|
||||
$issueFile->setServerFileName(''); // Blank until we insert to generate a file ID
|
||||
$issueFile->setFileType($temporaryFile->getFileType());
|
||||
$issueFile->setFileSize($temporaryFile->getFileSize());
|
||||
$issueFile->setOriginalFileName($temporaryFile->getOriginalFileName());
|
||||
$issueFile->setContentType($contentType);
|
||||
|
||||
if (!$issueFileDao->insertObject($issueFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$extension = $this->parseFileExtension($issueFile->getOriginalFileName());
|
||||
$newFileName = $issueFile->getIssueId() . '-' . $issueFile->getId() . '-' . $this->contentTypeToAbbrev($contentType) . '.' . $extension;
|
||||
$issueFile->setServerFileName($newFileName);
|
||||
|
||||
// Copy the actual file
|
||||
if (!$this->copyFile($temporaryFile->getFilePath(), $dir . $newFileName)) {
|
||||
// Upload failed; remove the new DB record.
|
||||
$issueFileDao->deleteById($issueFile->getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload succeeded. Update issue file record with new filename.
|
||||
$issueFileDao->updateObject($issueFile);
|
||||
|
||||
return $issueFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\file\IssueFileManager', '\IssueFileManager');
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/file/LibraryFileManager.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 LibraryFileManager
|
||||
*
|
||||
* @ingroup file
|
||||
*
|
||||
* @brief Wrapper class for uploading files to a site/context' library directory.
|
||||
*/
|
||||
|
||||
namespace APP\file;
|
||||
|
||||
use PKP\file\PKPLibraryFileManager;
|
||||
|
||||
class LibraryFileManager extends PKPLibraryFileManager
|
||||
{
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\file\LibraryFileManager', '\LibraryFileManager');
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/file/PublicFileManager.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 PublicFileManager
|
||||
*
|
||||
* @ingroup file
|
||||
*
|
||||
* @brief Wrapper class for uploading files to a site/journal's public directory.
|
||||
*/
|
||||
|
||||
namespace APP\file;
|
||||
|
||||
use PKP\config\Config;
|
||||
use PKP\file\PKPPublicFileManager;
|
||||
|
||||
class PublicFileManager extends PKPPublicFileManager
|
||||
{
|
||||
/**
|
||||
* @copydoc PKPPublicFileManager::getContextFilesPath()
|
||||
*/
|
||||
public function getContextFilesPath($contextId)
|
||||
{
|
||||
return Config::getVar('files', 'public_files_dir') . '/journals/' . (int) $contextId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\file\PublicFileManager', '\PublicFileManager');
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/handler/Handler.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 Handler
|
||||
*
|
||||
* @ingroup handler
|
||||
*
|
||||
* @brief Base request handler application class
|
||||
*/
|
||||
|
||||
namespace APP\handler;
|
||||
|
||||
use PKP\handler\PKPHandler;
|
||||
|
||||
class Handler extends PKPHandler
|
||||
{
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\handler\Handler', '\Handler');
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup i18n I18N
|
||||
* Implements localization concerns such as locale files, time zones, and country lists.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/i18n/AppLocale.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 PKPLocale
|
||||
*
|
||||
* @ingroup i18n
|
||||
*
|
||||
* @brief Deprecated class, kept only for backwards compatibility with external plugins
|
||||
*/
|
||||
|
||||
namespace APP\i18n;
|
||||
|
||||
use PKP\i18n\PKPLocale;
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
/**
|
||||
* @deprecated The class \APP\i18n\AppLocale has been replaced by PKP\facades\Locale
|
||||
*/
|
||||
class AppLocale extends PKPLocale
|
||||
{
|
||||
}
|
||||
|
||||
class_alias('\APP\i18n\AppLocale', '\AppLocale');
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/install/Install.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 Install
|
||||
*
|
||||
* @ingroup install
|
||||
*
|
||||
* @see Installer, InstallForm
|
||||
*
|
||||
* @brief Perform system installation.
|
||||
*
|
||||
* This script will:
|
||||
* - Create the database (optionally), and install the database tables and initial data.
|
||||
* - Update the config file with installation parameters.
|
||||
*/
|
||||
|
||||
namespace APP\install;
|
||||
|
||||
use PKP\install\PKPInstall;
|
||||
|
||||
class Install extends PKPInstall
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @see install.form.InstallForm for the expected parameters
|
||||
*
|
||||
* @param array $params installer parameters
|
||||
* @param string $descriptor descriptor path
|
||||
* @param bool $isPlugin true iff a plugin is being installed
|
||||
*/
|
||||
public function __construct($params, $descriptor = 'install.xml', $isPlugin = false)
|
||||
{
|
||||
parent::__construct($descriptor, $params, $isPlugin);
|
||||
}
|
||||
|
||||
//
|
||||
// Installer actions
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the names of the directories to create.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCreateDirectories()
|
||||
{
|
||||
$directories = parent::getCreateDirectories();
|
||||
$directories[] = 'journals';
|
||||
return $directories;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\install\Install', '\Install');
|
||||
}
|
||||
@@ -0,0 +1,691 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/install/Upgrade.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 Upgrade
|
||||
*
|
||||
* @ingroup install
|
||||
*
|
||||
* @brief Perform system upgrade.
|
||||
*/
|
||||
|
||||
namespace APP\install;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use APP\file\PublicFileManager;
|
||||
use APP\journal\JournalDAO;
|
||||
use APP\template\TemplateManager;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\Core;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\identity\Identity;
|
||||
use PKP\install\Installer;
|
||||
use PKP\navigationMenu\NavigationMenuItemDAO;
|
||||
use PKP\plugins\PluginSettingsDAO;
|
||||
use PKP\security\Role;
|
||||
use PKP\site\SiteDAO;
|
||||
use PKP\stageAssignment\StageAssignmentDAO;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class Upgrade extends Installer
|
||||
{
|
||||
protected $appEmailTemplateVariableNames = [
|
||||
'contextName' => 'journalName',
|
||||
'contextUrl' => 'journalUrl',
|
||||
'contextSignature' => 'journalSignature',
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $params upgrade parameters
|
||||
* @param string $installFile Name of XML descriptor to install
|
||||
* @param bool $isPlugin True iff the installer is for a plugin.
|
||||
*/
|
||||
public function __construct($params, $installFile = 'upgrade.xml', $isPlugin = false)
|
||||
{
|
||||
parent::__construct($installFile, $params, $isPlugin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true iff this is an upgrade process.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isUpgrade()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Upgrade actions
|
||||
//
|
||||
|
||||
/**
|
||||
* Rebuild the search index.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function rebuildSearchIndex()
|
||||
{
|
||||
$submissionSearchIndex = Application::getSubmissionSearchIndex();
|
||||
$submissionSearchIndex->rebuildIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the CSS cache files (needed when changing LESS files)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clearCssCache()
|
||||
{
|
||||
$request = Application::get()->getRequest();
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->clearCssCache();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For 3.1.0 upgrade (#2467): In multi-journal upgrades from OJS 2.x, the
|
||||
* user_group_id column in the authors table may be updated to point to
|
||||
* user groups in other journals.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function fixAuthorGroup()
|
||||
{
|
||||
$rows = DB::table('authors as a')
|
||||
->leftJoin('submissions as s', 's.submission_id', '=', 'a.submission_id')
|
||||
->leftJoin('user_groups as g', 'a.user_group_id', '=', 'g.user_group_id')
|
||||
->whereColumn('g.context_id', '<>', 's.context_id')
|
||||
->get(['a.author_id', 's.context_id']);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$authorGroup = Repo::userGroup()->getByRoleIds([Role::ROLE_ID_AUTHOR], $row->context_id, true);
|
||||
if ($authorGroup) {
|
||||
DB::table('authors')
|
||||
->where('author_id', '=', $row->author_id)
|
||||
->update(['user_group_id' => $authorGroup->getId()]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* For 3.0.x - 3.1.1 upgrade: repair the migration of the supp files.
|
||||
*
|
||||
* @return bool True indicates success.
|
||||
*/
|
||||
public function repairSuppFilesFilestage()
|
||||
{
|
||||
$fileManager = new FileManager();
|
||||
|
||||
$rows = DB::table('submission_supplementary_files as ssf')
|
||||
->leftJoin('submission_files as sf', 'sf.file_id', '=', 'ssf.file_id')
|
||||
->leftJoin('submissions as s', 's.submission_id', '=', 'sf.submission_id')
|
||||
->where('sf.file_stage', '=', SubmissionFile::SUBMISSION_FILE_SUBMISSION)
|
||||
->where('sf.assoc_type', '=', Application::ASSOC_TYPE_REPRESENTATION)
|
||||
->whereColumn('sf.revision', '=', 'ssf.revision')
|
||||
->get();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$submissionDir = Repo::submissionFile()
|
||||
->getSubmissionDir($row->context_id, $row->submission_id);
|
||||
$generatedOldFilename = sprintf(
|
||||
'%d-%s-%d-%d-%d-%s.%s',
|
||||
$row->submission_id,
|
||||
$row->genre_id,
|
||||
$row->file_id,
|
||||
$row->revision,
|
||||
$row->file_stage,
|
||||
date('Ymd', strtotime($row->date_uploaded)),
|
||||
strtolower_codesafe($fileManager->parseFileExtension($row->original_file_name))
|
||||
);
|
||||
$generatedNewFilename = sprintf(
|
||||
'%d-%s-%d-%d-%d-%s.%s',
|
||||
$row->submission_id,
|
||||
$row->genre_id,
|
||||
$row->file_id,
|
||||
$row->revision,
|
||||
SubmissionFile::SUBMISSION_FILE_PROOF,
|
||||
date('Ymd', strtotime($row->date_uploaded)),
|
||||
strtolower_codesafe($fileManager->parseFileExtension($row->original_file_name))
|
||||
);
|
||||
$oldFileName = $submissionDir . '/' . $this->_fileStageToPath($row->file_stage) . '/' . $generatedOldFilename;
|
||||
$newFileName = $submissionDir . '/' . $this->_fileStageToPath($row->file_stage) . '/' . $generatedNewFilename;
|
||||
if (!Services::get('file')->fs->rename($oldFileName, $newFileName)) {
|
||||
error_log("Unable to move \"{$oldFileName}\" to \"{$newFileName}\".");
|
||||
}
|
||||
DB::table('submission_files')
|
||||
->where('file_id', '=', $row->file_id)
|
||||
->where('revision', '=', $row->revision)
|
||||
->update(['file_stage' => SubmissionFile::SUBMISSION_FILE_PROOF]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If StaticPages table exists we should port the data as NMIs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function migrateStaticPagesToNavigationMenuItems()
|
||||
{
|
||||
if ($this->tableExists('static_pages')) {
|
||||
$contextDao = Application::getContextDAO();
|
||||
$navigationMenuItemDao = DAORegistry::getDAO('NavigationMenuItemDAO'); /** @var NavigationMenuItemDAO $navigationMenuItemDao */
|
||||
|
||||
$staticPagesDao = new \APP\plugins\generic\staticPages\classes\StaticPagesDAO();
|
||||
|
||||
$contexts = $contextDao->getAll();
|
||||
while ($context = $contexts->next()) {
|
||||
$contextStaticPages = $staticPagesDao->getByContextId($context->getId())->toAssociativeArray();
|
||||
foreach ($contextStaticPages as $staticPage) {
|
||||
$retNMIId = $navigationMenuItemDao->portStaticPage($staticPage);
|
||||
if ($retNMIId) {
|
||||
$staticPagesDao->deleteById($staticPage->getId());
|
||||
} else {
|
||||
error_log('WARNING: The StaticPage "' . $staticPage->getLocalizedTitle() . '" uses a path (' . $staticPage->getPath() . ') that conflicts with an existing Custom Navigation Menu Item path. Skipping this StaticPage.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate sr_SR locale to the new sr_RS@latin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function migrateSRLocale()
|
||||
{
|
||||
$oldLocale = 'sr_SR';
|
||||
$newLocale = 'sr_RS@latin';
|
||||
|
||||
$oldLocaleStringLength = 's:5';
|
||||
|
||||
$journalSettingsDao = new class () extends \PKP\db\DAO {
|
||||
/**
|
||||
* Method for update journal setting
|
||||
*
|
||||
* @param int $journalId
|
||||
* @param string $name
|
||||
* @param string $type data type of the setting. If omitted, type will be guessed
|
||||
* @param bool $isLocalized
|
||||
*/
|
||||
public function updateSetting($journalId, $name, $value, $type = null, $isLocalized = false)
|
||||
{
|
||||
if (!$isLocalized) {
|
||||
$value = $this->convertToDB($value, $type);
|
||||
DB::table('journal_settings')->updateOrInsert(
|
||||
['journal_id' => (int) $journalId, 'setting_name' => $name, 'locale' => ''],
|
||||
['setting_value' => $value, 'setting_type' => $type]
|
||||
);
|
||||
} else {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $locale => $localeValue) {
|
||||
$this->update('DELETE FROM journal_settings WHERE journal_id = ? AND setting_name = ? AND locale = ?', [(int) $journalId, $name, $locale]);
|
||||
if (empty($localeValue)) {
|
||||
continue;
|
||||
}
|
||||
$type = null;
|
||||
$this->update(
|
||||
'INSERT INTO journal_settings (journal_id, setting_name, setting_value, setting_type, locale) VALUES (?, ?, ?, ?, ?)',
|
||||
[$journalId, $name, $this->convertToDB($localeValue, $type), $type, $locale]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a context setting value.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $locale optional
|
||||
*/
|
||||
public function getSetting($journalId, $name, $locale = null)
|
||||
{
|
||||
$params = [(int) $journalId, $name];
|
||||
if ($locale) {
|
||||
$params[] = $locale;
|
||||
}
|
||||
$result = $this->retrieve(
|
||||
'SELECT setting_name, setting_value, setting_type, locale
|
||||
FROM journal_settings
|
||||
WHERE journal_id = ? AND
|
||||
setting_name = ?' .
|
||||
($locale ? ' AND locale = ?' : ''),
|
||||
$params
|
||||
);
|
||||
|
||||
$returner = [];
|
||||
foreach ($result as $row) {
|
||||
$returner[$row->locale] = $this->convertFromDB($row->setting_value, $row->setting_type);
|
||||
}
|
||||
if (count($returner) == 1) {
|
||||
return array_shift($returner);
|
||||
}
|
||||
if (count($returner) == 0) {
|
||||
return false;
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the sr_SR is used, and if not do not run further
|
||||
$srExistResult = $journalSettingsDao->retrieve('SELECT COUNT(*) AS row_count FROM site WHERE installed_locales LIKE ?', ['%' . $oldLocale . '%']);
|
||||
$row = $srExistResult->current();
|
||||
$srExist = $row && $row->row_count;
|
||||
if (!$srExist) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Consider all DB tables that have locale column:
|
||||
$dbTables = [
|
||||
'announcement_settings', 'announcement_type_settings', 'author_settings', 'books_for_review_settings', 'citation_settings', 'controlled_vocab_entry_settings',
|
||||
'data_object_tombstone_settings', 'email_templates_data', 'email_templates_default_data', 'external_feed_settings', 'filter_settings', 'genre_settings', 'group_settings',
|
||||
'issue_galleys', 'issue_galley_settings', 'issue_settings', 'journal_settings', 'library_file_settings',
|
||||
'navigation_menu_item_assignment_settings', 'navigation_menu_item_settings', 'notification_settings', 'referral_settings',
|
||||
'review_form_element_settings', 'review_form_settings', 'review_object_metadata_settings', 'review_object_type_settings', 'section_settings', 'site_settings',
|
||||
'static_page_settings', 'submissions', 'submission_file_settings', 'submission_galleys', 'submission_galley_settings', 'submission_settings', 'subscription_type_settings',
|
||||
'user_group_settings', 'user_settings',
|
||||
];
|
||||
foreach ($dbTables as $dbTable) {
|
||||
if ($this->tableExists($dbTable)) {
|
||||
$journalSettingsDao->update('UPDATE ' . $dbTable . ' SET locale = ? WHERE locale = ?', [$newLocale, $oldLocale]);
|
||||
}
|
||||
}
|
||||
// Consider other locale columns
|
||||
$journalSettingsDao->update('UPDATE journals SET primary_locale = ? WHERE primary_locale = ?', [$newLocale, $oldLocale]);
|
||||
$journalSettingsDao->update('UPDATE site SET primary_locale = ? WHERE primary_locale = ?', [$newLocale, $oldLocale]);
|
||||
$journalSettingsDao->update('UPDATE site SET installed_locales = REPLACE(installed_locales, ?, ?)', [$oldLocale, $newLocale]);
|
||||
$journalSettingsDao->update('UPDATE site SET supported_locales = REPLACE(supported_locales, ?, ?)', [$oldLocale, $newLocale]);
|
||||
$journalSettingsDao->update('UPDATE users SET locales = REPLACE(locales, ?, ?)', [$oldLocale, $newLocale]);
|
||||
|
||||
// journal_settings
|
||||
// Consider array setting values from the setting names:
|
||||
// supportedFormLocales, supportedLocales, supportedSubmissionLocales
|
||||
$settingNames = "('supportedFormLocales', 'supportedLocales', 'supportedSubmissionLocales')";
|
||||
// As a precaution use $oldLocaleStringLength, to exclude that the text contain the old locale string
|
||||
$settingValueResult = $journalSettingsDao->retrieve('SELECT * FROM journal_settings WHERE setting_name IN ' . $settingNames . ' AND setting_value LIKE ? AND setting_type = \'object\'', ['%' . $oldLocaleStringLength . ':"' . $oldLocale . '%']);
|
||||
foreach ($settingValueResult as $row) {
|
||||
$arraySettingValue = $journalSettingsDao->getSetting($row->journal_id, $row->setting_name);
|
||||
for ($i = 0; $i < count($arraySettingValue); $i++) {
|
||||
if ($arraySettingValue[$i] == $oldLocale) {
|
||||
$arraySettingValue[$i] = $newLocale;
|
||||
}
|
||||
}
|
||||
$journalSettingsDao->updateSetting($row->journal_id, $row->setting_name, $arraySettingValue);
|
||||
}
|
||||
|
||||
// Consider journal images
|
||||
// Note that the locale column values are already changed above
|
||||
$publicFileManager = new PublicFileManager();
|
||||
$settingNames = "('homeHeaderLogoImage', 'homeHeaderTitleImage', 'homepageImage', 'journalFavicon', 'journalThumbnail', 'pageHeaderLogoImage', 'pageHeaderTitleImage')";
|
||||
$settingValueResult = $journalSettingsDao->retrieve('SELECT * FROM journal_settings WHERE setting_name IN ' . $settingNames . ' AND locale = ? AND setting_value LIKE ? AND setting_type = \'object\'', [$newLocale, '%' . $oldLocale . '%']);
|
||||
foreach ($settingValueResult as $row) {
|
||||
$arraySettingValue = $journalSettingsDao->getSetting($row->journal_id, $row->setting_name, $newLocale);
|
||||
$oldUploadName = $arraySettingValue['uploadName'];
|
||||
$newUploadName = str_replace('_' . $oldLocale . '.', '_' . $newLocale . '.', $oldUploadName);
|
||||
if ($publicFileManager->fileExists($publicFileManager->getContextFilesPath($row->journal_id) . '/' . $oldUploadName)) {
|
||||
$publicFileManager->copyContextFile($row->journal_id, $publicFileManager->getContextFilesPath($row->journal_id) . '/' . $oldUploadName, $newUploadName);
|
||||
$publicFileManager->removeContextFile($row->journal_id, $oldUploadName);
|
||||
}
|
||||
$arraySettingValue['uploadName'] = $newUploadName;
|
||||
$newArraySettingValue[$newLocale] = $arraySettingValue;
|
||||
$journalSettingsDao->updateSetting($row->journal_id, $row->setting_name, $newArraySettingValue, 'object', true);
|
||||
}
|
||||
|
||||
// Consider issue cover images
|
||||
// Note that the locale column values are already changed above
|
||||
$settingValueResult = $journalSettingsDao->retrieve('SELECT a.*, b.journal_id FROM issue_settings a, issues b WHERE a.setting_name = \'fileName\' AND a.locale = ? AND a.setting_value LIKE ? AND a.setting_type = \'string\' AND b.issue_id = a.issue_id', [$newLocale, '%' . $oldLocale . '%']);
|
||||
foreach ($settingValueResult as $row) {
|
||||
$oldCoverImage = $row->setting_value;
|
||||
$newCoverImage = str_replace('_' . $oldLocale . '.', '_' . $newLocale . '.', $oldCoverImage);
|
||||
if ($publicFileManager->fileExists($publicFileManager->getContextFilesPath($row->journal_id) . '/' . $oldCoverImage)) {
|
||||
$publicFileManager->copyContextFile($row->journal_id, $publicFileManager->getContextFilesPath($row->journal_id) . '/' . $oldCoverImage, $newCoverImage);
|
||||
$publicFileManager->removeContextFile($row->journal_id, $oldCoverImage);
|
||||
}
|
||||
$journalSettingsDao->update('UPDATE issue_settings SET setting_value = ? WHERE issue_id = ? AND setting_name = \'fileName\' AND locale = ?', [$newCoverImage, (int) $row->issue_id, $newLocale]);
|
||||
}
|
||||
|
||||
// Consider article cover images
|
||||
// Note that the locale column values are already changed above
|
||||
$settingValueResult = $journalSettingsDao->retrieve('SELECT a.*, b.context_id FROM submission_settings a, submissions b WHERE a.setting_name = \'fileName\' AND a.locale = ? AND a.setting_value LIKE ? AND b.submission_id = a.submission_id', [$newLocale, '%' . $oldLocale . '%']);
|
||||
foreach ($settingValueResult as $row) {
|
||||
$oldCoverImage = $row->setting_value;
|
||||
$newCoverImage = str_replace('_' . $oldLocale . '.', '_' . $newLocale . '.', $oldCoverImage);
|
||||
if ($publicFileManager->fileExists($publicFileManager->getContextFilesPath($row->context_id) . '/' . $oldCoverImage)) {
|
||||
$publicFileManager->copyContextFile($row->context_id, $publicFileManager->getContextFilesPath($row->context_id) . '/' . $oldCoverImage, $newCoverImage);
|
||||
$publicFileManager->removeContextFile($row->context_id, $oldCoverImage);
|
||||
}
|
||||
$journalSettingsDao->update('UPDATE submission_settings SET setting_value = ? WHERE submission_id = ? AND setting_name = \'fileName\' AND locale = ?', [$newCoverImage, (int) $row->submission_id, $newLocale]);
|
||||
}
|
||||
|
||||
// plugin_settings
|
||||
// Consider array setting values from the setting names:
|
||||
// blockContent (from a custom block plugin), additionalInformation (from objects for review plugin)
|
||||
$pluginSettingsDao = DAORegistry::getDAO('PluginSettingsDAO'); /** @var PluginSettingsDAO $pluginSettingsDao */
|
||||
$settingNames = "('blockContent', 'additionalInformation')";
|
||||
$settingValueResult = $pluginSettingsDao->retrieve('SELECT * FROM plugin_settings WHERE setting_name IN ' . $settingNames . ' AND setting_value LIKE ?', ['%' . $oldLocaleStringLength . ':"' . $oldLocale . '%']);
|
||||
foreach ($settingValueResult as $row) {
|
||||
$arraySettingValue = $pluginSettingsDao->getSetting($row->context_id, $row->plugin_name, $row->setting_name);
|
||||
$arraySettingValue[$newLocale] = $arraySettingValue[$oldLocale];
|
||||
unset($arraySettingValue[$oldLocale]);
|
||||
$pluginSettingsDao->updateSetting($row->context_id, $row->plugin_name, $row->setting_name, $arraySettingValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate first and last user names as multilingual into the DB table user_settings.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function migrateUserAndAuthorNames()
|
||||
{
|
||||
// the user names will be saved in the site's primary locale
|
||||
DB::insert("INSERT INTO user_settings (user_id, locale, setting_name, setting_value, setting_type) SELECT DISTINCT u.user_id, s.primary_locale, ?, u.first_name, 'string' FROM users_tmp u, site s", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
DB::insert("INSERT INTO user_settings (user_id, locale, setting_name, setting_value, setting_type) SELECT DISTINCT u.user_id, s.primary_locale, ?, u.last_name, 'string' FROM users_tmp u, site s", [Identity::IDENTITY_SETTING_FAMILYNAME]);
|
||||
// the author names will be saved in the submission's primary locale
|
||||
DB::insert("INSERT INTO author_settings (author_id, locale, setting_name, setting_value, setting_type) SELECT DISTINCT a.author_id, s.locale, ?, a.first_name, 'string' FROM authors_tmp a, submissions s WHERE s.submission_id = a.submission_id", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
DB::insert("INSERT INTO author_settings (author_id, locale, setting_name, setting_value, setting_type) SELECT DISTINCT a.author_id, s.locale, ?, a.last_name, 'string' FROM authors_tmp a, submissions s WHERE s.submission_id = a.submission_id", [Identity::IDENTITY_SETTING_FAMILYNAME]);
|
||||
|
||||
// middle name will be migrated to the given name
|
||||
// note that given names are already migrated to the settings table
|
||||
switch (Config::getVar('database', 'driver')) {
|
||||
case 'mysql':
|
||||
case 'mysqli':
|
||||
// the alias for _settings table cannot be used for some reason -- syntax error
|
||||
DB::update("UPDATE user_settings, users_tmp u SET user_settings.setting_value = CONCAT(user_settings.setting_value, ' ', u.middle_name) WHERE user_settings.setting_name = ? AND u.user_id = user_settings.user_id AND u.middle_name IS NOT NULL AND u.middle_name <> ''", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
DB::update("UPDATE author_settings, authors_tmp a SET author_settings.setting_value = CONCAT(author_settings.setting_value, ' ', a.middle_name) WHERE author_settings.setting_name = ? AND a.author_id = author_settings.author_id AND a.middle_name IS NOT NULL AND a.middle_name <> ''", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
break;
|
||||
case 'postgres':
|
||||
case 'postgres64':
|
||||
case 'postgres7':
|
||||
case 'postgres8':
|
||||
case 'postgres9':
|
||||
DB::update("UPDATE user_settings SET setting_value = CONCAT(setting_value, ' ', u.middle_name) FROM users_tmp u WHERE user_settings.setting_name = ? AND u.user_id = user_settings.user_id AND u.middle_name IS NOT NULL AND u.middle_name <> ''", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
DB::update("UPDATE author_settings SET setting_value = CONCAT(setting_value, ' ', a.middle_name) FROM authors_tmp a WHERE author_settings.setting_name = ? AND a.author_id = author_settings.author_id AND a.middle_name IS NOT NULL AND a.middle_name <> ''", [Identity::IDENTITY_SETTING_GIVENNAME]);
|
||||
break;
|
||||
default: fatalError('Unknown database type!');
|
||||
}
|
||||
|
||||
// salutation and suffix will be migrated to the preferred public name
|
||||
// user preferred public names will be inserted for each supported site locales
|
||||
$siteDao = DAORegistry::getDAO('SiteDAO'); /** @var SiteDAO $siteDao */
|
||||
$site = $siteDao->getSite();
|
||||
$supportedLocales = $site->getSupportedLocales();
|
||||
$userResult = DB::select(
|
||||
"SELECT user_id, first_name, last_name, middle_name, salutation, suffix FROM users_tmp
|
||||
WHERE (salutation IS NOT NULL AND salutation <> '') OR
|
||||
(suffix IS NOT NULL AND suffix <> '')"
|
||||
);
|
||||
foreach ($userResult as $row) {
|
||||
$userId = $row->user_id;
|
||||
$firstName = $row->first_name;
|
||||
$lastName = $row->last_name;
|
||||
$middleName = $row->middle_name;
|
||||
$salutation = $row->salutation;
|
||||
$suffix = $row->suffix;
|
||||
foreach ($supportedLocales as $siteLocale) {
|
||||
$preferredPublicName = ($salutation != '' ? "{$salutation} " : '') . "{$firstName} " . ($middleName != '' ? "{$middleName} " : '') . $lastName . ($suffix != '' ? ", {$suffix}" : '');
|
||||
DB::insert(
|
||||
"INSERT INTO user_settings (user_id, locale, setting_name, setting_value, setting_type) VALUES (?, ?, 'preferredPublicName', ?, 'string')",
|
||||
[(int) $userId, $siteLocale, $preferredPublicName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// author suffix will be migrated to the author preferred public name
|
||||
// author preferred public names will be inserted for each journal supported locale
|
||||
// get supported locales for all journals
|
||||
$journalDao = DAORegistry::getDAO('JournalDAO'); /** @var JournalDAO $journalDao */
|
||||
$journals = $journalDao->getAll();
|
||||
$journalsSupportedLocales = [];
|
||||
while ($journal = $journals->next()) {
|
||||
$journalsSupportedLocales[$journal->getId()] = $journal->getSupportedLocales();
|
||||
}
|
||||
// get all authors with a suffix
|
||||
$authorResult = DB::select(
|
||||
"SELECT a.author_id, a.first_name, a.last_name, a.middle_name, a.suffix, j.journal_id FROM authors_tmp a
|
||||
LEFT JOIN submissions s ON (s.submission_id = a.submission_id)
|
||||
LEFT JOIN journals j ON (j.journal_id = s.context_id)
|
||||
WHERE suffix IS NOT NULL AND suffix <> ''"
|
||||
);
|
||||
foreach ($authorResult as $row) {
|
||||
$authorId = $row->author_id;
|
||||
$firstName = $row->first_name;
|
||||
$lastName = $row->last_name;
|
||||
$middleName = $row->middle_name;
|
||||
$suffix = $row->suffix;
|
||||
$journalId = $row->journal_id;
|
||||
$supportedLocales = $journalsSupportedLocales[$journalId];
|
||||
foreach ($supportedLocales as $locale) {
|
||||
$preferredPublicName = "{$firstName} " . ($middleName != '' ? "{$middleName} " : '') . $lastName . ($suffix != '' ? ", {$suffix}" : '');
|
||||
DB::insert(
|
||||
"INSERT INTO author_settings (author_id, locale, setting_name, setting_value, setting_type) VALUES (?, ?, 'preferredPublicName', ?, 'string')",
|
||||
[(int) $authorId, $locale, $preferredPublicName]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove temporary table
|
||||
$siteDao->update('DROP TABLE users_tmp');
|
||||
$siteDao->update('DROP TABLE authors_tmp');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update assoc_id for assoc_type Application::ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER = 531
|
||||
*
|
||||
* @return bool True indicates success.
|
||||
*/
|
||||
public function updateSuppFileMetrics()
|
||||
{
|
||||
// Copy 531 assoc_type data to temp table
|
||||
DB::statement('CREATE TABLE metrics_supp AS (SELECT * FROM metrics WHERE assoc_type = 531)');
|
||||
// Fetch submission_file data with old-supp-id
|
||||
$result = DB::select(
|
||||
'SELECT * FROM submission_file_settings WHERE setting_name = ?',
|
||||
['old-supp-id']
|
||||
);
|
||||
// Loop through the data and save to temp table
|
||||
foreach ($result as $row) {
|
||||
// Use assoc_type 2531 to prevent collisions between old assoc_id and new assoc_id
|
||||
DB::update('UPDATE metrics_supp SET assoc_id = ?, assoc_type = ? WHERE assoc_type = ? AND assoc_id = ?', [(int) $row->file_id, 2531, 531, (int) $row->setting_value]);
|
||||
}
|
||||
// update temprorary 2531 values to 531 values
|
||||
DB::update('UPDATE metrics_supp SET assoc_type = ? WHERE assoc_type = ?', [531, 2531]);
|
||||
// delete all existing 531 values from the actual metrics table
|
||||
DB::statement('DELETE FROM metrics WHERE assoc_type = 531');
|
||||
// copy updated 531 values from metrics_supp to metrics table
|
||||
DB::insert('INSERT INTO metrics SELECT * FROM metrics_supp');
|
||||
// Drop metrics_supp table
|
||||
DB::statement('DROP TABLE metrics_supp');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry for the site stylesheet to the site_settings database when it
|
||||
* exists
|
||||
*/
|
||||
public function migrateSiteStylesheet()
|
||||
{
|
||||
$siteDao = DAORegistry::getDAO('SiteDAO'); /** @var SiteDAO $siteDao */
|
||||
|
||||
$publicFileManager = new PublicFileManager();
|
||||
|
||||
if (!file_exists($publicFileManager->getSiteFilesPath() . '/sitestyle.css')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$site = $siteDao->getSite();
|
||||
$site->setData('styleSheet', 'sitestyle.css');
|
||||
$siteDao->updateObject($site);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a context's copyrightNotice to a new licenseTerms setting, leaving
|
||||
* the copyrightNotice in place.
|
||||
*/
|
||||
public function createLicenseTerms()
|
||||
{
|
||||
$contextDao = Application::getContextDao();
|
||||
|
||||
$result = $contextDao->retrieve('SELECT * from ' . $contextDao->settingsTableName . " WHERE setting_name='copyrightNotice'");
|
||||
foreach ($result as $row) {
|
||||
$row = (array) $row;
|
||||
$contextDao->update(
|
||||
'
|
||||
INSERT INTO ' . $contextDao->settingsTableName . ' (
|
||||
' . $contextDao->primaryKeyColumn . ',
|
||||
locale,
|
||||
setting_name,
|
||||
setting_value
|
||||
) VALUES (?, ?, ?, ?)',
|
||||
[
|
||||
$row[$contextDao->primaryKeyColumn],
|
||||
$row['locale'],
|
||||
'licenseTerms',
|
||||
$row['setting_value'],
|
||||
]
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update permit_metadata_edit and can_change_metadata for user_groups and stage_assignments tables.
|
||||
*
|
||||
* @return bool True indicates success.
|
||||
*/
|
||||
public function changeUserRolesAndStageAssignmentsForStagePermitSubmissionEdit()
|
||||
{
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
|
||||
|
||||
$roles = Repo::userGroup()::NOT_CHANGE_METADATA_EDIT_PERMISSION_ROLES;
|
||||
$roleString = '(' . implode(',', $roles) . ')';
|
||||
|
||||
DB::table('user_groups')
|
||||
->whereIn('role_id', $roles)
|
||||
->update(['permit_metadata_edit' => 1]);
|
||||
|
||||
switch (Config::getVar('database', 'driver')) {
|
||||
case 'mysql':
|
||||
case 'mysqli':
|
||||
$stageAssignmentDao->update('UPDATE stage_assignments sa JOIN user_groups ug on sa.user_group_id = ug.user_group_id SET sa.can_change_metadata = 1 WHERE ug.role_id IN ' . $roleString);
|
||||
break;
|
||||
case 'postgres':
|
||||
case 'postgres64':
|
||||
case 'postgres7':
|
||||
case 'postgres8':
|
||||
case 'postgres9':
|
||||
$stageAssignmentDao->update('UPDATE stage_assignments sa SET can_change_metadata=1 FROM user_groups ug WHERE sa.user_group_id = ug.user_group_id AND ug.role_id IN ' . $roleString);
|
||||
break;
|
||||
default: fatalError('Unknown database type!');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update how submission cover images are stored
|
||||
*
|
||||
* Combines the coverImage and coverImageAltText settings in the
|
||||
* submissions table into an assoc array stored under the coverImage
|
||||
* setting.
|
||||
*
|
||||
* This will be migrated to the publication_settings table in
|
||||
* 3.2.0_versioning.xml.
|
||||
*/
|
||||
public function migrateSubmissionCoverImages()
|
||||
{
|
||||
$coverImagesBySubmission = [];
|
||||
|
||||
$deprecatedDao = Repo::submission()->dao->deprecatedDao;
|
||||
$result = $deprecatedDao->retrieve(
|
||||
'SELECT * from submission_settings WHERE setting_name=\'coverImage\' OR setting_name=\'coverImageAltText\''
|
||||
);
|
||||
foreach ($result as $row) {
|
||||
$submissionId = $row->submission_id;
|
||||
if (empty($coverImagesBySubmission[$submissionId])) {
|
||||
$coverImagesBySubmission[$submissionId] = [];
|
||||
}
|
||||
if ($row->setting_name === 'coverImage') {
|
||||
$coverImagesBySubmission[$submissionId]['uploadName'] = $row->setting_value;
|
||||
$coverImagesBySubmission[$submissionId]['dateUploaded'] = Core::getCurrentDate();
|
||||
} elseif ($row->setting_name === 'coverImageAltText') {
|
||||
$coverImagesBySubmission[$submissionId]['altText'] = $row->setting_value;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($coverImagesBySubmission as $submissionId => $coverImagesBySubmission) {
|
||||
$deprecatedDao->update(
|
||||
'UPDATE submission_settings
|
||||
SET setting_value = ?
|
||||
WHERE submission_id = ? AND setting_name = ?',
|
||||
[
|
||||
serialize($coverImagesBySubmission),
|
||||
$submissionId,
|
||||
'coverImage',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory of a file based on its file stage
|
||||
*
|
||||
* @param int $fileStage One of SubmissionFile::SUBMISSION_FILE_ constants
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function _fileStageToPath($fileStage)
|
||||
{
|
||||
static $fileStagePathMap = [
|
||||
SubmissionFile::SUBMISSION_FILE_SUBMISSION => 'submission',
|
||||
SubmissionFile::SUBMISSION_FILE_NOTE => 'note',
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_FILE => 'submission/review',
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT => 'submission/review/attachment',
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION => 'submission/review/revision',
|
||||
SubmissionFile::SUBMISSION_FILE_FINAL => 'submission/final',
|
||||
SubmissionFile::SUBMISSION_FILE_COPYEDIT => 'submission/copyedit',
|
||||
SubmissionFile::SUBMISSION_FILE_DEPENDENT => 'submission/proof',
|
||||
SubmissionFile::SUBMISSION_FILE_PROOF => 'submission/proof',
|
||||
SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY => 'submission/productionReady',
|
||||
SubmissionFile::SUBMISSION_FILE_ATTACHMENT => 'attachment',
|
||||
SubmissionFile::SUBMISSION_FILE_QUERY => 'submission/query',
|
||||
];
|
||||
|
||||
if (!isset($fileStagePathMap[$fileStage])) {
|
||||
throw new \Exception('A file assigned to the file stage ' . $fileStage . ' could not be migrated.');
|
||||
}
|
||||
|
||||
return $fileStagePathMap[$fileStage];
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\install\Upgrade', '\Upgrade');
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/issue/Collector.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 Collector
|
||||
*
|
||||
* @brief A helper class to configure a query Builder to get a collection of issues
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use Exception;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use InvalidArgumentException;
|
||||
use PKP\core\interfaces\CollectorInterface;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class Collector implements CollectorInterface
|
||||
{
|
||||
public const ORDERBY_DATE_PUBLISHED = 'datePublished';
|
||||
public const ORDERBY_LAST_MODIFIED = 'lastModified';
|
||||
public const ORDERBY_SEQUENCE = 'seq';
|
||||
public const ORDERBY_PUBLISHED_ISSUES = 'publishedIssues';
|
||||
public const ORDERBY_UNPUBLISHED_ISSUES = 'unpublishedIssues';
|
||||
public const ORDERBY_SHELF = 'shelf';
|
||||
public const ORDER_DIR_ASC = 'ASC';
|
||||
public const ORDER_DIR_DESC = 'DESC';
|
||||
private const ORDER_CURRENT_ISSUE = 'currentIssue';
|
||||
|
||||
public DAO $dao;
|
||||
|
||||
public ?int $count = null;
|
||||
|
||||
public ?int $offset = null;
|
||||
|
||||
/** @var array|null Context ID or PKPApplication::CONTEXT_ID_ALL to get from all contexts */
|
||||
public ?array $contextIds = null;
|
||||
|
||||
/** @var array|null List of issue IDs to include */
|
||||
public ?array $issueIds = null;
|
||||
|
||||
/** @var array|null order and direction pairing for queries */
|
||||
public ?array $resultOrderings = null;
|
||||
|
||||
/** @var bool|null return published issues */
|
||||
public ?bool $isPublished = null;
|
||||
|
||||
/** @var array|null return issues in volume(s) */
|
||||
public ?array $volumes = null;
|
||||
|
||||
/** @var array|null return issues with number(s) */
|
||||
public ?array $numbers = null;
|
||||
|
||||
/** @var array|null return issues with year(s) */
|
||||
public ?array $years = null;
|
||||
|
||||
/** @var array|null return issues that match a title */
|
||||
public ?array $titles = null;
|
||||
|
||||
public ?array $doiStatuses = null;
|
||||
|
||||
public ?bool $hasDois = null;
|
||||
|
||||
/** @var array Which DOI types should be considered when checking if a submission has DOIs set */
|
||||
public array $enabledDoiTypes = [];
|
||||
|
||||
/** @var string|null Returns Issue by URL path */
|
||||
public ?string $urlPath = null;
|
||||
|
||||
/** @var string|null return issues which match words from this search phrase */
|
||||
public ?string $searchPhrase = null;
|
||||
|
||||
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,Issue>
|
||||
*/
|
||||
public function getMany(): LazyCollection
|
||||
{
|
||||
return $this->dao->getMany($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set context issues filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByContextIds(?array $contextIds): static
|
||||
{
|
||||
$this->contextIds = $contextIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set issue ID filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByIssueIds(?array $issueIds): static
|
||||
{
|
||||
$this->issueIds = $issueIds;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set result order and direction based on an ORDERBY_* constant
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function orderBy(string $orderByConstant): static
|
||||
{
|
||||
$this->resultOrderings = match ($orderByConstant) {
|
||||
static::ORDERBY_LAST_MODIFIED => [
|
||||
['orderBy' => 'i.last_modified', 'direction' => static::ORDER_DIR_DESC]
|
||||
],
|
||||
static::ORDERBY_SEQUENCE => [
|
||||
['orderBy' => 'o.seq', 'direction' => static::ORDER_DIR_ASC]
|
||||
],
|
||||
static::ORDERBY_PUBLISHED_ISSUES => [
|
||||
['orderBy' => 'o.seq', 'direction' => static::ORDER_DIR_ASC],
|
||||
['orderBy' => 'currentIssue', 'direction' => static::ORDER_DIR_DESC],
|
||||
['orderBy' => 'i.date_published', 'direction' => static::ORDER_DIR_DESC]
|
||||
],
|
||||
static::ORDERBY_UNPUBLISHED_ISSUES => [
|
||||
['orderBy' => 'i.year', 'direction' => static::ORDER_DIR_ASC],
|
||||
['orderBy' => 'i.volume', 'direction' => static::ORDER_DIR_ASC],
|
||||
['orderBy' => 'i.number', 'direction' => static::ORDER_DIR_ASC]
|
||||
],
|
||||
static::ORDERBY_SHELF => [
|
||||
['orderBy' => static::ORDER_CURRENT_ISSUE, 'direction' => static::ORDER_DIR_DESC],
|
||||
['orderBy' => 'i.year', 'direction' => static::ORDER_DIR_ASC],
|
||||
['orderBy' => 'i.volume', 'direction' => static::ORDER_DIR_ASC],
|
||||
['orderBy' => 'i.number', 'direction' => static::ORDER_DIR_ASC]
|
||||
],
|
||||
default => throw new InvalidArgumentException('One of ORDERBY_* constants must be provided')
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set published filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByPublished(bool $isPublished): static
|
||||
{
|
||||
$this->isPublished = $isPublished;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set volumes filter
|
||||
*
|
||||
* @param int[]|null $volumes
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByVolumes(?array $volumes): static
|
||||
{
|
||||
$this->volumes = $volumes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set volumes filter
|
||||
*
|
||||
* @param int[]|null $numbers
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByNumbers(?array $numbers): static
|
||||
{
|
||||
$this->numbers = $numbers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set volumes filter
|
||||
*
|
||||
* @param int[]|null $years
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByYears(?array $years): static
|
||||
{
|
||||
$this->years = $years;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* set urlPath filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByUrlPath(string $urlPath): static
|
||||
{
|
||||
$this->urlPath = $urlPath;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set titles filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByTitles(array $titles): static
|
||||
{
|
||||
$this->titles = $titles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to issues with these statuses
|
||||
*
|
||||
* @param array|null $statuses One or more of DOI::STATUS_* constants
|
||||
*
|
||||
*/
|
||||
public function filterByDoiStatuses(?array $statuses): static
|
||||
{
|
||||
$this->doiStatuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to issues that do/don't have any DOIs assign to their sub objects
|
||||
*
|
||||
* @param array|null $enabledDoiTypes TYPE_* constants to consider when checking issue has DOIs
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function filterByHasDois(?bool $hasDois, ?array $enabledDoiTypes = null): static
|
||||
{
|
||||
$this->hasDois = $hasDois;
|
||||
$this->enabledDoiTypes = $enabledDoiTypes === null ? [Repo::doi()::TYPE_ISSUE] : $enabledDoiTypes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set query search phrase
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function searchPhrase(?string $phrase): static
|
||||
{
|
||||
$this->searchPhrase = $phrase;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of objects retrieved
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function limit(?int $count): static
|
||||
{
|
||||
$this->count = $count;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the number of objects retrieved, for example to
|
||||
* retrieve the second page of contents
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function offset(?int $offset): static
|
||||
{
|
||||
$this->offset = $offset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getQueryBuilder(): Builder
|
||||
{
|
||||
$q = DB::table($this->dao->table, 'i')
|
||||
->select('i.*')
|
||||
->leftJoin('custom_issue_orders as o', 'o.issue_id', '=', 'i.issue_id');
|
||||
|
||||
// Issue titles (exact matches)
|
||||
$q->when(
|
||||
$this->titles !== null,
|
||||
fn (Builder $q) =>
|
||||
$q->whereIn(
|
||||
'i.issue_id',
|
||||
fn (Builder $q) =>
|
||||
$q->select('issue_id')
|
||||
->from($this->dao->settingsTable)
|
||||
->where('setting_name', '=', 'title')
|
||||
->whereIn('setting_value', $this->titles)
|
||||
)
|
||||
);
|
||||
|
||||
// Context
|
||||
// Never permit a query without a context_id unless the PKPApplication::CONTEXT_ID_ALL wildcard
|
||||
// has been set explicitly.
|
||||
if (!isset($this->contextIds)) {
|
||||
throw new Exception('Submissions can not be retrieved without a context id. Pass the Application::CONTEXT_ID_ALL wildcard to get submissions from any context.');
|
||||
} elseif (!in_array(PKPApplication::CONTEXT_ID_ALL, $this->contextIds)) {
|
||||
$q->whereIn('i.journal_id', $this->contextIds);
|
||||
}
|
||||
|
||||
// Issue IDs
|
||||
$q->when($this->issueIds !== null, fn (Builder $q) => $q->whereIn('i.issue_id', $this->issueIds));
|
||||
// Published
|
||||
$q->when($this->isPublished !== null, fn (Builder $q) => $q->where('i.published', '=', $this->isPublished ? 1 : 0));
|
||||
// Volumes
|
||||
$q->when($this->volumes !== null, fn (Builder $q) => $q->whereIn('i.volume', $this->volumes));
|
||||
// Numbers
|
||||
$q->when($this->numbers !== null, fn (Builder $q) => $q->whereIn('i.number', $this->numbers));
|
||||
// Years
|
||||
$q->when($this->years !== null, fn (Builder $q) => $q->whereIn('i.year', $this->years));
|
||||
// URL path
|
||||
$q->when($this->urlPath !== null, fn (Builder $q) => $q->where('i.url_path', '=', $this->urlPath));
|
||||
|
||||
// DOI statuses
|
||||
$q->when(
|
||||
$this->doiStatuses !== null,
|
||||
fn (Builder $q) =>
|
||||
$q->whereIn(
|
||||
'i.issue_id',
|
||||
fn (Builder $q) =>
|
||||
$q->select('i.issue_id')
|
||||
->from('issues as i')
|
||||
->leftJoin('dois as d', 'd.doi_id', '=', 'i.doi_id')
|
||||
->whereIn('d.status', $this->doiStatuses)
|
||||
)
|
||||
);
|
||||
|
||||
// By whether issue has DOI assigned
|
||||
$q->when(
|
||||
$this->hasDois !== null,
|
||||
fn (Builder $q) =>
|
||||
$q->whereIn(
|
||||
'i.issue_id',
|
||||
fn (Builder $q) =>
|
||||
$q->select('current_i.issue_id')
|
||||
->from('issues', 'current_i')
|
||||
->when(
|
||||
in_array(Repo::doi()::TYPE_ISSUE, $this->enabledDoiTypes),
|
||||
fn (Builder $q) =>
|
||||
$this->hasDois ? $q->whereNotNull('current_i.doi_id') : $q->whereNull('current_i.doi_id')
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Search phrase
|
||||
if ($this->searchPhrase !== null) {
|
||||
$searchPhrase = $this->searchPhrase;
|
||||
|
||||
// Add support for searching for the volume, number and year
|
||||
// using the localized issue identification formats. In
|
||||
// en this will match Vol. 1. No. 1 (2018) against:
|
||||
// i.volume = 1 AND i.number = 1 AND i.year = 2018
|
||||
$volume = '';
|
||||
$volumeRegex = '/\b' . preg_quote(__('issue.vol'), '/') . '\s+(\d+)/';
|
||||
if (preg_match($volumeRegex, $searchPhrase, $matches)) {
|
||||
[$found, $volume] = $matches;
|
||||
$searchPhrase = str_replace($found, '', $searchPhrase);
|
||||
}
|
||||
$number = '';
|
||||
$numberRegex = '/\b' . preg_quote(__('issue.no'), '/') . '\s+(\S+)\b/';
|
||||
if (preg_match($numberRegex, $searchPhrase, $matches)) {
|
||||
[$found, $number] = $matches;
|
||||
$searchPhrase = str_replace($found, '', $searchPhrase);
|
||||
}
|
||||
$year = '';
|
||||
if (preg_match('/\((\d{4})\)/', $searchPhrase, $matches)) {
|
||||
[$found, $year] = $matches;
|
||||
$searchPhrase = str_replace($found, '', $searchPhrase);
|
||||
}
|
||||
$q->when(
|
||||
strlen($volume) || $number !== '' || $year !== '',
|
||||
fn (Builder $q) => $q->where(
|
||||
fn (Builder $q) => $q
|
||||
->when($volume !== '', fn (Builder $q) => $q->where('i.volume', '=', $volume))
|
||||
->when($number !== '', fn (Builder $q) => $q->where('i.number', '=', $number))
|
||||
->when($year !== '', fn (Builder $q) => $q->where('i.year', '=', $year))
|
||||
)
|
||||
);
|
||||
|
||||
$words = array_filter(array_unique(explode(' ', $searchPhrase)), 'strlen');
|
||||
if (count($words)) {
|
||||
$likePattern = DB::raw("CONCAT('%', LOWER(?), '%')");
|
||||
foreach ($words as $word) {
|
||||
$q->where(
|
||||
fn (Builder $q) => $q
|
||||
->whereIn(
|
||||
'i.issue_id',
|
||||
fn (Builder $q) =>
|
||||
$q->select('iss_t.issue_id')
|
||||
->from($this->dao->settingsTable, 'iss_t')
|
||||
->where('iss_t.setting_name', '=', 'title')
|
||||
->where(DB::raw('LOWER(iss_t.setting_value)'), 'LIKE', $likePattern)->addBinding($word)
|
||||
)
|
||||
->orWhereIn(
|
||||
'i.issue_id',
|
||||
fn (Builder $q) =>
|
||||
$q->select('iss_d.issue_id')
|
||||
->from($this->dao->settingsTable, 'iss_d')
|
||||
->where('iss_d.setting_name', '=', 'name')
|
||||
->where(DB::raw('LOWER(iss_d.setting_value)'), 'LIKE', $likePattern)->addBinding($word)
|
||||
)
|
||||
// Match any four-digit number to the year
|
||||
->when(ctype_digit($word) && strlen($word) === 4, fn (Builder $q) => $q->orWhere('i.year', '=', $word))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ordering for query-builder-based and legacy-based orderings
|
||||
$q->when($this->resultOrderings !== null, function (Builder $q) {
|
||||
foreach ($this->resultOrderings as $resultOrdering) {
|
||||
if ($resultOrdering['orderBy'] === static::ORDER_CURRENT_ISSUE) {
|
||||
// Custom query to order by current issue status from the journals table
|
||||
$q->leftJoin('journals as j', 'j.current_issue_id', '=', 'i.issue_id')
|
||||
->orderByRaw('CASE WHEN j.current_issue_id IS NOT NULL then 1 else 0 END ' . $resultOrdering['direction']);
|
||||
} else {
|
||||
$q->orderBy($resultOrdering['orderBy'], $resultOrdering['direction']);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Limit and offset results for pagination
|
||||
$q->when($this->count !== null, fn (Builder $q) => $q->limit($this->count));
|
||||
$q->when($this->offset !== null, fn (Builder $q) => $q->offset($this->offset));
|
||||
|
||||
// Add app-specific query statements
|
||||
Hook::call('Issue::getMany::queryObject', [&$q, $this]);
|
||||
|
||||
return $q;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/issue/DAO.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.
|
||||
*
|
||||
* @ingroup issue
|
||||
*
|
||||
* @see Issue
|
||||
*
|
||||
* @brief Operations for retrieving and modifying Issue objects.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\cache\CacheManager;
|
||||
use PKP\cache\GenericCache;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\core\traits\EntityWithParent;
|
||||
use PKP\db\DAOResultFactory;
|
||||
use PKP\services\PKPSchemaService;
|
||||
|
||||
/**
|
||||
* @extends EntityDAO<Issue>
|
||||
*/
|
||||
class DAO extends EntityDAO implements \PKP\plugins\PKPPubIdPluginDAO
|
||||
{
|
||||
/**
|
||||
* @use EntityWithParent<Issue>
|
||||
*/
|
||||
use EntityWithParent;
|
||||
|
||||
// TODO: Needs to be addressed with refactor of caching.
|
||||
public $caches;
|
||||
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = PKPSchemaService::SCHEMA_ISSUE;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'issues';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'issue_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryKeyColumn */
|
||||
public $primaryKeyColumn = 'issue_id';
|
||||
|
||||
/** @copydoc SchemaDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'id' => 'issue_id',
|
||||
'journalId' => 'journal_id',
|
||||
'volume' => 'volume',
|
||||
'number' => 'number',
|
||||
'year' => 'year',
|
||||
'published' => 'published',
|
||||
'datePublished' => 'date_published',
|
||||
'dateNotified' => 'date_notified',
|
||||
'lastModified' => 'last_modified',
|
||||
'accessStatus' => 'access_status',
|
||||
'openAccessDate' => 'open_access_date',
|
||||
'showVolume' => 'show_volume',
|
||||
'showNumber' => 'show_number',
|
||||
'showYear' => 'show_year',
|
||||
'showTitle' => 'show_title',
|
||||
'styleFileName' => 'style_file_name',
|
||||
'originalStyleFileName' => 'original_style_file_name',
|
||||
'urlPath' => 'url_path',
|
||||
'doiId' => 'doi_id'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent object ID column name
|
||||
*/
|
||||
public function getParentColumn(): string
|
||||
{
|
||||
return 'journal_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a cache miss.
|
||||
*
|
||||
* TODO: Caching not currently working as expected
|
||||
*
|
||||
*/
|
||||
public function _cacheMiss(GenericCache $cache, int $id): ?Issue
|
||||
{
|
||||
if ($cache->getCacheId() === 'current') {
|
||||
$issue = Repo::issue()->getCurrent($id);
|
||||
} else {
|
||||
$issue = Repo::issue()->getByBestId($id, null, false);
|
||||
}
|
||||
$cache->setCache($id, $issue);
|
||||
return $issue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an issue cache by cache ID
|
||||
*
|
||||
* TODO: Not currently working as expected. Not used throughout current class
|
||||
*
|
||||
* @return mixed|object|\PKP\cache\APCCache|\PKP\cache\FileCache|GenericCache|\PKP\cache\MemcacheCache|\PKP\cache\XCacheCache
|
||||
*/
|
||||
public function _getCache(string $cacheId)
|
||||
{
|
||||
if (!isset($this->caches)) {
|
||||
$this->caches = [];
|
||||
}
|
||||
if (!isset($this->caches[$cacheId])) {
|
||||
$cacheManager = CacheManager::getManager();
|
||||
$this->caches[$cacheId] = $cacheManager->getObjectCache('issues', $cacheId, [$this, '_cacheMiss']);
|
||||
}
|
||||
return $this->caches[$cacheId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new DataObject
|
||||
*/
|
||||
public function newDataObject(): Issue
|
||||
{
|
||||
return app(Issue::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of announcements matching the configured query
|
||||
*/
|
||||
public function getCount(Collector $query): int
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of ids matching the configured query
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->select('i.' . $this->primaryKeyColumn)
|
||||
->pluck('i.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of issues matching the configured query
|
||||
*
|
||||
* @return LazyCollection<int,Issue>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->issue_id => $this->fromRow($row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issue id by its url path
|
||||
*
|
||||
*/
|
||||
public function getIdByUrlPath(string $urlPath, int $contextId): ?int
|
||||
{
|
||||
$issue = DB::table($this->table, 'i')
|
||||
->where('i.journal_id', '=', $contextId)
|
||||
->where('i.url_path', '=', $urlPath)
|
||||
->first();
|
||||
|
||||
return $issue ? $issue->issue_id : null;
|
||||
}
|
||||
|
||||
/** @copydoc EntityDAO::fromRow() */
|
||||
public function fromRow(object $row): Issue
|
||||
{
|
||||
$issue = parent::fromRow($row);
|
||||
$this->setDoiObject($issue);
|
||||
|
||||
return $issue;
|
||||
}
|
||||
|
||||
/** @copydoc EntityDAO::_insert() */
|
||||
public function insert(Issue $issue): int
|
||||
{
|
||||
$issueId = parent::_insert($issue);
|
||||
$this->resequenceCustomIssueOrders($issue->getData('journalId'));
|
||||
return $issueId;
|
||||
}
|
||||
|
||||
/** @copydoc EntityDAO::_update() */
|
||||
public function update(Issue $issue)
|
||||
{
|
||||
$issue->stampModified();
|
||||
parent::_update($issue);
|
||||
$this->resequenceCustomIssueOrders($issue->getData('journalId'));
|
||||
// TODO: Flush cache
|
||||
}
|
||||
|
||||
/** @copydoc EntityDAO::_delete() */
|
||||
public function delete(Issue $issue)
|
||||
{
|
||||
parent::_delete($issue);
|
||||
$this->resequenceCustomIssueOrders($issue->getData('journalId'));
|
||||
// TODO: Flush cache
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes any custom issue ordering for a given context
|
||||
*
|
||||
*/
|
||||
public function deleteCustomIssueOrdering(int $issueId)
|
||||
{
|
||||
DB::table('custom_issue_orders')
|
||||
->where('issue_id', '=', $issueId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequentially renumber custom issue orderings in their sequence order.
|
||||
*
|
||||
*/
|
||||
public function resequenceCustomIssueOrders(int $contextId)
|
||||
{
|
||||
// If no custom issue ordering already exists, there is nothing to do
|
||||
if (!$this->customIssueOrderingExists($contextId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$results = DB::table($this->table, 'i')
|
||||
->leftJoin('custom_issue_orders as o', 'o.issue_id', '=', 'i.issue_id')
|
||||
->where('i.journal_id', '=', $contextId)
|
||||
// TODO: Previous behaviour would resequence all issues, including those that haven't been published (cont.)
|
||||
// artificially giving them a position in the custom_issue_orders table before they've been published.
|
||||
->where('i.published', '=', 1)
|
||||
->orderBy('o.seq')
|
||||
->select('i.issue_id')
|
||||
->get();
|
||||
$results->each(function ($item, $key) use ($contextId) {
|
||||
$newSeq = $key + 1;
|
||||
DB::table('custom_issue_orders')
|
||||
->updateOrInsert(
|
||||
[
|
||||
'issue_id' => $item->issue_id,
|
||||
],
|
||||
[
|
||||
'issue_id' => $item->issue_id,
|
||||
'journal_id' => (int) $contextId,
|
||||
'seq' => $newSeq
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a journal has custom issue ordering
|
||||
*
|
||||
*/
|
||||
public function customIssueOrderingExists(int $contextId): bool
|
||||
{
|
||||
$resultCount = DB::table('custom_issue_orders', 'o')
|
||||
->where('o.journal_id', '=', $contextId)
|
||||
->count();
|
||||
return $resultCount != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the custom issue order of a journal
|
||||
*
|
||||
*/
|
||||
public function getCustomIssueOrder(int $contextId, int $issueId): ?int
|
||||
{
|
||||
$results = DB::table('custom_issue_orders')
|
||||
->where('journal_id', '=', (int) $contextId)
|
||||
->where('issue_id', '=', (int) $issueId);
|
||||
|
||||
$row = $results->first();
|
||||
return $row ? (int) $row->seq : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all the years in which issues have been published
|
||||
*
|
||||
*/
|
||||
public function getYearsIssuesPublished(int $contextId): Collection
|
||||
{
|
||||
$collector = Repo::issue()->getCollector();
|
||||
$q = $collector->filterByContextIds([$contextId])
|
||||
->filterByPublished(true)
|
||||
->getQueryBuilder();
|
||||
|
||||
return $q->select('i.year')
|
||||
->groupBy('i.year')
|
||||
->orderBy('i.year', 'DESC')
|
||||
->pluck('i.year');
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL USE ONLY: Insert a custom issue ordering
|
||||
* TODO: See if should be protected/private
|
||||
*
|
||||
*/
|
||||
public function insertCustomIssueOrder(int $contextId, int $issueId, int $seq)
|
||||
{
|
||||
DB::table('custom_issue_orders')
|
||||
->insert(
|
||||
[
|
||||
'issue_id' => $issueId,
|
||||
'journal_id' => $contextId,
|
||||
'seq' => $seq
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a custom issue ordering up or down, resequencing as necessary.
|
||||
*
|
||||
* @param int $newPos The new position (0-based) of this section
|
||||
*/
|
||||
public function moveCustomIssueOrder(int $contextId, int $issueId, int $newPos)
|
||||
{
|
||||
DB::table('custom_issue_orders')
|
||||
->updateOrInsert(
|
||||
[
|
||||
'journal_id' => $contextId,
|
||||
'issue_id' => $issueId
|
||||
],
|
||||
[
|
||||
'seq' => $newPos
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::pubIdExists()
|
||||
*
|
||||
* From legacy IssueDAO
|
||||
*/
|
||||
public function pubIdExists($pubIdType, $pubId, $excludePubObjectId, $contextId)
|
||||
{
|
||||
$result = $this->deprecatedDao->retrieve(
|
||||
'SELECT COUNT(*) AS row_count
|
||||
FROM issue_settings ist
|
||||
INNER JOIN issues i ON ist.issue_id = i.issue_id
|
||||
WHERE ist.setting_name = ? AND ist.setting_value = ? AND i.issue_id <> ? AND i.journal_id = ?',
|
||||
[
|
||||
'pub-id::' . $pubIdType,
|
||||
$pubId,
|
||||
(int) $excludePubObjectId,
|
||||
(int) $contextId
|
||||
]
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row && $row->row_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::changePubId()
|
||||
*
|
||||
* From legacy IssueDAO
|
||||
*/
|
||||
public function changePubId($pubObjectId, $pubIdType, $pubId)
|
||||
{
|
||||
DB::table('issue_settings')->updateOrInsert(
|
||||
['issue_id' => (int) $pubObjectId, 'locale' => '', 'setting_name' => 'pub-id::' . $pubIdType],
|
||||
['setting_value' => (string) $pubId]
|
||||
);
|
||||
// TODO: Cache not implemented
|
||||
// $this->flushCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::deletePubId()
|
||||
*
|
||||
* From legacy IssueDAO
|
||||
*/
|
||||
public function deletePubId($pubObjectId, $pubIdType)
|
||||
{
|
||||
$this->deprecatedDao->update(
|
||||
'DELETE FROM issue_settings WHERE setting_name = ? AND issue_id = ?',
|
||||
[
|
||||
'pub-id::' . $pubIdType,
|
||||
(int)$pubObjectId
|
||||
]
|
||||
);
|
||||
// TODO: Cache not implemented
|
||||
// $this->flushCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::deleteAllPubIds()
|
||||
*
|
||||
* From legacy IssueDAO
|
||||
*/
|
||||
public function deleteAllPubIds($contextId, $pubIdType)
|
||||
{
|
||||
$issues = Repo::issue()->getCollector()->filterByContextIds([$contextId])->getMany();
|
||||
foreach ($issues as $issue) {
|
||||
$this->deprecatedDao->update(
|
||||
'DELETE FROM issue_settings WHERE setting_name = ? AND issue_id = ?',
|
||||
[
|
||||
'pub-id::' . $pubIdType,
|
||||
(int)$issue->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
// TODO: Cache not implemented
|
||||
// $this->flushCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all published issues (eventually with a pubId assigned and) matching the specified settings.
|
||||
*
|
||||
* From legacy IssueDAO
|
||||
*
|
||||
* @param int $contextId optional
|
||||
* @param string $pubIdType
|
||||
* @param string $pubIdSettingName optional
|
||||
* (e.g. crossref::registeredDoi)
|
||||
* @param string $pubIdSettingValue optional
|
||||
* @param ?\PKP\db\DBResultRange $rangeInfo optional
|
||||
*
|
||||
* @return DAOResultFactory<Issue>
|
||||
*/
|
||||
public function getExportable($contextId, $pubIdType = null, $pubIdSettingName = null, $pubIdSettingValue = null, $rangeInfo = null)
|
||||
{
|
||||
$params = [];
|
||||
if ($pubIdSettingName) {
|
||||
$params[] = $pubIdSettingName;
|
||||
}
|
||||
$params[] = (int) $contextId;
|
||||
if ($pubIdType) {
|
||||
$params[] = 'pub-id::' . $pubIdType;
|
||||
}
|
||||
|
||||
import('classes.plugins.PubObjectsExportPlugin'); // Constants
|
||||
if ($pubIdSettingName && $pubIdSettingValue && $pubIdSettingValue != EXPORT_STATUS_NOT_DEPOSITED) {
|
||||
$params[] = $pubIdSettingValue;
|
||||
}
|
||||
|
||||
$result = $this->deprecatedDao->retrieveRange(
|
||||
$sql = 'SELECT i.*
|
||||
FROM issues i
|
||||
LEFT JOIN custom_issue_orders o ON (o.issue_id = i.issue_id)
|
||||
' . ($pubIdType != null ? ' LEFT JOIN issue_settings ist ON (i.issue_id = ist.issue_id)' : '')
|
||||
. ($pubIdSettingName != null ? ' LEFT JOIN issue_settings iss ON (i.issue_id = iss.issue_id AND iss.setting_name = ?)' : '') . '
|
||||
WHERE
|
||||
i.published = 1 AND i.journal_id = ?
|
||||
' . ($pubIdType != null ? ' AND ist.setting_name = ? AND ist.setting_value IS NOT NULL' : '')
|
||||
. (($pubIdSettingName != null && $pubIdSettingValue != null && $pubIdSettingValue == EXPORT_STATUS_NOT_DEPOSITED) ? ' AND iss.setting_value IS NULL' : '')
|
||||
. (($pubIdSettingName != null && $pubIdSettingValue != null && $pubIdSettingValue != EXPORT_STATUS_NOT_DEPOSITED) ? ' AND iss.setting_value = ?' : '')
|
||||
. (($pubIdSettingName != null && is_null($pubIdSettingValue)) ? ' AND (iss.setting_value IS NULL OR iss.setting_value = \'\')' : '')
|
||||
. ' ORDER BY i.date_published DESC',
|
||||
$params,
|
||||
$rangeInfo
|
||||
);
|
||||
|
||||
return new DAOResultFactory($result, $this, 'fromRow', [], $sql, $params, $rangeInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the issue cache.
|
||||
*
|
||||
* TODO: Not currently in use. _getCache always results in cache miss.
|
||||
*/
|
||||
public function flushCache()
|
||||
{
|
||||
$this->_getCache('issues')->flush();
|
||||
$this->_getCache('current')->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DOI object
|
||||
*
|
||||
*/
|
||||
protected function setDoiObject(Issue $issue)
|
||||
{
|
||||
if (!empty($issue->getData('doiId'))) {
|
||||
$issue->setData('doiObject', Repo::doi()->get($issue->getData('doiId')));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @defgroup issue Issue
|
||||
* Implement journal issues.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/issue/Issue.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 Issue
|
||||
*
|
||||
* @ingroup issue
|
||||
*
|
||||
* @see \APP\issue\DAO
|
||||
*
|
||||
* @brief Class for Issue.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\file\PublicFileManager;
|
||||
use PKP\core\Core;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\submission\PKPSubmission;
|
||||
|
||||
class Issue extends \PKP\core\DataObject
|
||||
{
|
||||
public const ISSUE_ACCESS_OPEN = 1;
|
||||
public const ISSUE_ACCESS_SUBSCRIPTION = 2;
|
||||
|
||||
/**
|
||||
* get journal id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getJournalId()
|
||||
{
|
||||
return $this->getData('journalId');
|
||||
}
|
||||
|
||||
/**
|
||||
* set journal id
|
||||
*
|
||||
* @param int $journalId
|
||||
*/
|
||||
public function setJournalId($journalId)
|
||||
{
|
||||
return $this->setData('journalId', $journalId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized title
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedTitle()
|
||||
{
|
||||
return $this->getLocalizedData('title');
|
||||
}
|
||||
|
||||
/**
|
||||
* get title
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string|array<string,string>
|
||||
*/
|
||||
public function getTitle($locale)
|
||||
{
|
||||
return $this->getData('title', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* set title
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setTitle($title, $locale)
|
||||
{
|
||||
return $this->setData('title', $title, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* get volume
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getVolume()
|
||||
{
|
||||
return $this->getData('volume');
|
||||
}
|
||||
|
||||
/**
|
||||
* set volume
|
||||
*
|
||||
* @param int $volume
|
||||
*/
|
||||
public function setVolume($volume)
|
||||
{
|
||||
return $this->setData('volume', $volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* get number
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNumber()
|
||||
{
|
||||
return $this->getData('number');
|
||||
}
|
||||
|
||||
/**
|
||||
* set number
|
||||
*
|
||||
* @param string $number
|
||||
*/
|
||||
public function setNumber($number)
|
||||
{
|
||||
return $this->setData('number', $number);
|
||||
}
|
||||
|
||||
/**
|
||||
* get year
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getYear()
|
||||
{
|
||||
return $this->getData('year');
|
||||
}
|
||||
|
||||
/**
|
||||
* set year
|
||||
*
|
||||
* @param int $year
|
||||
*/
|
||||
public function setYear($year)
|
||||
{
|
||||
return $this->setData('year', $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* get published
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPublished()
|
||||
{
|
||||
return $this->getData('published');
|
||||
}
|
||||
|
||||
/**
|
||||
* set published
|
||||
*
|
||||
* @param int $published
|
||||
*/
|
||||
public function setPublished($published)
|
||||
{
|
||||
return $this->setData('published', $published);
|
||||
}
|
||||
|
||||
/**
|
||||
* get date published
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDatePublished()
|
||||
{
|
||||
return $this->getData('datePublished');
|
||||
}
|
||||
|
||||
/**
|
||||
* set date published
|
||||
*
|
||||
* @param string $datePublished
|
||||
*/
|
||||
public function setDatePublished($datePublished)
|
||||
{
|
||||
return $this->setData('datePublished', $datePublished);
|
||||
}
|
||||
|
||||
/**
|
||||
* get date the users were last notified
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDateNotified()
|
||||
{
|
||||
return $this->getData('dateNotified');
|
||||
}
|
||||
|
||||
/**
|
||||
* set date the users were last notified
|
||||
*
|
||||
* @param string $dateNotified
|
||||
*/
|
||||
public function setDateNotified($dateNotified)
|
||||
{
|
||||
return $this->setData('dateNotified', $dateNotified);
|
||||
}
|
||||
|
||||
/**
|
||||
* get date the issue was last modified
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLastModified()
|
||||
{
|
||||
return $this->getData('lastModified');
|
||||
}
|
||||
|
||||
/**
|
||||
* set date the issue was last modified
|
||||
*
|
||||
* @param string $lastModified
|
||||
*/
|
||||
public function setLastModified($lastModified)
|
||||
{
|
||||
return $this->setData('lastModified', $lastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stamp the date of the last modification to the current time.
|
||||
*/
|
||||
public function stampModified()
|
||||
{
|
||||
return $this->setLastModified(Core::getCurrentDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* get access status (ISSUE_ACCESS_...)
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAccessStatus()
|
||||
{
|
||||
return $this->getData('accessStatus');
|
||||
}
|
||||
|
||||
/**
|
||||
* set access status (ISSUE_ACCESS_...)
|
||||
*
|
||||
* @param int $accessStatus
|
||||
*/
|
||||
public function setAccessStatus($accessStatus)
|
||||
{
|
||||
return $this->setData('accessStatus', $accessStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* get open access date
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOpenAccessDate()
|
||||
{
|
||||
return $this->getData('openAccessDate');
|
||||
}
|
||||
|
||||
/**
|
||||
* set open access date
|
||||
*
|
||||
* @param string $openAccessDate
|
||||
*/
|
||||
public function setOpenAccessDate($openAccessDate)
|
||||
{
|
||||
return $this->setData('openAccessDate', $openAccessDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedDescription()
|
||||
{
|
||||
return $this->getLocalizedData('description');
|
||||
}
|
||||
|
||||
/**
|
||||
* get description
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string|array<string,string>
|
||||
*/
|
||||
public function getDescription($locale)
|
||||
{
|
||||
return $this->getData('description', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* set description
|
||||
*
|
||||
* @param string $description
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setDescription($description, $locale)
|
||||
{
|
||||
return $this->setData('description', $description, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current DOI
|
||||
*
|
||||
*/
|
||||
public function getDoi(): ?string
|
||||
{
|
||||
$doiObject = $this->getData('doiObject');
|
||||
|
||||
if (empty($doiObject)) {
|
||||
return null;
|
||||
} else {
|
||||
return $doiObject->getData('doi');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored public ID of the issue.
|
||||
*
|
||||
* This helper function is required by PKPPubIdPlugins.
|
||||
* NB: To maintain backwards compatibility, getDoi() is called from here
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStoredPubId($pubIdType)
|
||||
{
|
||||
if ($pubIdType == 'doi') {
|
||||
return $this->getDoi();
|
||||
} else {
|
||||
return $this->getData('pub-id::' . $pubIdType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stored public issue id.
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
*/
|
||||
public function setStoredPubId($pubIdType, $pubId)
|
||||
{
|
||||
if ($pubIdType == 'doi') {
|
||||
if ($doiObject = $this->getData('doiObject')) {
|
||||
Repo::doi()->edit($doiObject, ['doi' => $pubId]);
|
||||
} else {
|
||||
$newDoiObject = Repo::doi()->newDataObject(
|
||||
[
|
||||
'doi' => $pubId,
|
||||
'contextId' => $this->getJournalId()
|
||||
]
|
||||
);
|
||||
$doiId = Repo::doi()->add($newDoiObject);
|
||||
$this->setData('doiId', $doiId);
|
||||
}
|
||||
} else {
|
||||
$this->setData('pub-id::' . $pubIdType, $pubId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get show issue volume
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShowVolume()
|
||||
{
|
||||
return $this->getData('showVolume');
|
||||
}
|
||||
|
||||
/**
|
||||
* set show issue volume
|
||||
*
|
||||
* @param int $showVolume
|
||||
*/
|
||||
public function setShowVolume($showVolume)
|
||||
{
|
||||
return $this->setData('showVolume', $showVolume);
|
||||
}
|
||||
|
||||
/**
|
||||
* get show issue number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShowNumber()
|
||||
{
|
||||
return $this->getData('showNumber');
|
||||
}
|
||||
|
||||
/**
|
||||
* set show issue number
|
||||
*
|
||||
* @param int $showNumber
|
||||
*/
|
||||
public function setShowNumber($showNumber)
|
||||
{
|
||||
return $this->setData('showNumber', $showNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* get show issue year
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShowYear()
|
||||
{
|
||||
return $this->getData('showYear');
|
||||
}
|
||||
|
||||
/**
|
||||
* set show issue year
|
||||
*
|
||||
* @param int $showYear
|
||||
*/
|
||||
public function setShowYear($showYear)
|
||||
{
|
||||
return $this->setData('showYear', $showYear);
|
||||
}
|
||||
|
||||
/**
|
||||
* get show issue title
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getShowTitle()
|
||||
{
|
||||
return $this->getData('showTitle');
|
||||
}
|
||||
|
||||
/**
|
||||
* set show issue title
|
||||
*
|
||||
* @param int $showTitle
|
||||
*/
|
||||
public function setShowTitle($showTitle)
|
||||
{
|
||||
return $this->setData('showTitle', $showTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized issue cover image file name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedCoverImage()
|
||||
{
|
||||
return $this->getLocalizedData('coverImage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get issue cover image file name
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function getCoverImage($locale)
|
||||
{
|
||||
return $this->getData('coverImage', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set issue cover image file name
|
||||
*
|
||||
* @param string|array $coverImage
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setCoverImage($coverImage, $locale)
|
||||
{
|
||||
return $this->setData('coverImage', $coverImage, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized issue cover image alternate text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedCoverImageAltText()
|
||||
{
|
||||
return $this->getLocalizedData('coverImageAltText');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get issue cover image alternate text
|
||||
*
|
||||
* @param string $locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCoverImageAltText($locale)
|
||||
{
|
||||
return $this->getData('coverImageAltText', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a full URL to the localized cover image
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedCoverImageUrl()
|
||||
{
|
||||
$coverImage = $this->getLocalizedCoverImage();
|
||||
if (!$coverImage) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
|
||||
$publicFileManager = new PublicFileManager();
|
||||
|
||||
return $request->getBaseUrl() . '/' . $publicFileManager->getContextFilesPath($this->getJournalId()) . '/' . $coverImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full URL to all localized cover images
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCoverImageUrls()
|
||||
{
|
||||
$coverImages = $this->getCoverImage(null);
|
||||
if (empty($coverImages)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$request = Application::get()->getRequest();
|
||||
$publicFileManager = new PublicFileManager();
|
||||
|
||||
$urls = [];
|
||||
|
||||
foreach ($coverImages as $locale => $coverImage) {
|
||||
$urls[$locale] = sprintf('%s/%s/%s', $request->getBaseUrl(), $publicFileManager->getContextFilesPath($this->getJournalId()), $coverImage);
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set issue cover image alternate text
|
||||
*
|
||||
* @param string $coverImageAltText
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setCoverImageAltText($coverImageAltText, $locale)
|
||||
{
|
||||
return $this->setData('coverImageAltText', $coverImageAltText, $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string of the issue identification based label format
|
||||
*
|
||||
* @param array $force force show/hide of data components
|
||||
* @param string $locale use specific non-default locale
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIssueIdentification($force = [], $locale = null)
|
||||
{
|
||||
$displayOptions = [
|
||||
'showVolume' => $this->getData('showVolume'),
|
||||
'showNumber' => $this->getData('showNumber'),
|
||||
'showYear' => $this->getData('showYear'),
|
||||
'showTitle' => $this->getData('showTitle'),
|
||||
];
|
||||
|
||||
$displayOptions = array_merge($displayOptions, $force);
|
||||
if (is_null($locale)) {
|
||||
$locale = Locale::getLocale();
|
||||
}
|
||||
|
||||
$volLabel = __('issue.vol', [], $locale);
|
||||
$numLabel = __('issue.no', [], $locale);
|
||||
|
||||
$vol = $this->getData('volume');
|
||||
$num = $this->getData('number');
|
||||
$year = $this->getData('year');
|
||||
$title = $this->getTitle($locale);
|
||||
if (empty($title)) {
|
||||
$title = $this->getLocalizedTitle();
|
||||
}
|
||||
|
||||
$identification = [];
|
||||
foreach ($displayOptions as $opt => $val) {
|
||||
if (empty($val)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($opt == 'showVolume') {
|
||||
$identification[] = "{$volLabel} {$vol}";
|
||||
} elseif ($opt == 'showNumber') {
|
||||
$identification[] = "{$numLabel} {$num}";
|
||||
} elseif ($opt == 'showYear') {
|
||||
$identification[] = !empty($identification) ? "({$year})" : $year;
|
||||
} elseif ($opt == 'showTitle') {
|
||||
if (!empty($title)) {
|
||||
// Append a separator to the last key
|
||||
if (!empty($identification)) {
|
||||
end($identification);
|
||||
$identification[key($identification)] .= ':';
|
||||
}
|
||||
$identification[] = $title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we've got an empty title, re-run the function and force a result
|
||||
if (empty($identification)) {
|
||||
return $this->getIssueIdentification(
|
||||
[
|
||||
'showVolume' => true,
|
||||
'showNumber' => true,
|
||||
'showYear' => true,
|
||||
'showTitle' => false,
|
||||
],
|
||||
$locale
|
||||
);
|
||||
}
|
||||
|
||||
return join(' ', $identification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string of the issue series identification
|
||||
* eg: Vol 1 No 1 (2000)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIssueSeries()
|
||||
{
|
||||
if ($this->getShowVolume() || $this->getShowNumber() || $this->getShowYear()) {
|
||||
return $this->getIssueIdentification(['showTitle' => false]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of articles in this issue.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumArticles()
|
||||
{
|
||||
return Repo::submission()->getCollector()
|
||||
->filterByContextIds([$this->getData('journalId')])
|
||||
->filterByIssueIds([$this->getId()])
|
||||
->filterByStatus([PKPSubmission::STATUS_SCHEDULED, PKPSubmission::STATUS_PUBLISHED])
|
||||
->getCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "best" issue ID -- If a public issue ID is set,
|
||||
* use it; otherwise use the internal issue Id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBestIssueId()
|
||||
{
|
||||
return strlen($urlPath = (string) $this->getData('urlPath')) ? $urlPath : $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a description exists for this issue
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasDescription()
|
||||
{
|
||||
$description = $this->getLocalizedDescription();
|
||||
return !empty($description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether issue had DOI assigned to it
|
||||
*
|
||||
*/
|
||||
public function hasDoi(): bool
|
||||
{
|
||||
return (bool) $this->getData('doiObject');
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc \PKP\core\DataObject::getDAO()
|
||||
*/
|
||||
public function getDAO(): DAO
|
||||
{
|
||||
return Repo::issue()->dao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the object in Import/Export results
|
||||
*
|
||||
* @return string A string that Identifies the object
|
||||
*/
|
||||
public function getUIDisplayString()
|
||||
{
|
||||
return __('plugins.importexport.issue.cli.display', ['issueId' => $this->getId(), 'issueIdentification' => $this->getIssueIdentification()]);
|
||||
}
|
||||
}
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\Issue', '\Issue');
|
||||
foreach ([
|
||||
'ISSUE_ACCESS_OPEN',
|
||||
'ISSUE_ACCESS_SUBSCRIPTION',
|
||||
] as $constantName) {
|
||||
define($constantName, constant('\Issue::' . $constantName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/issue/IssueAction.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 IssueAction
|
||||
*
|
||||
* @ingroup issue
|
||||
*
|
||||
* @see Issue
|
||||
*
|
||||
* @brief IssueAction class.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\subscription\IndividualSubscriptionDAO;
|
||||
use APP\subscription\InstitutionalSubscriptionDAO;
|
||||
use APP\subscription\Subscription;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\security\Role;
|
||||
use PKP\security\RoleDAO;
|
||||
use PKP\submission\PKPSubmission;
|
||||
|
||||
class IssueAction
|
||||
{
|
||||
/**
|
||||
* Actions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if subscription is required for viewing the issue
|
||||
*
|
||||
* @param Issue $issue
|
||||
* @param \APP\journal\Journal $journal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function subscriptionRequired($issue, $journal)
|
||||
{
|
||||
assert($issue instanceof \APP\issue\Issue);
|
||||
assert($journal instanceof \APP\journal\Journal);
|
||||
assert($journal->getId() == $issue->getJournalId());
|
||||
|
||||
// Check subscription state.
|
||||
$result = $journal->getData('publishingMode') == \APP\journal\Journal::PUBLISHING_MODE_SUBSCRIPTION &&
|
||||
$issue->getAccessStatus() != \APP\issue\Issue::ISSUE_ACCESS_OPEN && (
|
||||
is_null($issue->getOpenAccessDate()) ||
|
||||
strtotime($issue->getOpenAccessDate()) > time()
|
||||
);
|
||||
Hook::call('IssueAction::subscriptionRequired', [&$journal, &$issue, &$result]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this user is granted access to pre-publication issue galleys
|
||||
* based on their roles in the journal (i.e. Manager, Editor, etc).
|
||||
*
|
||||
* @param \APP\journal\Journal $journal
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function allowedIssuePrePublicationAccess($journal, $user)
|
||||
{
|
||||
/** @var RoleDAO */
|
||||
$roleDao = DAORegistry::getDAO('RoleDAO');
|
||||
if ($user && $journal) {
|
||||
$journalId = $journal->getId();
|
||||
$userId = $user->getId();
|
||||
$subscriptionAssumedRoles = [
|
||||
Role::ROLE_ID_MANAGER,
|
||||
Role::ROLE_ID_SUB_EDITOR,
|
||||
Role::ROLE_ID_ASSISTANT,
|
||||
Role::ROLE_ID_SUBSCRIPTION_MANAGER
|
||||
];
|
||||
|
||||
$roles = $roleDao->getByUserId($userId, $journalId);
|
||||
foreach ($roles as $role) {
|
||||
if (in_array($role->getRoleId(), $subscriptionAssumedRoles)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user has subscription
|
||||
*
|
||||
* @param \PKP\user\User $user
|
||||
* @param \APP\journal\Journal $journal
|
||||
* @param int $issueId Issue ID (optional)
|
||||
* @param int $articleId Article ID (optional)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function subscribedUser($user, $journal, $issueId = null, $articleId = null)
|
||||
{
|
||||
$subscriptionDao = DAORegistry::getDAO('IndividualSubscriptionDAO'); /** @var IndividualSubscriptionDAO $subscriptionDao */
|
||||
$submission = Repo::submission()->get((int) $articleId);
|
||||
$result = false;
|
||||
if (isset($user) && isset($journal)) {
|
||||
if ($submission && Repo::submission()->canPreview($user, $submission)) {
|
||||
$result = true;
|
||||
} else {
|
||||
$result = $subscriptionDao->isValidIndividualSubscription($user->getId(), $journal->getId());
|
||||
}
|
||||
|
||||
// If no valid subscription, check if there is an expired subscription
|
||||
// that was valid during publication date of any one of the submission's
|
||||
// publications
|
||||
if (!$result && $journal->getData('subscriptionExpiryPartial')) {
|
||||
if (isset($submission) && !empty($submission->getData('publications'))) {
|
||||
foreach ($submission->getData('publications') as $publication) {
|
||||
if ($subscriptionDao->isValidIndividualSubscription($user->getId(), $journal->getId(), Subscription::SUBSCRIPTION_DATE_END, $publication->getData('datePublished'))) {
|
||||
$result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif (isset($issueId)) {
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
if (isset($issue) && $issue->getPublished()) {
|
||||
$result = $subscriptionDao->isValidIndividualSubscription($user->getId(), $journal->getId(), Subscription::SUBSCRIPTION_DATE_END, $issue->getDatePublished());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Hook::call('IssueAction::subscribedUser', [&$user, &$journal, &$issueId, &$articleId, &$result]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if remote client domain or ip is allowed
|
||||
*
|
||||
* @param \APP\core\Request $request
|
||||
* @param \APP\journal\Journal $journal
|
||||
* @param int $issueId Issue ID (optional)
|
||||
* @param int $articleId Article ID (optional)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function subscribedDomain($request, $journal, $issueId = null, $articleId = null)
|
||||
{
|
||||
$subscriptionDao = DAORegistry::getDAO('InstitutionalSubscriptionDAO'); /** @var InstitutionalSubscriptionDAO $subscriptionDao */
|
||||
$result = false;
|
||||
if (isset($journal)) {
|
||||
$result = $subscriptionDao->isValidInstitutionalSubscription($request->getRemoteDomain(), $request->getRemoteAddr(), $journal->getId());
|
||||
|
||||
// If no valid subscription, check if there is an expired subscription
|
||||
// that was valid during publication date of requested content
|
||||
if (!$result && $journal->getData('subscriptionExpiryPartial')) {
|
||||
if (isset($articleId)) {
|
||||
$submission = Repo::submission()->get($articleId);
|
||||
if ($submission->getData('status') === PKPSubmission::STATUS_PUBLISHED) {
|
||||
$result = $subscriptionDao->isValidInstitutionalSubscription($request->getRemoteDomain(), $request->getRemoteAddr(), $journal->getId(), Subscription::SUBSCRIPTION_DATE_END, $submission->getDatePublished());
|
||||
}
|
||||
} elseif (isset($issueId)) {
|
||||
$issue = Repo::issue()->get($issueId);
|
||||
if (isset($issue) && $issue->getPublished()) {
|
||||
$result = $subscriptionDao->isValidInstitutionalSubscription($request->getRemoteDomain(), $request->getRemoteAddr(), $journal->getId(), Subscription::SUBSCRIPTION_DATE_END, $issue->getDatePublished());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Hook::call('IssueAction::subscribedDomain', [&$request, &$journal, &$issueId, &$articleId, &$result]);
|
||||
return (bool) $result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\IssueAction', '\IssueAction');
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/issue/IssueFile.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 IssueFile
|
||||
*
|
||||
* @ingroup issue
|
||||
*
|
||||
* @brief Issue file class.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use PKP\file\PKPFile;
|
||||
|
||||
class IssueFile extends PKPFile
|
||||
{
|
||||
/** @var int File content type IDs */
|
||||
public const ISSUE_FILE_PUBLIC = 1;
|
||||
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
|
||||
/**
|
||||
* Get ID of issue.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getIssueId()
|
||||
{
|
||||
return $this->getData('issueId');
|
||||
}
|
||||
|
||||
/**
|
||||
* set ID of issue.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function setIssueId($issueId)
|
||||
{
|
||||
return $this->setData('issueId', $issueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content type of the file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->getData('contentType');
|
||||
}
|
||||
|
||||
/**
|
||||
* set type of the file.
|
||||
*/
|
||||
public function setContentType($contentType)
|
||||
{
|
||||
return $this->setData('contentType', $contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get modified date of file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDateModified()
|
||||
{
|
||||
return $this->getData('dateModified');
|
||||
}
|
||||
|
||||
/**
|
||||
* set modified date of file.
|
||||
*
|
||||
* @param string $dateModified
|
||||
*/
|
||||
public function setDateModified($dateModified)
|
||||
{
|
||||
return $this->setData('dateModified', $dateModified);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\IssueFile', '\IssueFile');
|
||||
define('ISSUE_FILE_PUBLIC', IssueFile::ISSUE_FILE_PUBLIC);
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/issue/IssueFileDAO.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 IssueFileDAO
|
||||
*
|
||||
* @ingroup issue
|
||||
*
|
||||
* @see IssueFile
|
||||
*
|
||||
* @brief Operations for retrieving and modifying IssueFile objects.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use PKP\db\DAO;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class IssueFileDAO extends DAO
|
||||
{
|
||||
/** @var array MIME types that can be displayed inline in a browser */
|
||||
public $_inlineableTypes = null;
|
||||
|
||||
|
||||
/**
|
||||
* Get inlineable file types.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInlineableTypes()
|
||||
{
|
||||
return $this->_inlineableTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set inlineable file types.
|
||||
*
|
||||
* @param array $inlineableTypes
|
||||
*/
|
||||
public function setInlineableTypes($inlineableTypes)
|
||||
{
|
||||
$this->_inlineableTypes = $inlineableTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an issue file by ID.
|
||||
*
|
||||
* @param int $fileId
|
||||
* @param int $issueId optional
|
||||
*
|
||||
* @return IssueFile
|
||||
*/
|
||||
public function getById($fileId, $issueId = null)
|
||||
{
|
||||
$params = [(int) $fileId];
|
||||
if ($issueId) {
|
||||
$params[] = (int) $issueId;
|
||||
}
|
||||
$result = $this->retrieve(
|
||||
'SELECT f.*
|
||||
FROM issue_files f
|
||||
WHERE f.file_id = ?
|
||||
' . ($issueId ? ' AND f.issue_id = ?' : ''),
|
||||
$params
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row ? $this->_fromRow((array) $row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new IssueFile data object.
|
||||
*
|
||||
* @return IssueFile
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new IssueFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to return an IssueFile object from a row.
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return IssueFile
|
||||
*/
|
||||
public function _fromRow($row)
|
||||
{
|
||||
$issueFile = $this->newDataObject();
|
||||
$issueFile->setId($row['file_id']);
|
||||
$issueFile->setIssueId($row['issue_id']);
|
||||
$issueFile->setServerFileName($row['file_name']);
|
||||
$issueFile->setFileType($row['file_type']);
|
||||
$issueFile->setFileSize($row['file_size']);
|
||||
$issueFile->setContentType($row['content_type']);
|
||||
$issueFile->setOriginalFileName($row['original_file_name']);
|
||||
$issueFile->setDateUploaded($this->datetimeFromDB($row['date_uploaded']));
|
||||
$issueFile->setDateModified($this->datetimeFromDB($row['date_modified']));
|
||||
Hook::call('IssueFileDAO::_returnIssueFileFromRow', [&$issueFile, &$row]);
|
||||
return $issueFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new IssueFile.
|
||||
*
|
||||
* @param IssueFile $issueFile
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function insertObject($issueFile)
|
||||
{
|
||||
$this->update(
|
||||
sprintf(
|
||||
'INSERT INTO issue_files
|
||||
(issue_id,
|
||||
file_name,
|
||||
file_type,
|
||||
file_size,
|
||||
content_type,
|
||||
original_file_name,
|
||||
date_uploaded,
|
||||
date_modified)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, %s, %s)',
|
||||
$this->datetimeToDB($issueFile->getDateUploaded()),
|
||||
$this->datetimeToDB($issueFile->getDateModified())
|
||||
),
|
||||
[
|
||||
(int) $issueFile->getIssueId(),
|
||||
$issueFile->getServerFileName(),
|
||||
$issueFile->getFileType(),
|
||||
$issueFile->getFileSize(),
|
||||
$issueFile->getContentType(),
|
||||
$issueFile->getOriginalFileName()
|
||||
]
|
||||
);
|
||||
|
||||
$issueFile->setId($this->getInsertId());
|
||||
return $issueFile->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing issue file.
|
||||
*/
|
||||
public function updateObject($issueFile)
|
||||
{
|
||||
$this->update(
|
||||
sprintf(
|
||||
'UPDATE issue_files
|
||||
SET
|
||||
issue_id = ?,
|
||||
file_name = ?,
|
||||
file_type = ?,
|
||||
file_size = ?,
|
||||
content_type = ?,
|
||||
original_file_name = ?,
|
||||
date_uploaded = %s,
|
||||
date_modified = %s
|
||||
WHERE file_id = ?',
|
||||
$this->datetimeToDB($issueFile->getDateUploaded()),
|
||||
$this->datetimeToDB($issueFile->getDateModified())
|
||||
),
|
||||
[
|
||||
(int) $issueFile->getIssueId(),
|
||||
$issueFile->getServerFileName(),
|
||||
$issueFile->getFileType(),
|
||||
$issueFile->getFileSize(),
|
||||
$issueFile->getContentType(),
|
||||
$issueFile->getOriginalFileName(),
|
||||
(int) $issueFile->getId()
|
||||
]
|
||||
);
|
||||
|
||||
return $issueFile->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an issue file.
|
||||
*/
|
||||
public function deleteObject($issueFile)
|
||||
{
|
||||
$this->deleteById($issueFile->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an issue file by ID.
|
||||
*/
|
||||
public function deleteById($fileId)
|
||||
{
|
||||
$this->update('DELETE FROM issue_files WHERE file_id = ?', [(int) $fileId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all issue files for an issue.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function deleteByIssueId($issueId)
|
||||
{
|
||||
$this->update('DELETE FROM issue_files WHERE issue_id = ?', [(int) $issueId]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\IssueFileDAO', '\IssueFileDAO');
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* @defgroup issue_galley Issue Galleys
|
||||
* Issue galleys allow for the representation of an entire journal issue with
|
||||
* a single file, typically a PDF.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file classes/issue/IssueGalley.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 IssueGalley
|
||||
*
|
||||
* @ingroup issue_galley
|
||||
*
|
||||
* @see IssueGalleyDAO
|
||||
*
|
||||
* @brief A galley is a final presentation version of the full-text of an issue.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Services;
|
||||
use APP\statistics\StatisticsHelper;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\facades\Locale;
|
||||
|
||||
class IssueGalley extends IssueFile
|
||||
{
|
||||
/** @var IssueFile */
|
||||
public $_issueFile;
|
||||
|
||||
|
||||
/**
|
||||
* Check if galley is a PDF galley.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPdfGalley()
|
||||
{
|
||||
switch ($this->getFileType()) {
|
||||
case 'application/pdf':
|
||||
case 'application/x-pdf':
|
||||
case 'text/pdf':
|
||||
case 'text/x-pdf':
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Get/set methods
|
||||
//
|
||||
/**
|
||||
* Get views count.
|
||||
*
|
||||
* @deprecated 3.4
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getViews()
|
||||
{
|
||||
$filters = [
|
||||
'dateStart' => StatisticsHelper::STATISTICS_EARLIEST_DATE,
|
||||
'dateEnd' => date('Y-m-d', strtotime('yesterday')),
|
||||
'contextIds' => [Application::get()->getRequest()->getContext()->getId()],
|
||||
'issueGalleyIds' => [$this->getId()],
|
||||
];
|
||||
$metrics = Services::get('issueStats')
|
||||
->getQueryBuilder($filters)
|
||||
->getSum([])
|
||||
->value('metric');
|
||||
return $metrics ? $metrics : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the localized value of the galley label.
|
||||
*
|
||||
* @return $string
|
||||
*/
|
||||
public function getGalleyLabel()
|
||||
{
|
||||
$label = $this->getLabel();
|
||||
if ($this->getLocale() != Locale::getLocale()) {
|
||||
$label .= ' (' . Locale::getMetadata($this->getLocale())->getDisplayName() . ')';
|
||||
}
|
||||
return $label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get label/title.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel()
|
||||
{
|
||||
return $this->getData('label');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set label/title.
|
||||
*
|
||||
* @param string $label
|
||||
*/
|
||||
public function setLabel($label)
|
||||
{
|
||||
return $this->setData('label', $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocale()
|
||||
{
|
||||
return $this->getData('locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set locale.
|
||||
*
|
||||
* @param string $locale
|
||||
*/
|
||||
public function setLocale($locale)
|
||||
{
|
||||
return $this->setData('locale', $locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sequence order.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getSequence()
|
||||
{
|
||||
return $this->getData('sequence');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sequence order.
|
||||
*
|
||||
* @param float $sequence
|
||||
*/
|
||||
public function setSequence($sequence)
|
||||
{
|
||||
return $this->setData('sequence', $sequence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file ID.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFileId()
|
||||
{
|
||||
return $this->getData('fileId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file ID.
|
||||
*
|
||||
* @param int $fileId
|
||||
*/
|
||||
public function setFileId($fileId)
|
||||
{
|
||||
return $this->setData('fileId', $fileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored public ID of the galley.
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStoredPubId($pubIdType)
|
||||
{
|
||||
return $this->getData('pub-id::' . $pubIdType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stored public galley id.
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
*/
|
||||
public function setStoredPubId($pubIdType, $pubId)
|
||||
{
|
||||
return $this->setData('pub-id::' . $pubIdType, $pubId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "best" issue galley ID -- If a urlPath is set,
|
||||
* use it; otherwise use the internal article Id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBestGalleyId()
|
||||
{
|
||||
return strlen($urlPath = (string) $this->getData('urlPath')) ? $urlPath : $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file corresponding to this galley.
|
||||
*
|
||||
* @return IssueFile
|
||||
*/
|
||||
public function getFile()
|
||||
{
|
||||
if (!isset($this->_issueFile)) {
|
||||
$issueFileDao = DAORegistry::getDAO('IssueFileDAO'); /** @var IssueFileDAO $issueFileDao */
|
||||
$this->_issueFile = $issueFileDao->getById($this->getFileId());
|
||||
}
|
||||
return $this->_issueFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\IssueGalley', '\IssueGalley');
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/issue/IssueGalleyDAO.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 IssueGalleyDAO
|
||||
*
|
||||
* @ingroup issue_galley
|
||||
*
|
||||
* @see IssueGalley
|
||||
*
|
||||
* @brief Operations for retrieving and modifying IssueGalley objects.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\file\IssueFileManager;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
class IssueGalleyDAO extends \PKP\db\DAO
|
||||
{
|
||||
/**
|
||||
* Retrieve a galley by ID.
|
||||
*
|
||||
* @param int $galleyId
|
||||
* @param int $issueId optional
|
||||
*
|
||||
* @return IssueGalley
|
||||
*/
|
||||
public function getById($galleyId, $issueId = null)
|
||||
{
|
||||
$params = [(int) $galleyId];
|
||||
if ($issueId !== null) {
|
||||
$params[] = (int) $issueId;
|
||||
}
|
||||
$result = $this->retrieve(
|
||||
'SELECT
|
||||
g.*,
|
||||
f.file_name,
|
||||
f.original_file_name,
|
||||
f.file_type,
|
||||
f.file_size,
|
||||
f.content_type,
|
||||
f.date_uploaded,
|
||||
f.date_modified
|
||||
FROM issue_galleys g
|
||||
LEFT JOIN issue_files f ON (g.file_id = f.file_id)
|
||||
WHERE g.galley_id = ?' .
|
||||
($issueId !== null ? ' AND g.issue_id = ?' : ''),
|
||||
$params
|
||||
);
|
||||
|
||||
$returner = null;
|
||||
if ($row = $result->current()) {
|
||||
$returner = $this->_fromRow((array) $row);
|
||||
} else {
|
||||
Hook::call('IssueGalleyDAO::getById', [&$galleyId, &$issueId, &$returner]);
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if public identifier exists (other than for the specified
|
||||
* galley ID, which is treated as an exception).
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
* @param int $galleyId An ID to be excluded from the search.
|
||||
* @param int $journalId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function pubIdExists($pubIdType, $pubId, $galleyId, $journalId)
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT COUNT(*) AS row_count
|
||||
FROM issue_galley_settings igs
|
||||
INNER JOIN issue_galleys ig ON igs.galley_id = ig.galley_id
|
||||
INNER JOIN issues i ON ig.issue_id = i.issue_id
|
||||
WHERE igs.setting_name = ? AND igs.setting_value = ? AND igs.galley_id <> ? AND i.journal_id = ?',
|
||||
[
|
||||
'pub-id::' . $pubIdType,
|
||||
$pubId,
|
||||
(int) $galleyId,
|
||||
(int) $journalId
|
||||
]
|
||||
);
|
||||
$row = $result->current();
|
||||
return $row ? (bool) $row->row_count : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a galley by ID.
|
||||
*
|
||||
* @param string $pubIdType One of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
* @param string $pubId
|
||||
* @param int $issueId
|
||||
*
|
||||
* @return IssueGalley
|
||||
*/
|
||||
public function getByPubId($pubIdType, $pubId, $issueId)
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT
|
||||
g.*,
|
||||
f.file_name,
|
||||
f.original_file_name,
|
||||
f.file_type,
|
||||
f.file_size,
|
||||
f.content_type,
|
||||
f.date_uploaded,
|
||||
f.date_modified
|
||||
FROM issue_galleys g
|
||||
INNER JOIN issue_galley_settings gs ON g.galley_id = gs.galley_id
|
||||
LEFT JOIN issue_files f ON (g.file_id = f.file_id)
|
||||
WHERE gs.setting_name = ? AND
|
||||
gs.setting_value = ? AND
|
||||
g.issue_id = ?',
|
||||
['pub-id::' . $pubIdType, (string) $pubId, (int) $issueId]
|
||||
);
|
||||
$row = $result->current();
|
||||
$returner = null;
|
||||
if ($row) {
|
||||
$returner = $this->_fromRow((array) $row);
|
||||
} else {
|
||||
Hook::call('IssueGalleyDAO::getByPubId', [&$pubIdType, &$pubId, &$issueId, &$returner]);
|
||||
}
|
||||
return $returner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all galleys for an issue.
|
||||
*
|
||||
* @param int $issueId
|
||||
*
|
||||
* @return array<int,IssueGalley> IssueGalleys
|
||||
*/
|
||||
public function getByIssueId($issueId)
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT
|
||||
g.*,
|
||||
f.file_name,
|
||||
f.original_file_name,
|
||||
f.file_type,
|
||||
f.file_size,
|
||||
f.content_type,
|
||||
f.date_uploaded,
|
||||
f.date_modified
|
||||
FROM issue_galleys g
|
||||
LEFT JOIN issue_files f ON (g.file_id = f.file_id)
|
||||
WHERE g.issue_id = ? ORDER BY g.seq',
|
||||
[(int) $issueId]
|
||||
);
|
||||
|
||||
$galleys = [];
|
||||
foreach ($result as $row) {
|
||||
$issueGalley = $this->_fromRow((array) $row);
|
||||
$galleys[$issueGalley->getId()] = $issueGalley;
|
||||
}
|
||||
Hook::call('IssueGalleyDAO::getGalleysByIssue', [&$galleys, &$issueId]);
|
||||
return $galleys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve issue galley by urlPath or, failing that,
|
||||
* internal galley ID; urlPath takes precedence.
|
||||
*
|
||||
* @param string $galleyId
|
||||
* @param int $issueId
|
||||
*
|
||||
* @return IssueGalley object
|
||||
*/
|
||||
public function getByBestId($galleyId, $issueId)
|
||||
{
|
||||
$result = $this->retrieve(
|
||||
'SELECT
|
||||
g.*,
|
||||
f.file_name,
|
||||
f.original_file_name,
|
||||
f.file_type,
|
||||
f.file_size,
|
||||
f.content_type,
|
||||
f.date_uploaded,
|
||||
f.date_modified
|
||||
FROM issue_galleys g
|
||||
LEFT JOIN issue_files f ON (g.file_id = f.file_id)
|
||||
WHERE g.url_path = ? AND
|
||||
g.issue_id = ?',
|
||||
[
|
||||
$galleyId,
|
||||
(int) $issueId,
|
||||
]
|
||||
);
|
||||
if ($row = $result->current()) {
|
||||
return $this->_fromRow((array) $row);
|
||||
}
|
||||
return $this->getById($galleyId, $issueId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of fields for which data is localized.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocaleFieldNames()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of additional fields that do not have
|
||||
* dedicated accessors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAdditionalFieldNames()
|
||||
{
|
||||
$additionalFields = parent::getAdditionalFieldNames();
|
||||
// FIXME: Move this to a PID plug-in.
|
||||
$additionalFields[] = 'pub-id::publisher-id';
|
||||
return $additionalFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the localized fields for this galley.
|
||||
*
|
||||
* @param IssueGalley $galley
|
||||
*/
|
||||
public function updateLocaleFields($galley)
|
||||
{
|
||||
$this->updateDataObjectSettings('issue_galley_settings', $galley, [
|
||||
'galley_id' => $galley->getId()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new issue galley.
|
||||
*
|
||||
* @return IssueGalley
|
||||
*/
|
||||
public function newDataObject()
|
||||
{
|
||||
return new IssueGalley();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to return an IssueGalley object from a row.
|
||||
*
|
||||
* @param array $row
|
||||
*
|
||||
* @return IssueGalley
|
||||
*/
|
||||
public function _fromRow($row)
|
||||
{
|
||||
$galley = $this->newDataObject();
|
||||
|
||||
$galley->setId($row['galley_id']);
|
||||
$galley->setIssueId($row['issue_id']);
|
||||
$galley->setLocale($row['locale']);
|
||||
$galley->setFileId($row['file_id']);
|
||||
$galley->setLabel($row['label']);
|
||||
$galley->setSequence($row['seq']);
|
||||
$galley->setData('urlPath', $row['url_path']);
|
||||
|
||||
// IssueFile set methods
|
||||
$galley->setServerFileName($row['file_name']);
|
||||
$galley->setOriginalFileName($row['original_file_name']);
|
||||
$galley->setFileType($row['file_type']);
|
||||
$galley->setFileSize($row['file_size']);
|
||||
$galley->setContentType($row['content_type']);
|
||||
$galley->setDateModified($this->datetimeFromDB($row['date_modified']));
|
||||
$galley->setDateUploaded($this->datetimeFromDB($row['date_uploaded']));
|
||||
|
||||
$this->getDataObjectSettings('issue_galley_settings', 'galley_id', $row['galley_id'], $galley);
|
||||
|
||||
Hook::call('IssueGalleyDAO::_fromRow', [&$galley, &$row]);
|
||||
|
||||
return $galley;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new IssueGalley.
|
||||
*
|
||||
* @param IssueGalley $galley
|
||||
*/
|
||||
public function insertObject($galley)
|
||||
{
|
||||
$this->update(
|
||||
'INSERT INTO issue_galleys
|
||||
(issue_id,
|
||||
file_id,
|
||||
label,
|
||||
locale,
|
||||
seq,
|
||||
url_path)
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?)',
|
||||
[
|
||||
(int) $galley->getIssueId(),
|
||||
(int) $galley->getFileId(),
|
||||
$galley->getLabel(),
|
||||
$galley->getLocale(),
|
||||
$galley->getSequence() == null ? $this->getNextGalleySequence($galley->getIssueId()) : $galley->getSequence(),
|
||||
$galley->getData('urlPath'),
|
||||
]
|
||||
);
|
||||
$galley->setId($this->getInsertId());
|
||||
$this->updateLocaleFields($galley);
|
||||
|
||||
Hook::call('IssueGalleyDAO::insertObject', [&$galley, $galley->getId()]);
|
||||
|
||||
return $galley->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing IssueGalley.
|
||||
*
|
||||
* @param IssueGalley $galley
|
||||
*/
|
||||
public function updateObject($galley)
|
||||
{
|
||||
$this->update(
|
||||
'UPDATE issue_galleys
|
||||
SET
|
||||
file_id = ?,
|
||||
label = ?,
|
||||
locale = ?,
|
||||
seq = ?,
|
||||
url_path = ?
|
||||
WHERE galley_id = ?',
|
||||
[
|
||||
(int) $galley->getFileId(),
|
||||
$galley->getLabel(),
|
||||
$galley->getLocale(),
|
||||
$galley->getSequence(),
|
||||
$galley->getData('urlPath'),
|
||||
(int) $galley->getId()
|
||||
]
|
||||
);
|
||||
$this->updateLocaleFields($galley);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an IssueGalley.
|
||||
*
|
||||
* @param IssueGalley $galley
|
||||
*/
|
||||
public function deleteObject($galley)
|
||||
{
|
||||
return $this->deleteById($galley->getId(), $galley->getIssueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a galley by ID.
|
||||
*
|
||||
* @param int $galleyId
|
||||
* @param int $issueId optional
|
||||
*/
|
||||
public function deleteById($galleyId, $issueId = null)
|
||||
{
|
||||
Hook::call('IssueGalleyDAO::deleteById', [&$galleyId, &$issueId]);
|
||||
|
||||
if (isset($issueId)) {
|
||||
// Delete the file
|
||||
$issueGalley = $this->getById($galleyId);
|
||||
$issueFileManager = new IssueFileManager($issueId);
|
||||
$issueFileManager->deleteById($issueGalley->getFileId());
|
||||
|
||||
$affectedRows = $this->update(
|
||||
'DELETE FROM issue_galleys WHERE galley_id = ? AND issue_id = ?',
|
||||
[(int) $galleyId, (int) $issueId]
|
||||
);
|
||||
} else {
|
||||
$affectedRows = $this->update(
|
||||
'DELETE FROM issue_galleys WHERE galley_id = ?',
|
||||
[(int) $galleyId]
|
||||
);
|
||||
}
|
||||
if ($affectedRows) {
|
||||
$this->update('DELETE FROM issue_galley_settings WHERE galley_id = ?', [(int) $galleyId]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete galleys by issue.
|
||||
* NOTE that this will not delete issue_file entities or the respective files.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function deleteByIssueId($issueId)
|
||||
{
|
||||
$galleys = $this->getByIssueId($issueId);
|
||||
foreach ($galleys as $galley) {
|
||||
$this->deleteById($galley->getId(), $issueId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sequentially renumber galleys for an issue in their sequence order.
|
||||
*
|
||||
* @param int $issueId
|
||||
*/
|
||||
public function resequence($issueId)
|
||||
{
|
||||
$result = $this->retrieve('SELECT galley_id FROM issue_galleys WHERE issue_id = ? ORDER BY seq', [(int) $issueId]);
|
||||
for ($i = 1; $row = $result->current(); $i++) {
|
||||
$this->update('UPDATE issue_galleys SET seq = ? WHERE galley_id = ?', [$i, $row->galley_id]);
|
||||
$result->next();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the the next sequence number for an issue's galleys (i.e., current max + 1).
|
||||
*
|
||||
* @param int $issueId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNextGalleySequence($issueId)
|
||||
{
|
||||
$result = $this->retrieve('SELECT COALESCE(MAX(seq), 0) + 1 AS next_sequence FROM issue_galleys WHERE issue_id = ?', [(int) $issueId]);
|
||||
$row = $result->current();
|
||||
return $row->next_sequence;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\issue\IssueGalleyDAO', '\IssueGalleyDAO');
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/issue/Repository.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 Repository
|
||||
*
|
||||
* @brief A repository to find and manage issues.
|
||||
*/
|
||||
|
||||
namespace APP\issue;
|
||||
|
||||
use APP\core\Request;
|
||||
use APP\facades\Repo;
|
||||
use APP\file\IssueFileManager;
|
||||
use APP\file\PublicFileManager;
|
||||
use APP\journal\JournalDAO;
|
||||
use Illuminate\Support\Collection;
|
||||
use PKP\context\Context;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\doi\exceptions\DoiException;
|
||||
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 $schemaService */
|
||||
protected $schemaService;
|
||||
|
||||
// TODO: Explicitly excluding caching for now as it wasn't actually setting data to cache
|
||||
|
||||
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 = []): Issue
|
||||
{
|
||||
$object = $this->dao->newDataObject();
|
||||
if (!empty($params)) {
|
||||
$object->setAllData($params);
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::exists() */
|
||||
public function exists(int $id, ?int $journalId = null): bool
|
||||
{
|
||||
return $this->dao->exists($id, $journalId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::get()
|
||||
* TODO: Function signature should stick with ID, but previous DAO expected $useCache = false as default
|
||||
*/
|
||||
public function get(int $id, ?int $journalId = null): ?Issue
|
||||
{
|
||||
// TODO: Caching as currently setup never properly caches objects and always fires a _cacheMiss()
|
||||
// if ($useCache) {
|
||||
// $cache = $this->dao->_getCache('issues');
|
||||
// $returner = $cache->get($id);
|
||||
// if ($returner && $contextId != null && $contextId != $returner->getJournalId()) {
|
||||
// $returner = null;
|
||||
// }
|
||||
// return $returner;
|
||||
// }
|
||||
|
||||
return $this->dao->get($id, $journalId);
|
||||
}
|
||||
|
||||
/** @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 issue
|
||||
*
|
||||
* Perform validation checks on data used to add or edit an issue.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array A key/value array with validation errors. Empty if no errors
|
||||
*/
|
||||
public function validate(?Issue $object, array $props, array $allowedLocales, string $primaryLocale): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
$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);
|
||||
|
||||
if ($validator->fails()) {
|
||||
$errors = $this->schemaService->formatValidationErrors($validator->errors());
|
||||
}
|
||||
|
||||
Hook::call('Issue::validate', [&$errors, $object, $props, $allowedLocales, $primaryLocale]);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::insert() */
|
||||
public function add(Issue $issue): int
|
||||
{
|
||||
return $this->dao->insert($issue);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::update() */
|
||||
public function edit(Issue $issue, array $params)
|
||||
{
|
||||
$newIssue = $this->newDataObject(array_merge($issue->_data, $params));
|
||||
|
||||
Hook::call('Issue::edit', [&$newIssue, $issue, $params]);
|
||||
|
||||
$this->dao->update($newIssue);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::delete() */
|
||||
public function delete(Issue $issue)
|
||||
{
|
||||
$publicFileManager = new PublicFileManager();
|
||||
|
||||
if (is_array($issue->getCoverImage(null))) {
|
||||
foreach ($issue->getCoverImage(null) as $coverImage) {
|
||||
if ($coverImage != '') {
|
||||
$publicFileManager->removeContextFile($issue->getJournalId(), $coverImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$issueId = $issue->getId();
|
||||
|
||||
// Delete issue-specific ordering if it exists.
|
||||
Repo::section()->deleteCustomSectionOrdering($issueId);
|
||||
|
||||
// Delete published issue galleys and issue files
|
||||
$issueGalleyDao = DAORegistry::getDAO('IssueGalleyDAO'); /** @var IssueGalleyDAO $issueGalleyDao */
|
||||
$issueGalleyDao->deleteByIssueId($issueId);
|
||||
|
||||
$issueFileDao = DAORegistry::getDAO('IssueFileDAO'); /** @var IssueFileDAO $issueFileDao */
|
||||
$issueFileDao->deleteByIssueId($issueId);
|
||||
|
||||
$issueFileManager = new IssueFileManager($issueId);
|
||||
$issueFileManager->deleteIssueTree();
|
||||
|
||||
$this->dao->deleteCustomIssueOrdering($issueId);
|
||||
|
||||
$this->dao->delete($issue);
|
||||
}
|
||||
|
||||
public function deleteMany(Collector $collector)
|
||||
{
|
||||
foreach ($collector->getIds() as $issueId) {
|
||||
$this->dao->deleteById($issueId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve current issue
|
||||
*
|
||||
* @param bool $useCache TODO: Not currently implemented. Adding to preserved desired cache usage in future
|
||||
*/
|
||||
public function getCurrent(int $contextId, bool $useCache = false): ?Issue
|
||||
{
|
||||
// TODO: Caching as currently setup never properly caches objects and always fires a _cacheMiss()
|
||||
// if ($useCache) {
|
||||
// $cache = $this->dao->_getCache('current');
|
||||
// return $cache->get($contextId);
|
||||
// }
|
||||
|
||||
/** @var JournalDAO $journalDao */
|
||||
$journalDao = DAORegistry::getDAO('JournalDAO');
|
||||
|
||||
$journal = $journalDao->getById($contextId);
|
||||
$issueId = $journal->getData('currentIssueId');
|
||||
|
||||
return $issueId != null ? $this->get($issueId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current issue for the journal.
|
||||
*
|
||||
*/
|
||||
public function updateCurrent(int $contextId, ?Issue $newCurrentIssue = null)
|
||||
{
|
||||
/** @var JournalDAO $journalDao */
|
||||
$journalDao = DAORegistry::getDAO('JournalDAO');
|
||||
$journal = $journalDao->getById($contextId);
|
||||
|
||||
if ($newCurrentIssue) {
|
||||
$journal->setData('currentIssueId', $newCurrentIssue->getId());
|
||||
$this->edit($newCurrentIssue, []);
|
||||
$journalDao->updateObject($journal);
|
||||
} else {
|
||||
$journalDao->removeCurrentIssue($journal->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get issue by a submission id
|
||||
*
|
||||
*/
|
||||
public function getBySubmissionId(int $submissionId): ?Issue
|
||||
{
|
||||
$issueId = Repo::submission()
|
||||
->get($submissionId)
|
||||
?->getCurrentPublication()
|
||||
?->getData('issueId');
|
||||
return $issueId ? $this->get($issueId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Issue by "best" issue id -- url path if it exists,
|
||||
* falling back on the internal issue ID otherwise.
|
||||
*
|
||||
* @param bool $useCache TODO: Carryover from IssueDAO—was not in use
|
||||
*/
|
||||
public function getByBestId(string $idOrUrlPath, ?int $contextId = null, bool $useCache = false): ?Issue
|
||||
{
|
||||
// Get the issue that matches the requested urlPath (if $idOrUrlPath is one)
|
||||
return ctype_digit((string) $idOrUrlPath)
|
||||
? $this->get((int) $idOrUrlPath, $contextId)
|
||||
: $this->getByUrlPath($idOrUrlPath, $contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an issue by its urlPath
|
||||
*
|
||||
*/
|
||||
public function getByUrlPath(string $urlPath, int $contextId): ?Issue
|
||||
{
|
||||
$issueId = $this->dao->getIdByUrlPath($urlPath, $contextId);
|
||||
return $issueId ? $this->get($issueId) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all the years in which issues have been published
|
||||
*
|
||||
*/
|
||||
public function getYearsIssuesPublished(int $contextId): Collection
|
||||
{
|
||||
return $this->dao->getYearsIssuesPublished($contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all Issues for a given context
|
||||
*
|
||||
*/
|
||||
public function deleteByContextId(int $contextId)
|
||||
{
|
||||
$collector = $this->getCollector()->filterByContextIds([$contextId]);
|
||||
$this->deleteMany($collector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DOI for the given issue
|
||||
*
|
||||
* @return DoiException[]
|
||||
*/
|
||||
public function createDoi(Issue $issue): array
|
||||
{
|
||||
/** @var JournalDAO $contextDao */
|
||||
$contextDao = DAORegistry::getDAO('JournalDAO');
|
||||
$context = $contextDao->getById($issue->getData('journalId'));
|
||||
|
||||
$doiCreationFailures = [];
|
||||
|
||||
if ($context->isDoiTypeEnabled(Repo::doi()::TYPE_ISSUE) && empty($issue->getData('doiId'))) {
|
||||
try {
|
||||
$doiId = Repo::doi()->mintIssueDoi($issue, $context);
|
||||
$issue->setData('doiId', $doiId);
|
||||
$this->dao->update($issue);
|
||||
} catch (DoiException $exception) {
|
||||
$doiCreationFailures[] = $exception;
|
||||
}
|
||||
}
|
||||
|
||||
return $doiCreationFailures;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/issue/maps/Schema.php
|
||||
*
|
||||
* Copyright (c) 2014-2023 Simon Fraser University
|
||||
* Copyright (c) 2003-2023 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Schema
|
||||
*
|
||||
* @brief Map sections to the properties defined in the issue schema
|
||||
*/
|
||||
|
||||
namespace APP\issue\maps;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Issue;
|
||||
use APP\issue\IssueGalleyDAO;
|
||||
use APP\journal\Journal;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\submission\Genre;
|
||||
use PKP\userGroup\UserGroup;
|
||||
|
||||
class Schema extends \PKP\core\maps\Schema
|
||||
{
|
||||
/** @copydoc \PKP\core\maps\Schema::$collection */
|
||||
public Enumerable $collection;
|
||||
|
||||
/** @copydoc \PKP\core\maps\Schema::$schema */
|
||||
public string $schema = PKPSchemaService::SCHEMA_ISSUE;
|
||||
|
||||
/** @var LazyCollection<int,UserGroup> The user groups for this context. */
|
||||
public LazyCollection $userGroups;
|
||||
|
||||
/** @var Genre[] The genres for this context. */
|
||||
public array $genres;
|
||||
|
||||
/**
|
||||
* Map an Issue
|
||||
*
|
||||
* Includes all properties in the Issue schema
|
||||
*
|
||||
* @param LazyCollection<int,UserGroup> $userGroups The user groups of this content
|
||||
* @param Genre[] $genres The genres of this context
|
||||
*/
|
||||
public function map(Issue $item, Journal $context, LazyCollection $userGroups, array $genres): array
|
||||
{
|
||||
$this->userGroups = $userGroups;
|
||||
$this->genres = $genres;
|
||||
return $this->mapByProperties($this->getProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize an Issue
|
||||
*
|
||||
* Includes properties with the apiSummary flag in the Issue schema.
|
||||
*
|
||||
*/
|
||||
public function summarize(Issue $item, Journal $context): array
|
||||
{
|
||||
$this->context = $context;
|
||||
return $this->mapByProperties($this->getSummaryProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a collection of Issues
|
||||
*
|
||||
* @see self::map
|
||||
*
|
||||
* @param LazyCollection<int,UserGroup> $userGroups The user groups of this content
|
||||
* @param Genre[] $genres The genres of this context
|
||||
*/
|
||||
public function mapMany(Enumerable $collection, Journal $context, LazyCollection $userGroups, array $genres): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) use ($context, $userGroups, $genres) {
|
||||
return $this->map($item, $context, $userGroups, $genres);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a collection of Issues
|
||||
*
|
||||
* @see self::summarize
|
||||
*/
|
||||
public function summarizeMany(Enumerable $collection, Journal $context): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) use ($context) {
|
||||
return $this->summarize($item, $context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map schema properties of an Issue to an assoc array
|
||||
*
|
||||
*/
|
||||
private function mapByProperties(array $props, Issue $issue): array
|
||||
{
|
||||
$output = [];
|
||||
|
||||
foreach ($props as $prop) {
|
||||
switch ($prop) {
|
||||
case '_href':
|
||||
$output[$prop] = $this->getApiUrl('issues/' . $issue->getId(), $this->context->getData('urlPath'));
|
||||
break;
|
||||
case 'articles':
|
||||
$data = [];
|
||||
|
||||
$submissions = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$issue->getJournalId()])
|
||||
->filterByIssueIds([$issue->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$data[] = Repo::submission()->getSchemaMap()->summarize($submission, $this->userGroups, $this->genres);
|
||||
}
|
||||
|
||||
$output[$prop] = $data;
|
||||
break;
|
||||
case 'coverImageUrl':
|
||||
$output[$prop] = $issue->getCoverImageUrls();
|
||||
break;
|
||||
case 'doiObject':
|
||||
if ($issue->getData('doiObject')) {
|
||||
$retVal = Repo::doi()->getSchemaMap()->summarize($issue->getData('doiObject'));
|
||||
} else {
|
||||
$retVal = null;
|
||||
}
|
||||
|
||||
$output[$prop] = $retVal;
|
||||
break;
|
||||
case 'galleys':
|
||||
$data = [];
|
||||
/** @var IssueGalleyDAO $issueGalleyDao */
|
||||
$issueGalleyDao = DAORegistry::getDAO('IssueGalleyDAO');
|
||||
$galleys = $issueGalleyDao->getByIssueId($issue->getId());
|
||||
if (!empty($galleys)) {
|
||||
$request = Application::get()->getRequest();
|
||||
foreach ($galleys as $galley) {
|
||||
$data[] = [
|
||||
'fileId' => $galley->getData('fileId'),
|
||||
'label' => $galley->getData('label'),
|
||||
'locale' => $galley->getData('locale'),
|
||||
'pub-id::publisher-id' => $galley->getData('pub-id::publisher-id'),
|
||||
'sequence' => $galley->getData('sequence'),
|
||||
'urlPublished' => $request->getDispatcher()->url(
|
||||
$request,
|
||||
Application::ROUTE_PAGE,
|
||||
$this->context->getPath(),
|
||||
'issue',
|
||||
'view',
|
||||
[
|
||||
$galley->getIssueId(),
|
||||
$galley->getId()
|
||||
]
|
||||
),
|
||||
'urlRemote' => $galley->getData('urlRemote'),
|
||||
];
|
||||
}
|
||||
}
|
||||
$output[$prop] = $data;
|
||||
break;
|
||||
case 'publishedUrl':
|
||||
$output['publishedUrl'] = $this->request->getDispatcher()->url(
|
||||
$this->request,
|
||||
Application::ROUTE_PAGE,
|
||||
$this->context->getPath(),
|
||||
'issue',
|
||||
'view',
|
||||
$issue->getId()
|
||||
);
|
||||
break;
|
||||
case 'sections':
|
||||
$data = [];
|
||||
$sections = Repo::section()->getByIssueId($issue->getId());
|
||||
if (!empty($sections)) {
|
||||
$seq = 1;
|
||||
foreach ($sections as $section) {
|
||||
$sectionProperties = Repo::section()->getSchemaMap()->summarize($section);
|
||||
$sectionProperties['seq'] = $seq;
|
||||
$seq++;
|
||||
$data[] = $sectionProperties;
|
||||
}
|
||||
}
|
||||
$output[$prop] = $data;
|
||||
break;
|
||||
case 'identification':
|
||||
$output[$prop] = $issue->getIssueIdentification();
|
||||
break;
|
||||
|
||||
default:
|
||||
$output[$prop] = $issue->getData($prop);
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map an issue with only the issue identification for the stats list
|
||||
*/
|
||||
public function mapToStats(Issue $issue): array
|
||||
{
|
||||
$props = $this->mapByProperties([
|
||||
'_href',
|
||||
'id',
|
||||
'identification',
|
||||
'publishedUrl'
|
||||
], $issue);
|
||||
return $props;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user