first commit
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/submissionFile/Collector.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2000-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Collector
|
||||
*
|
||||
* @brief A helper class to configure a Query Builder to get a collection of submission files
|
||||
*/
|
||||
|
||||
namespace PKP\submissionFile;
|
||||
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\interfaces\CollectorInterface;
|
||||
use PKP\plugins\Hook;
|
||||
|
||||
/**
|
||||
* @template T of SubmissionFile
|
||||
*/
|
||||
class Collector implements CollectorInterface
|
||||
{
|
||||
/** @var DAO */
|
||||
public $dao;
|
||||
|
||||
/** @var null|array get submission files for one or more file stages */
|
||||
protected $fileStages = null;
|
||||
|
||||
/** @var null|array get submission files for one or more genres */
|
||||
protected $genreIds = null;
|
||||
|
||||
/** @var null|array get submission files for one or more review rounds */
|
||||
protected $reviewRoundIds = null;
|
||||
|
||||
/** @var null|array get submission files for one or more review assignments */
|
||||
protected $reviewIds = null;
|
||||
|
||||
/** @var null|array get submission files for one or more submissions */
|
||||
protected $submissionIds = null;
|
||||
|
||||
/** @var null|array get submission files matching one or more files */
|
||||
protected $fileIds = null;
|
||||
|
||||
/** @var null|string get submission files matching one ASSOC_TYPE */
|
||||
protected $assocType = null;
|
||||
|
||||
/** @var null|array get submission files matching an ASSOC_ID with one of the assocTypes */
|
||||
protected $assocIds = null;
|
||||
|
||||
/** @var bool include submission files in the SubmissionFile::SUBMISSION_FILE_DEPENDENT stage */
|
||||
protected $includeDependentFiles = false;
|
||||
|
||||
/** @var null|array get submission files matching one or more uploader users id */
|
||||
protected $uploaderUserIds = null;
|
||||
|
||||
/** @var null|int */
|
||||
public $count = null;
|
||||
|
||||
/** @var null|int */
|
||||
public $offset = null;
|
||||
|
||||
public function __construct(DAO $dao)
|
||||
{
|
||||
$this->dao = $dao;
|
||||
}
|
||||
|
||||
public function getCount(): int
|
||||
{
|
||||
return $this->dao->getCount($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(): Collection
|
||||
{
|
||||
return $this->dao->getIds($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc DAO::getMany()
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(): LazyCollection
|
||||
{
|
||||
return $this->dao->getMany($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fileStages filter
|
||||
*/
|
||||
public function filterByFileStages(?array $fileStages): self
|
||||
{
|
||||
$this->fileStages = $fileStages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set genreIds filter
|
||||
*/
|
||||
public function filterByGenreIds(?array $genreIds): self
|
||||
{
|
||||
$this->genreIds = $genreIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set review rounds filter
|
||||
*/
|
||||
public function filterByReviewRoundIds(?array $reviewRoundIds): self
|
||||
{
|
||||
$this->reviewRoundIds = $reviewRoundIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set review assignments filter
|
||||
*/
|
||||
public function filterByReviewIds(?array $reviewIds): self
|
||||
{
|
||||
$this->reviewIds = $reviewIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set submissionIds filter
|
||||
*/
|
||||
public function filterBySubmissionIds(?array $submissionIds): self
|
||||
{
|
||||
$this->submissionIds = $submissionIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fileIds filter
|
||||
*/
|
||||
public function filterByFileIds(?array $fileIds): self
|
||||
{
|
||||
$this->fileIds = $fileIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set assocType and assocId filters
|
||||
*
|
||||
* @param null|int $assocType One of the Application::ASSOC_TYPE_ constants
|
||||
* @param null|array $assocIds Match for the specified assoc type
|
||||
*/
|
||||
public function filterByAssoc(?int $assocType, ?array $assocIds = null): self
|
||||
{
|
||||
$this->assocType = $assocType;
|
||||
$this->assocIds = $assocIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set uploaderUserIds filter
|
||||
*/
|
||||
public function filterByUploaderUserIds(?array $uploaderUserIds): self
|
||||
{
|
||||
$this->uploaderUserIds = $uploaderUserIds;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to include dependent files in the results
|
||||
*/
|
||||
public function includeDependentFiles(bool $includeDependentFiles = true): self
|
||||
{
|
||||
$this->includeDependentFiles = $includeDependentFiles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the number of objects retrieved
|
||||
*/
|
||||
public function limit(?int $count): self
|
||||
{
|
||||
$this->count = $count;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the number of objects retrieved, for example to
|
||||
* retrieve the second page of contents
|
||||
*/
|
||||
public function offset(?int $offset): self
|
||||
{
|
||||
$this->offset = $offset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc CollectorInterface::getQueryBuilder()
|
||||
*/
|
||||
public function getQueryBuilder(): Builder
|
||||
{
|
||||
$qb = DB::table($this->dao->table . ' as sf')
|
||||
->join('submissions as s', 's.submission_id', '=', 'sf.submission_id')
|
||||
->join('files as f', 'f.file_id', '=', 'sf.file_id')
|
||||
->select(['sf.*', 'f.*', 's.locale as locale']);
|
||||
|
||||
if ($this->submissionIds !== null) {
|
||||
$qb->whereIn('sf.submission_id', $this->submissionIds);
|
||||
}
|
||||
|
||||
if ($this->fileStages !== null) {
|
||||
$qb->whereIn('sf.file_stage', $this->fileStages);
|
||||
}
|
||||
|
||||
if ($this->genreIds !== null) {
|
||||
$qb->whereIn('sf.genre_id', $this->genreIds);
|
||||
}
|
||||
|
||||
if ($this->fileIds !== null) {
|
||||
$qb->whereIn('sf.submission_file_id', function ($query) {
|
||||
return $query->select('submission_file_id')
|
||||
->from('submission_file_revisions')
|
||||
->whereIn('file_id', $this->fileIds);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->reviewRoundIds !== null) {
|
||||
$qb->whereIn('sf.submission_file_id', function ($query) {
|
||||
return $query->select('submission_file_id')
|
||||
->from('review_round_files')
|
||||
->whereIn('review_round_id', $this->reviewRoundIds);
|
||||
});
|
||||
}
|
||||
|
||||
if ($this->reviewIds !== null) {
|
||||
$qb->join('review_files as rf', 'rf.submission_file_id', '=', 'sf.submission_file_id')
|
||||
->whereIn('rf.review_id', $this->reviewIds);
|
||||
}
|
||||
|
||||
if ($this->assocType !== null) {
|
||||
$qb->where('sf.assoc_type', $this->assocType);
|
||||
|
||||
if ($this->assocIds !== null) {
|
||||
$qb->whereIn('sf.assoc_id', $this->assocIds);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->uploaderUserIds !== null) {
|
||||
$qb->whereIn('sf.uploader_user_id', $this->uploaderUserIds);
|
||||
}
|
||||
|
||||
if ($this->includeDependentFiles !== true && $this->fileStages !== null && !in_array(SubmissionFile::SUBMISSION_FILE_DEPENDENT, $this->fileStages)) {
|
||||
$qb->where('sf.file_stage', '!=', SubmissionFile::SUBMISSION_FILE_DEPENDENT);
|
||||
}
|
||||
|
||||
$qb->orderBy('sf.created_at', 'desc');
|
||||
|
||||
if ($this->count !== null) {
|
||||
$qb->limit($this->count);
|
||||
}
|
||||
|
||||
if ($this->offset !== null) {
|
||||
$qb->offset($this->offset);
|
||||
}
|
||||
|
||||
Hook::call('SubmissionFile::Collector::getQueryBuilder', [&$qb, $this]);
|
||||
|
||||
return $qb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/submissionFile/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 submissionFile
|
||||
*
|
||||
* @see SubmissionFile
|
||||
*
|
||||
* @brief Operations for retrieving and modifying submission files
|
||||
*/
|
||||
|
||||
namespace PKP\submissionFile;
|
||||
|
||||
use APP\core\Application;
|
||||
use Exception;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\LazyCollection;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\plugins\PKPPubIdPluginDAO;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\submission\reviewAssignment\ReviewAssignmentDAO;
|
||||
use PKP\submission\ReviewFilesDAO;
|
||||
use PKP\submission\reviewRound\ReviewRound;
|
||||
use PKP\submission\reviewRound\ReviewRoundDAO;
|
||||
|
||||
/**
|
||||
* @template T of SubmissionFile
|
||||
* @extends EntityDAO<T>
|
||||
*/
|
||||
class DAO extends EntityDAO implements PKPPubIdPluginDAO
|
||||
{
|
||||
/** @copydoc EntityDAO::$schema */
|
||||
public $schema = PKPSchemaService::SCHEMA_SUBMISSION_FILE;
|
||||
|
||||
/** @copydoc EntityDAO::$table */
|
||||
public $table = 'submission_files';
|
||||
|
||||
/** @copydoc EntityDAO::$settingsTable */
|
||||
public $settingsTable = 'submission_file_settings';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryKeyColumn */
|
||||
public $primaryKeyColumn = 'submission_file_id';
|
||||
|
||||
/** @copydoc EntityDAO::$primaryTableColumns */
|
||||
public $primaryTableColumns = [
|
||||
'assocId' => 'assoc_id',
|
||||
'assocType' => 'assoc_type',
|
||||
'createdAt' => 'created_at',
|
||||
'fileId' => 'file_id',
|
||||
'fileStage' => 'file_stage',
|
||||
'genreId' => 'genre_id',
|
||||
'id' => 'submission_file_id',
|
||||
'sourceSubmissionFileId' => 'source_submission_file_id',
|
||||
'submissionId' => 'submission_id',
|
||||
'updatedAt' => 'updated_at',
|
||||
'uploaderUserId' => 'uploader_user_id',
|
||||
'viewable' => 'viewable',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent object ID column name
|
||||
*/
|
||||
public function getParentColumn(): string
|
||||
{
|
||||
return 'submission_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new SubmissionFile
|
||||
*/
|
||||
public function newDataObject(): SubmissionFile
|
||||
{
|
||||
return app(SubmissionFile::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a submission file.
|
||||
*
|
||||
* Optionally, pass the submission ID to only get a submission file
|
||||
* if it exists and is assigned to that submission.
|
||||
*/
|
||||
public function get(int $id, int $submissionId = null): ?SubmissionFile
|
||||
{
|
||||
$query = new Collector($this);
|
||||
$row = $query
|
||||
->getQueryBuilder()
|
||||
->where($this->primaryKeyColumn, '=', $id)
|
||||
->when($submissionId !== null, fn (Builder $query) => $query->where('sf.submission_id', $submissionId))
|
||||
->first();
|
||||
|
||||
return $row ? $this->fromRow($row) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a submission file exists.
|
||||
*
|
||||
* Optionally, pass the submission ID to check if the submission file
|
||||
* exists and is assigned to that submission.
|
||||
*/
|
||||
public function exists(int $id, int $submissionId = null): bool
|
||||
{
|
||||
return DB::table($this->table)
|
||||
->where($this->primaryKeyColumn, '=', $id)
|
||||
->when($submissionId !== null, fn (Builder $query) => $query->where($this->getParentColumn(), $submissionId))
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return Collection<int,int>
|
||||
*/
|
||||
public function getIds(Collector $query): Collection
|
||||
{
|
||||
return $query
|
||||
->getQueryBuilder()
|
||||
->select('sf.' . $this->primaryKeyColumn)
|
||||
->pluck('sf.' . $this->primaryKeyColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of announcements matching the configured query
|
||||
* @return LazyCollection<int,T>
|
||||
*/
|
||||
public function getMany(Collector $query): LazyCollection
|
||||
{
|
||||
$rows = $query
|
||||
->getQueryBuilder()
|
||||
->get();
|
||||
|
||||
return LazyCollection::make(function () use ($rows) {
|
||||
foreach ($rows as $row) {
|
||||
yield $row->submission_file_id => $this->fromRow($row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::fromRow()
|
||||
*/
|
||||
public function fromRow(object $primaryRow): SubmissionFile
|
||||
{
|
||||
$submissionFile = parent::fromRow($primaryRow);
|
||||
$submissionFile->setData('locale', $primaryRow->locale);
|
||||
$submissionFile->setData('path', $primaryRow->path);
|
||||
$submissionFile->setData('mimetype', $primaryRow->mimetype);
|
||||
|
||||
return $submissionFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::insert()
|
||||
*/
|
||||
public function insert(SubmissionFile $submissionFile): int
|
||||
{
|
||||
parent::_insert($submissionFile);
|
||||
|
||||
DB::table('submission_file_revisions')->insert([
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
'file_id' => $submissionFile->getData('fileId'),
|
||||
]);
|
||||
|
||||
$this->insertReviewRound($submissionFile);
|
||||
|
||||
return $submissionFile->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a Submission File
|
||||
*/
|
||||
public function update(SubmissionFile $submissionFile): void
|
||||
{
|
||||
parent::_update($submissionFile);
|
||||
|
||||
$hasSubmissionFileRevision = DB::table('submission_file_revisions')
|
||||
->where([
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
'file_id' => $submissionFile->getData('fileId')
|
||||
])
|
||||
->exists();
|
||||
|
||||
if ($hasSubmissionFileRevision) {
|
||||
return;
|
||||
}
|
||||
|
||||
DB::table('submission_file_revisions')->insert([
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
'file_id' => $submissionFile->getData('fileId'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDAO::delete()
|
||||
*/
|
||||
public function delete(SubmissionFile $submissionFile)
|
||||
{
|
||||
parent::_delete($submissionFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc EntityDao::deleteById()
|
||||
*/
|
||||
public function deleteById(int $submissionFileId)
|
||||
{
|
||||
DB::table('submission_file_revisions')
|
||||
->where('submission_file_id', '=', $submissionFileId)
|
||||
->delete();
|
||||
|
||||
DB::table('review_round_files')
|
||||
->where('submission_file_id', '=', $submissionFileId)
|
||||
->delete();
|
||||
|
||||
$reviewFilesDao = DAORegistry::getDAO('ReviewFilesDAO'); /** @var ReviewFilesDAO $reviewFilesDao */
|
||||
$reviewFilesDao->revokeBySubmissionFileId($submissionFileId);
|
||||
|
||||
parent::deleteById($submissionFileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve file by public file ID
|
||||
*
|
||||
* $pubIdType it is 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 null|mixed $submissionId
|
||||
* @param null|mixed $contextId
|
||||
*/
|
||||
public function getByPubId(
|
||||
$pubIdType,
|
||||
$pubId,
|
||||
$submissionId = null,
|
||||
$contextId = null
|
||||
): ?SubmissionFile {
|
||||
if (empty($pubId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$submissionFileId = DB::table('submission_files as sf')
|
||||
->where('sf.submission_id', '=', $submissionId)
|
||||
->whereIn('sf.submission_file_id', function ($q) use ($pubIdType, $pubId) {
|
||||
return $q->select('sfs.submission_file_id')
|
||||
->from($this->settingsTable . ' as sfs')
|
||||
->where('sfs.setting_name', '=', 'pub-id::' . $pubIdType)
|
||||
->where('sfs.setting_value', '=', $pubId);
|
||||
})
|
||||
->select('sf.*')
|
||||
->value('sf.submission_file_id');
|
||||
|
||||
if (empty($submissionFileId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$submissionFile = $this->get($submissionFileId);
|
||||
|
||||
if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_PROOF) {
|
||||
return $submissionFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve file by public ID or submissionFileId
|
||||
*
|
||||
* @param string|int $bestId Publisher id or submissionFileId
|
||||
*/
|
||||
public function getByBestId(
|
||||
$bestId,
|
||||
int $submissionId
|
||||
): ?SubmissionFile {
|
||||
$submissionFile = null;
|
||||
|
||||
if ($bestId != '') {
|
||||
$submissionFile = $this->getByPubId('publisher-id', $bestId, $submissionId, null);
|
||||
}
|
||||
|
||||
if (!isset($submissionFile)) {
|
||||
$submissionFile = $this->get($bestId);
|
||||
}
|
||||
|
||||
if (
|
||||
$submissionFile &&
|
||||
in_array(
|
||||
$submissionFile->getData('fileStage'),
|
||||
[
|
||||
SubmissionFile::SUBMISSION_FILE_PROOF,
|
||||
SubmissionFile::SUBMISSION_FILE_DEPENDENT
|
||||
]
|
||||
)
|
||||
) {
|
||||
return $submissionFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign file to a review round.
|
||||
*/
|
||||
public function assignRevisionToReviewRound(
|
||||
SubmissionFile $submissionFile,
|
||||
ReviewRound $reviewRound
|
||||
): void {
|
||||
DB::table('review_round_files')->updateOrInsert(
|
||||
[
|
||||
'submission_id' => $reviewRound->getSubmissionId(),
|
||||
'review_round_id' => $reviewRound->getId(),
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
],
|
||||
[
|
||||
'submission_id' => $reviewRound->getSubmissionId(),
|
||||
'review_round_id' => $reviewRound->getId(),
|
||||
'stage_id' => $reviewRound->getStageId(),
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if public identifier exists (other than for the specified
|
||||
* submission file ID, which is treated as an exception).
|
||||
*
|
||||
* $pubIdType it is one of the NLM pub-id-type values or
|
||||
* 'other::something' if not part of the official NLM list
|
||||
* (see <http://dtd.nlm.nih.gov/publishing/tag-library/n-4zh0.html>).
|
||||
*/
|
||||
public function pubIdExists(
|
||||
$pubIdType,
|
||||
$pubId,
|
||||
$excludePubObjectId,
|
||||
$contextId
|
||||
): bool {
|
||||
$result = DB::table($this->settingsTable . ' as sfs')
|
||||
->join('submission_files AS sf', 'sfs.submission_file_id', '=', 'sf.submission_file_id')
|
||||
->join('submissions AS s', 'sf.submission_id', '=', 's.submission_id')
|
||||
->where([
|
||||
'sfs.setting_name' => 'pub-id::' . (string) $pubIdType,
|
||||
'sfs.setting_value' => (string) $pubId,
|
||||
'sfs.submission_file_id' => (int) $excludePubObjectId,
|
||||
's.context_id' => (int) $contextId
|
||||
])->count();
|
||||
return (bool) $result > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::changePubId()
|
||||
*/
|
||||
public function changePubId($pubObjectId, $pubIdType, $pubId)
|
||||
{
|
||||
DB::table($this->settingsTable)
|
||||
->updateOrInsert(
|
||||
[
|
||||
'submission_file_id' => (int) $pubObjectId,
|
||||
'setting_name' => 'pub-id::' . (string) $pubIdType,
|
||||
'setting_value' => (string) $pubId
|
||||
],
|
||||
['locale' => '']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::deletePubId()
|
||||
*/
|
||||
public function deletePubId($pubObjectId, $pubIdType)
|
||||
{
|
||||
DB::table($this->settingsTable)
|
||||
->where([
|
||||
'submission_file_id' => (int) $pubObjectId,
|
||||
'setting_name' => 'pub-id::' . (string) $pubIdType
|
||||
])->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPluginDAO::deleteAllPubIds()
|
||||
*/
|
||||
public function deleteAllPubIds($contextId, $pubIdType)
|
||||
{
|
||||
return DB::table('publication_settings as ps')
|
||||
->leftJoin('publications as p', 'p.publication_id', '=', 'ps.publication_id')
|
||||
->leftJoin('submissions as s', 's.submission_id', '=', 'p.submission_id')
|
||||
->where('ps.setting_name', '=', 'pub-id::' . $pubIdType)
|
||||
->where('s.context_id', '=', $contextId)
|
||||
->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a review round for Submission File
|
||||
*
|
||||
* @throws Exception If we couldn't find the review Round, throws an exception.
|
||||
*/
|
||||
protected function insertReviewRound(SubmissionFile $submissionFile): void
|
||||
{
|
||||
if (
|
||||
!in_array(
|
||||
$submissionFile->getData('assocType'),
|
||||
[
|
||||
Application::ASSOC_TYPE_REVIEW_ROUND,
|
||||
Application::ASSOC_TYPE_REVIEW_ASSIGNMENT
|
||||
]
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$reviewRound = $this->getReviewRound($submissionFile);
|
||||
|
||||
if (!$reviewRound) {
|
||||
throw new Exception('Review round not found for adding submission file.');
|
||||
}
|
||||
|
||||
DB::table('review_round_files')->insert([
|
||||
'submission_id' => $submissionFile->getData('submissionId'),
|
||||
'review_round_id' => $reviewRound->getId(),
|
||||
'stage_id' => $reviewRound->getStageId(),
|
||||
'submission_file_id' => $submissionFile->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the review round for a SubmissionFile, otherwise returns null
|
||||
*/
|
||||
protected function getReviewRound(SubmissionFile $submissionFile): ?ReviewRound
|
||||
{
|
||||
if ($submissionFile->getData('assocType') === Application::ASSOC_TYPE_REVIEW_ROUND) {
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
return $reviewRoundDao->getById($submissionFile->getData('assocId'));
|
||||
}
|
||||
|
||||
if ($submissionFile->getData('assocType') === Application::ASSOC_TYPE_REVIEW_ASSIGNMENT) {
|
||||
$reviewAssignmentDao = DAORegistry::getDAO('ReviewAssignmentDAO'); /** @var ReviewAssignmentDAO $reviewAssignmentDao */
|
||||
$reviewAssignment = $reviewAssignmentDao->getById($submissionFile->getData('assocId'));
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
return $reviewRoundDao->getById($reviewAssignment->getReviewRoundId());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/submissionFile/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 submission files.
|
||||
*/
|
||||
|
||||
namespace PKP\submissionFile;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use APP\notification\Notification;
|
||||
use APP\notification\NotificationManager;
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\log\SubmissionEmailLogDAO;
|
||||
use PKP\log\SubmissionEmailLogEntry;
|
||||
use PKP\log\event\SubmissionFileEventLogEntry;
|
||||
use PKP\mail\mailables\RevisedVersionNotify;
|
||||
use PKP\note\NoteDAO;
|
||||
use PKP\notification\PKPNotification;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\query\QueryDAO;
|
||||
use PKP\security\authorization\SubmissionFileAccessPolicy;
|
||||
use PKP\security\Role;
|
||||
use PKP\security\Validation;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\stageAssignment\StageAssignmentDAO;
|
||||
use PKP\submission\reviewRound\ReviewRoundDAO;
|
||||
use PKP\submissionFile\maps\Schema;
|
||||
use PKP\validation\ValidatorFactory;
|
||||
|
||||
abstract class Repository
|
||||
{
|
||||
public DAO $dao;
|
||||
public string $schemaMap = Schema::class;
|
||||
protected Request $request;
|
||||
/** @var PKPSchemaService<SubmissionFile> */
|
||||
protected PKPSchemaService $schemaService;
|
||||
|
||||
/** @var array<int> $reviewFileStages The file stages that are part of a review workflow stage */
|
||||
public array $reviewFileStages = [];
|
||||
|
||||
public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
|
||||
{
|
||||
$this->schemaService = $schemaService;
|
||||
$this->dao = $dao;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::newDataObject() */
|
||||
public function newDataObject(array $params = []): SubmissionFile
|
||||
{
|
||||
$object = $this->dao->newDataObject();
|
||||
if (!empty($params)) {
|
||||
$object->setAllData($params);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::get() */
|
||||
public function get(int $id, int $submissionId = null): ?SubmissionFile
|
||||
{
|
||||
return $this->dao->get($id, $submissionId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::exists() */
|
||||
public function exists(int $id, int $submissionId = null): bool
|
||||
{
|
||||
return $this->dao->exists($id, $submissionId);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::getCollector() */
|
||||
public function getCollector(): Collector
|
||||
{
|
||||
return app(Collector::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the map class for mapping
|
||||
* submission Files to their schema
|
||||
*/
|
||||
public function getSchemaMap(): Schema
|
||||
{
|
||||
return app('maps')->withExtensions($this->schemaMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate properties for a submission file
|
||||
*
|
||||
* Perform validation checks on data used to add or edit a submission file.
|
||||
*
|
||||
* @param array $props A key/value array with the new data to validate
|
||||
* @param array $allowedLocales The context's supported locales
|
||||
* @param string $primaryLocale The context's primary locale
|
||||
*
|
||||
* @return array A key/value array with validation errors. Empty if no errors
|
||||
*/
|
||||
public function validate(
|
||||
?SubmissionFile $object,
|
||||
array $props,
|
||||
array $allowedLocales,
|
||||
string $primaryLocale
|
||||
): array {
|
||||
$validator = ValidatorFactory::make(
|
||||
$props,
|
||||
$this->schemaService->getValidationRules($this->dao->schema, $allowedLocales),
|
||||
[]
|
||||
);
|
||||
|
||||
// Check required fields
|
||||
ValidatorFactory::required(
|
||||
$validator,
|
||||
$object,
|
||||
$this->schemaService->getRequiredProps($this->dao->schema),
|
||||
$this->schemaService->getMultilingualProps($this->dao->schema),
|
||||
$allowedLocales,
|
||||
$primaryLocale
|
||||
);
|
||||
|
||||
// Check for input from disallowed locales
|
||||
ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales);
|
||||
|
||||
// Do not allow the uploaderUserId or createdAt properties to be modified
|
||||
if ($object) {
|
||||
$validator->after(function ($validator) use ($props) {
|
||||
if (
|
||||
!empty($props['uploaderUserId']) &&
|
||||
!$validator->errors()->get('uploaderUserId')
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'uploaderUserId',
|
||||
__('submission.file.notAllowedUploaderUserId')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($props['createdAt']) &&
|
||||
!$validator->errors()->get('createdAt')
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'createdAt',
|
||||
__('api.files.400.notAllowedCreatedAt')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Make sure that file stage and assocType match
|
||||
if (isset($props['assocType'])) {
|
||||
$validator->after(function ($validator) use ($props) {
|
||||
if (
|
||||
$props['assocType'] === PKPApplication::ASSOC_TYPE_REVIEW_ROUND &&
|
||||
!in_array(
|
||||
$props['fileStage'],
|
||||
[SubmissionFile::SUBMISSION_FILE_REVIEW_FILE, SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION, SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE, SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION]
|
||||
)
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'assocType',
|
||||
__('api.submissionFiles.400.badReviewRoundAssocType')
|
||||
);
|
||||
}
|
||||
|
||||
if ($props['assocType'] === PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT && $props['fileStage'] !== SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'assocType',
|
||||
__('api.submissionFiles.400.badReviewAssignmentAssocType')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$props['assocType'] === PKPApplication::ASSOC_TYPE_SUBMISSION_FILE &&
|
||||
$props['fileStage'] !== SubmissionFile::SUBMISSION_FILE_DEPENDENT
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'assocType',
|
||||
__('api.submissionFiles.400.badDependentFileAssocType')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$props['assocType'] === PKPApplication::ASSOC_TYPE_NOTE &&
|
||||
$props['fileStage'] !== SubmissionFile::SUBMISSION_FILE_NOTE
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'assocType',
|
||||
__('api.submissionFiles.400.badNoteAssocType')
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$props['assocType'] === PKPApplication::ASSOC_TYPE_REPRESENTATION &&
|
||||
$props['fileStage'] !== SubmissionFile::SUBMISSION_FILE_PROOF
|
||||
) {
|
||||
$validator
|
||||
->errors()
|
||||
->add(
|
||||
'assocType',
|
||||
__('api.submissionFiles.400.badRepresentationAssocType')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
|
||||
if ($validator->fails()) {
|
||||
$errors = $this->schemaService->formatValidationErrors($validator->errors());
|
||||
}
|
||||
|
||||
Hook::call(
|
||||
'SubmissionFile::validate',
|
||||
[
|
||||
&$errors,
|
||||
$object,
|
||||
$props,
|
||||
$allowedLocales,
|
||||
$primaryLocale
|
||||
]
|
||||
);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::insert() */
|
||||
public function add(SubmissionFile $submissionFile): int
|
||||
{
|
||||
$submissionFile->setData('createdAt', Core::getCurrentDate());
|
||||
$submissionFile->setData('updatedAt', Core::getCurrentDate());
|
||||
|
||||
$submissionFileId = $this->dao->insert($submissionFile);
|
||||
|
||||
$submissionFile = $this->get($submissionFileId);
|
||||
|
||||
Hook::call('SubmissionFile::add', [$submissionFile]);
|
||||
|
||||
$logData = $this->getSubmissionFileLogData($submissionFile);
|
||||
|
||||
$logEntry = Repo::eventLog()->newDataObject(array_merge(
|
||||
$logData,
|
||||
[
|
||||
'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE,
|
||||
'assocId' => $submissionFile->getId(),
|
||||
'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD,
|
||||
'dateLogged' => Core::getCurrentDate(),
|
||||
'message' => 'submission.event.fileUploaded',
|
||||
'isTranslated' => false,
|
||||
]
|
||||
));
|
||||
Repo::eventLog()->add($logEntry);
|
||||
|
||||
$submission = Repo::submission()->get($submissionFile->getData('submissionId'));
|
||||
|
||||
$logEntry = Repo::eventLog()->newDataObject(array_merge(
|
||||
$logData,
|
||||
[
|
||||
'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION,
|
||||
'assocId' => $submission->getId(),
|
||||
'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD,
|
||||
'dateLogged' => Core::getCurrentDate(),
|
||||
'message' => 'submission.event.fileRevised',
|
||||
'isTranslated' => false,
|
||||
]
|
||||
));
|
||||
Repo::eventLog()->add($logEntry);
|
||||
|
||||
// Update status and notifications when revisions have been uploaded
|
||||
if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION ||
|
||||
$submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION) {
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
$reviewRound = $reviewRoundDao->getById($submissionFile->getData('assocId'));
|
||||
if (!$reviewRound) {
|
||||
throw new Exception('Submission file added to review round that does not exist.');
|
||||
}
|
||||
|
||||
$reviewRoundDao->updateStatus($reviewRound);
|
||||
|
||||
// Update author notifications
|
||||
$authorUserIds = [];
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
|
||||
$authorAssignments = $stageAssignmentDao->getBySubmissionAndRoleIds($submissionFile->getData('submissionId'), [Role::ROLE_ID_AUTHOR]);
|
||||
while ($assignment = $authorAssignments->next()) {
|
||||
if ($assignment->getStageId() == $reviewRound->getStageId()) {
|
||||
$authorUserIds[] = (int) $assignment->getUserId();
|
||||
}
|
||||
}
|
||||
$notificationMgr = new NotificationManager();
|
||||
$notificationMgr->updateNotification(
|
||||
$this->request,
|
||||
[PKPNotification::NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, PKPNotification::NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS],
|
||||
$authorUserIds,
|
||||
PKPApplication::ASSOC_TYPE_SUBMISSION,
|
||||
$submissionFile->getData('submissionId')
|
||||
);
|
||||
|
||||
// Notify editors if the file is uploaded by an author
|
||||
if (in_array($submissionFile->getData('uploaderUserId'), $authorUserIds)) {
|
||||
if (!$submission) {
|
||||
throw new Exception('Submission file added to submission that does not exist.');
|
||||
}
|
||||
|
||||
$this->notifyEditorsRevisionsUploaded($submissionFile);
|
||||
}
|
||||
}
|
||||
|
||||
return $submissionFileId;
|
||||
}
|
||||
|
||||
/** @copydoc DAO::update() */
|
||||
public function edit(
|
||||
SubmissionFile $submissionFile,
|
||||
array $params
|
||||
): void {
|
||||
$newSubmissionFile = clone $submissionFile;
|
||||
$newSubmissionFile->setAllData(array_merge($newSubmissionFile->_data, $params));
|
||||
|
||||
Hook::call(
|
||||
'SubmissionFile::edit',
|
||||
[
|
||||
$newSubmissionFile,
|
||||
$submissionFile,
|
||||
$params
|
||||
]
|
||||
);
|
||||
|
||||
$newSubmissionFile->setData('updatedAt', Core::getCurrentDate());
|
||||
|
||||
$this->dao->update($newSubmissionFile);
|
||||
|
||||
$newFileUploaded = !empty($params['fileId']) && $params['fileId'] !== $submissionFile->getData('fileId');
|
||||
|
||||
$logData = $this->getSubmissionFileLogData($submissionFile);
|
||||
$logEntry = Repo::eventLog()->newDataObject(array_merge(
|
||||
$logData,
|
||||
[
|
||||
'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE,
|
||||
'assocId' => $submissionFile->getId(),
|
||||
'eventType' => $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT,
|
||||
'message' => $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited',
|
||||
'isTranslated' => false,
|
||||
'dateLogged' => Core::getCurrentDate(),
|
||||
]
|
||||
));
|
||||
Repo::eventLog()->add($logEntry);
|
||||
|
||||
$submission = Repo::submission()->get($submissionFile->getData('submissionId'));
|
||||
|
||||
Repo::eventLog()->newDataObject(array_merge(
|
||||
$logData,
|
||||
[
|
||||
'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION,
|
||||
'assocId' => $submission->getId(),
|
||||
'eventType' => $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT,
|
||||
'message' => $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited',
|
||||
'isTranslate' => false,
|
||||
'dateLogged' => Core::getCurrentDate(),
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a submission file to another stage
|
||||
*
|
||||
* @return int ID of the new submission file
|
||||
*/
|
||||
public function copy(SubmissionFile $submissionFile, int $toFileStage, ?int $reviewRoundId = null): int
|
||||
{
|
||||
$newSubmissionFile = clone $submissionFile;
|
||||
$newSubmissionFile->setData('fileStage', $toFileStage);
|
||||
$newSubmissionFile->setData('sourceSubmissionFileId', $submissionFile->getId());
|
||||
$newSubmissionFile->setData('assocType', null);
|
||||
$newSubmissionFile->setData('assocId', null);
|
||||
|
||||
if ($reviewRoundId) {
|
||||
$newSubmissionFile->setData('assocType', Application::ASSOC_TYPE_REVIEW_ROUND);
|
||||
$newSubmissionFile->setData('assocId', $reviewRoundId);
|
||||
}
|
||||
|
||||
return Repo::submissionFile()->add($newSubmissionFile);
|
||||
}
|
||||
|
||||
/** @copydoc DAO::delete() */
|
||||
public function delete(SubmissionFile $submissionFile): void
|
||||
{
|
||||
Hook::call('SubmissionFile::delete::before', [$submissionFile]);
|
||||
|
||||
// Delete dependent files
|
||||
$this
|
||||
->getCollector()
|
||||
->includeDependentFiles(true)
|
||||
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_DEPENDENT])
|
||||
->filterByAssoc(Application::ASSOC_TYPE_SUBMISSION_FILE, [$submissionFile->getId()])
|
||||
->getMany()
|
||||
->each(function (SubmissionFile $dependentFile) {
|
||||
$this->delete($dependentFile);
|
||||
});
|
||||
|
||||
// Delete notes for this submission file
|
||||
$noteDao = DAORegistry::getDAO('NoteDAO'); /** @var NoteDAO $noteDao */
|
||||
$noteDao->deleteByAssoc(Application::ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getId());
|
||||
|
||||
// Update tasks
|
||||
$notificationMgr = new NotificationManager();
|
||||
switch ($submissionFile->getData('fileStage')) {
|
||||
case SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION:
|
||||
$authorUserIds = [];
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
|
||||
$submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleIds($submissionFile->getData('submissionId'), [Role::ROLE_ID_AUTHOR]);
|
||||
while ($assignment = $submitterAssignments->next()) {
|
||||
$authorUserIds[] = $assignment->getUserId();
|
||||
}
|
||||
$notificationMgr->updateNotification(
|
||||
Application::get()->getRequest(),
|
||||
[
|
||||
Notification::NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS,
|
||||
Notification::NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS
|
||||
],
|
||||
$authorUserIds,
|
||||
Application::ASSOC_TYPE_SUBMISSION,
|
||||
$submissionFile->getData('submissionId')
|
||||
);
|
||||
break;
|
||||
|
||||
case SubmissionFile::SUBMISSION_FILE_COPYEDIT:
|
||||
$notificationMgr->updateNotification(
|
||||
Application::get()->getRequest(),
|
||||
[
|
||||
Notification::NOTIFICATION_TYPE_ASSIGN_COPYEDITOR,
|
||||
Notification::NOTIFICATION_TYPE_AWAITING_COPYEDITS
|
||||
],
|
||||
null,
|
||||
Application::ASSOC_TYPE_SUBMISSION,
|
||||
$submissionFile->getData('submissionId')
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get all revision file ids before they are deleted
|
||||
$revisions = $this->getRevisions($submissionFile->getId());
|
||||
|
||||
// Get the review round before review round files are deleted
|
||||
if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION) {
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
$reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId());
|
||||
}
|
||||
|
||||
|
||||
$this->dao->delete($submissionFile);
|
||||
|
||||
// Delete all files that are not referenced by other submission files
|
||||
foreach ($revisions as $revision) {
|
||||
$countFileShares = $this
|
||||
->getCollector()
|
||||
->filterByFileIds([$revision->fileId])
|
||||
->includeDependentFiles(true)
|
||||
->getCount();
|
||||
if (!$countFileShares) {
|
||||
Services::get('file')->delete($revision->fileId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the review round status after deletion
|
||||
if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION) {
|
||||
$reviewRoundDao->updateStatus($reviewRound);
|
||||
}
|
||||
|
||||
// Log the deletion
|
||||
$logEntry = Repo::eventLog()->newDataObject(array_merge(
|
||||
$this->getSubmissionFileLogData($submissionFile),
|
||||
[
|
||||
'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE,
|
||||
'assocId' => $submissionFile->getId(),
|
||||
'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_DELETE,
|
||||
'message' => 'submission.event.fileDeleted',
|
||||
'isTranslated' => false,
|
||||
'dateLogged' => Core::getCurrentDate(),
|
||||
]
|
||||
));
|
||||
Repo::eventLog()->add($logEntry);
|
||||
|
||||
Hook::call('SubmissionFile::delete', [$submissionFile]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file stage ids that a user can access based on their
|
||||
* stage assignments
|
||||
*
|
||||
* This does not return file stages for ROLE_ID_REVIEWER or ROLE_ID_READER.
|
||||
* These roles are not granted stage assignments and this method should not
|
||||
* be used for these roles.
|
||||
*
|
||||
* This method does not define access to review attachments, discussion
|
||||
* files or dependent files. Access to these files are not determined by
|
||||
* stage assignment.
|
||||
*
|
||||
* In some cases it may be necessary to apply additional restrictions. For example,
|
||||
* authors are granted write access to submission files or revisions only when other
|
||||
* conditions are met. This method only considers these an assigned file stage for
|
||||
* authors when read access is requested.
|
||||
*
|
||||
* $stageAssignments it's an array holding the stage assignments of this user.
|
||||
* Each key is a workflow stage and value is an array of assigned roles
|
||||
* $action it's an integer holding a flag to read or write to file stages. One of SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_
|
||||
*
|
||||
* @return array List of file stages (SubmissionFile::SUBMISSION_FILE_*)
|
||||
*/
|
||||
public function getAssignedFileStages(
|
||||
array $stageAssignments,
|
||||
int $action
|
||||
): array {
|
||||
$allowedRoles = [
|
||||
Role::ROLE_ID_MANAGER,
|
||||
Role::ROLE_ID_SITE_ADMIN,
|
||||
Role::ROLE_ID_SUB_EDITOR,
|
||||
Role::ROLE_ID_ASSISTANT,
|
||||
Role::ROLE_ID_AUTHOR
|
||||
];
|
||||
$notAuthorRoles = array_diff($allowedRoles, [Role::ROLE_ID_AUTHOR]);
|
||||
|
||||
$allowedFileStages = [];
|
||||
|
||||
if (
|
||||
array_key_exists(WORKFLOW_STAGE_ID_SUBMISSION, $stageAssignments) &&
|
||||
!empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]))
|
||||
) {
|
||||
$hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_SUBMISSION]));
|
||||
// Authors only have read access
|
||||
if ($action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_SUBMISSION;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists(WORKFLOW_STAGE_ID_INTERNAL_REVIEW, $stageAssignments)) {
|
||||
$hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_INTERNAL_REVIEW]));
|
||||
// Authors can only write revision files under specific conditions
|
||||
if ($action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION;
|
||||
}
|
||||
// Authors can never access review files
|
||||
if ($hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists(WORKFLOW_STAGE_ID_EXTERNAL_REVIEW, $stageAssignments)) {
|
||||
$hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_EXTERNAL_REVIEW]));
|
||||
// Authors can only write revision files under specific conditions
|
||||
if ($action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION;
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_ATTACHMENT;
|
||||
}
|
||||
// Authors can never access review files
|
||||
if ($hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_REVIEW_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
array_key_exists(WORKFLOW_STAGE_ID_EDITING, $stageAssignments) &&
|
||||
!empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_EDITING]))
|
||||
) {
|
||||
$hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_EDITING]));
|
||||
// Authors only have read access
|
||||
if ($action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_COPYEDIT;
|
||||
}
|
||||
if ($hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_FINAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists(WORKFLOW_STAGE_ID_PRODUCTION, $stageAssignments) &&
|
||||
!empty(array_intersect($allowedRoles, $stageAssignments[WORKFLOW_STAGE_ID_PRODUCTION]))
|
||||
) {
|
||||
$hasEditorialAssignment = !empty(array_intersect($notAuthorRoles, $stageAssignments[WORKFLOW_STAGE_ID_PRODUCTION]));
|
||||
// Authors only have read access
|
||||
if ($action === SubmissionFileAccessPolicy::SUBMISSION_FILE_ACCESS_READ || $hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_PROOF;
|
||||
}
|
||||
|
||||
if ($hasEditorialAssignment) {
|
||||
$allowedFileStages[] = SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY;
|
||||
}
|
||||
}
|
||||
|
||||
return $allowedFileStages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all valid file stages
|
||||
*
|
||||
* Valid file stages should be passed through
|
||||
* the hook SubmissionFile::fileStages.
|
||||
*/
|
||||
abstract public function getFileStages(): array;
|
||||
|
||||
/**
|
||||
* Get the path to a submission's file directory
|
||||
*
|
||||
* This returns the relative path from the files_dir set in the config.
|
||||
*/
|
||||
public function getSubmissionDir(
|
||||
int $contextId,
|
||||
int $submissionId
|
||||
): string {
|
||||
$dirNames = Application::getFileDirectories();
|
||||
return sprintf(
|
||||
'%s/%d/%s/%d',
|
||||
str_replace('/', '', $dirNames['context']),
|
||||
$contextId,
|
||||
str_replace('/', '', $dirNames['submission']),
|
||||
$submissionId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the workflow stage for a submission file
|
||||
*/
|
||||
public function getWorkflowStageId(SubmissionFile $submissionFile): ?int
|
||||
{
|
||||
$fileStage = $submissionFile->getData('fileStage');
|
||||
|
||||
if ($fileStage === SubmissionFile::SUBMISSION_FILE_SUBMISSION) {
|
||||
return WORKFLOW_STAGE_ID_SUBMISSION;
|
||||
}
|
||||
|
||||
if (
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_FINAL ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_COPYEDIT
|
||||
) {
|
||||
return WORKFLOW_STAGE_ID_EDITING;
|
||||
}
|
||||
|
||||
if (
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_PROOF ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY
|
||||
) {
|
||||
return WORKFLOW_STAGE_ID_PRODUCTION;
|
||||
}
|
||||
|
||||
if (
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_DEPENDENT
|
||||
) {
|
||||
$parentFile = $this->get($submissionFile->getData('assocId'));
|
||||
|
||||
return $parentFile ? $this->getWorkflowStageId($parentFile) : null;
|
||||
}
|
||||
|
||||
if (
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_REVIEW_FILE ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_ATTACHMENT ||
|
||||
$fileStage === SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION
|
||||
) {
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
$reviewRound = $reviewRoundDao->getBySubmissionFileId($submissionFile->getId());
|
||||
|
||||
return $reviewRound?->getStageId();
|
||||
}
|
||||
|
||||
if ($fileStage === SubmissionFile::SUBMISSION_FILE_QUERY) {
|
||||
// This file should be associated with a note. If not, fail.
|
||||
if ($submissionFile->getData('assocType') != PKPApplication::ASSOC_TYPE_NOTE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the associated note.
|
||||
$noteDao = DAORegistry::getDAO('NoteDAO'); /** @var NoteDAO $noteDao */
|
||||
$note = $noteDao->getById($submissionFile->getData('assocId'));
|
||||
|
||||
// The note should be associated with a query. If not, fail.
|
||||
if ($note?->getAssocType() != PKPApplication::ASSOC_TYPE_QUERY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the associated query.
|
||||
$queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */
|
||||
$query = $queryDao->getById($note->getAssocId());
|
||||
|
||||
// The query will have an associated file stage.
|
||||
return $query ? $query->getStageId() : null;
|
||||
}
|
||||
|
||||
throw new Exception('Could not determine the workflow stage id from submission file ' . $submissionFile->getId() . ' with file stage ' . $submissionFile->getData('fileStage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a submission file supports dependent files
|
||||
*/
|
||||
public function supportsDependentFiles(SubmissionFile $submissionFile): bool
|
||||
{
|
||||
$fileStage = $submissionFile->getData('fileStage');
|
||||
$excludedFileStages = [
|
||||
SubmissionFile::SUBMISSION_FILE_DEPENDENT,
|
||||
SubmissionFile::SUBMISSION_FILE_QUERY,
|
||||
];
|
||||
$allowedMimetypes = [
|
||||
'text/html',
|
||||
'application/xml',
|
||||
'text/xml',
|
||||
];
|
||||
|
||||
$result = !in_array($fileStage, $excludedFileStages) && in_array($submissionFile->getData('mimetype'), $allowedMimetypes);
|
||||
|
||||
Hook::call('SubmissionFile::supportsDependentFiles', [&$result, $submissionFile]);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the files for each revision of a submission file
|
||||
*/
|
||||
public function getRevisions(int $submissionFileId): Collection
|
||||
{
|
||||
return DB::table('submission_file_revisions as sfr')
|
||||
->leftJoin('files as f', 'f.file_id', '=', 'sfr.file_id')
|
||||
->where('submission_file_id', '=', $submissionFileId)
|
||||
->orderBy('revision_id', 'desc')
|
||||
->select(['f.file_id as fileId', 'f.path', 'f.mimetype', 'sfr.revision_id'])
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends email to notify editors about new revision of a submission file
|
||||
*/
|
||||
protected function notifyEditorsRevisionsUploaded(SubmissionFile $submissionFile): void
|
||||
{
|
||||
$submission = Repo::submission()->get($submissionFile->getData('submissionId'));
|
||||
$context = Services::get('context')->get($submission->getData('contextId'));
|
||||
$uploader = Repo::user()->get($submissionFile->getData('uploaderUserId'));
|
||||
$user = $this->request->getUser();
|
||||
|
||||
// Fetch the latest notification email timestamp
|
||||
$submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO');
|
||||
/** @var SubmissionEmailLogDAO $submissionEmailLogDao */
|
||||
$submissionEmails = $submissionEmailLogDao->getByEventType(
|
||||
$submission->getId(),
|
||||
SubmissionEmailLogEntry::SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION
|
||||
);
|
||||
$lastNotification = null;
|
||||
$sentDates = [];
|
||||
if ($submissionEmails) {
|
||||
while ($email = $submissionEmails->next()) {
|
||||
if ($email->getDateSent()) {
|
||||
$sentDates[] = $email->getDateSent();
|
||||
}
|
||||
}
|
||||
if (!empty($sentDates)) {
|
||||
$lastNotification = max(array_map('strtotime', $sentDates));
|
||||
}
|
||||
}
|
||||
|
||||
// Get editors assigned to the submission, consider also the recommendOnly editors
|
||||
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
|
||||
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao*/
|
||||
$reviewRound = $reviewRoundDao->getById($submissionFile->getData('assocId'));
|
||||
$editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage(
|
||||
$submission->getId(),
|
||||
$reviewRound->getStageId()
|
||||
);
|
||||
$recipients = [];
|
||||
foreach ($editorsStageAssignments as $editorsStageAssignment) {
|
||||
$editor = Repo::user()->get($editorsStageAssignment->getUserId());
|
||||
// IF no prior notification exists
|
||||
// OR if editor has logged in after the last revision upload
|
||||
// OR the last upload and notification was sent more than a day ago,
|
||||
// THEN send a new notification
|
||||
if (is_null($lastNotification) || strtotime($editor->getDateLastLogin()) > $lastNotification || strtotime('-1 day') > $lastNotification) {
|
||||
$recipients[] = $editor;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($recipients)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mailable = new RevisedVersionNotify($context, $submission, $uploader, $reviewRound);
|
||||
$template = Repo::emailTemplate()->getByKey($context->getId(), RevisedVersionNotify::getEmailTemplateKey());
|
||||
$mailable->body($template->getLocalizedData('body'))
|
||||
->subject($template->getLocalizedData('subject'))
|
||||
->sender($user)
|
||||
->recipients($recipients)
|
||||
->replyTo($context->getData('contactEmail'), $context->getData('contactName'));
|
||||
|
||||
Mail::send($mailable);
|
||||
$submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /** @var SubmissionEmailLogDAO $submissionEmailLogDao */
|
||||
$submissionEmailLogDao->logMailable(
|
||||
SubmissionEmailLogEntry::SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION,
|
||||
$mailable,
|
||||
$submission,
|
||||
$user
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive data from the submission file to record in the event log
|
||||
*/
|
||||
protected function getSubmissionFileLogData(SubmissionFile $submissionFile): array
|
||||
{
|
||||
$user = $this->request->getUser();
|
||||
|
||||
return [
|
||||
'userId' => Validation::loggedInAs() ?: $user?->getId(),
|
||||
'fileStage' => $submissionFile->getData('fileStage'),
|
||||
'submissionFileId' => $submissionFile->getId(),
|
||||
'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'),
|
||||
'fileId' => $submissionFile->getData('fileId'),
|
||||
'submissionId' => $submissionFile->getData('submissionId'),
|
||||
'filename' => $submissionFile->getData('name'),
|
||||
'username' => $user?->getUsername(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/submissionFile/SubmissionFile.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 SubmissionFile
|
||||
*
|
||||
* @ingroup submission
|
||||
*
|
||||
* @brief Submission file class.
|
||||
*/
|
||||
|
||||
namespace PKP\submissionFile;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use PKP\facades\Locale;
|
||||
|
||||
/**
|
||||
* @extends \PKP\core\DataObject<DAO>
|
||||
*/
|
||||
class SubmissionFile extends \PKP\core\DataObject
|
||||
{
|
||||
// Define the file stage identifiers.
|
||||
public const SUBMISSION_FILE_SUBMISSION = 2;
|
||||
public const SUBMISSION_FILE_NOTE = 3;
|
||||
public const SUBMISSION_FILE_REVIEW_FILE = 4;
|
||||
public const SUBMISSION_FILE_REVIEW_ATTACHMENT = 5;
|
||||
public const SUBMISSION_FILE_FINAL = 6;
|
||||
public const SUBMISSION_FILE_COPYEDIT = 9;
|
||||
public const SUBMISSION_FILE_PROOF = 10;
|
||||
public const SUBMISSION_FILE_PRODUCTION_READY = 11;
|
||||
public const SUBMISSION_FILE_ATTACHMENT = 13;
|
||||
public const SUBMISSION_FILE_REVIEW_REVISION = 15;
|
||||
public const SUBMISSION_FILE_DEPENDENT = 17;
|
||||
public const SUBMISSION_FILE_QUERY = 18;
|
||||
public const SUBMISSION_FILE_INTERNAL_REVIEW_FILE = 19;
|
||||
public const SUBMISSION_FILE_INTERNAL_REVIEW_REVISION = 20;
|
||||
|
||||
public const INTERNAL_REVIEW_STAGES = [
|
||||
SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE,
|
||||
SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION,
|
||||
];
|
||||
|
||||
public const EXTERNAL_REVIEW_STAGES = [
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_FILE,
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the default/fall back locale the values should exist for
|
||||
*/
|
||||
public function getDefaultLocale(): ?string
|
||||
{
|
||||
return $this->getData('locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale of the submission.
|
||||
* This is not properly a property of the submission file
|
||||
* (e.g. it won't be persisted to the DB with the update function)
|
||||
* It helps solve submission locale requirement for file's multilingual metadata
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionLocale()
|
||||
{
|
||||
return $this->getData('locale');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale of the submission.
|
||||
* This is not properly a property of the submission file
|
||||
* (e.g. it won't be persisted to the DB with the update function)
|
||||
* It helps solve submission locale requirement for file's multilingual metadata
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param string $submissionLocale
|
||||
*/
|
||||
public function setSubmissionLocale($submissionLocale)
|
||||
{
|
||||
$this->setData('locale', $submissionLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored public ID of the file.
|
||||
*
|
||||
* @param string $pubIdType @literal 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>). @endliteral
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStoredPubId($pubIdType)
|
||||
{
|
||||
if ($pubIdType === 'doi') {
|
||||
return $this->getDoi();
|
||||
} else {
|
||||
return $this->getData('pub-id::' . $pubIdType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stored public ID of the file.
|
||||
*
|
||||
* @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' => Repo::submission()->get($this->getData('submissionId'))->getData('contextId')
|
||||
]
|
||||
);
|
||||
$doiId = Repo::doi()->add($newDoiObject);
|
||||
$this->setData('doiId', $doiId);
|
||||
}
|
||||
} else {
|
||||
$this->setData('pub-id::' . $pubIdType, $pubId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get price of submission file.
|
||||
* A null return indicates "not available"; 0 is free.
|
||||
*
|
||||
* @return float|null
|
||||
*/
|
||||
public function getDirectSalesPrice()
|
||||
{
|
||||
return $this->getData('directSalesPrice');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set direct sales price.
|
||||
* A null return indicates "not available"; 0 is free.
|
||||
*
|
||||
* @param float|null $directSalesPrice
|
||||
*/
|
||||
public function setDirectSalesPrice($directSalesPrice)
|
||||
{
|
||||
$this->setData('directSalesPrice', $directSalesPrice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sales type of submission file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSalesType()
|
||||
{
|
||||
return $this->getData('salesType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set sales type.
|
||||
*
|
||||
* @param string $salesType
|
||||
*/
|
||||
public function setSalesType($salesType)
|
||||
{
|
||||
$this->setData('salesType', $salesType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the genre id of this file (i.e. referring to Manuscript, Index, etc)
|
||||
* Foreign key into genres table
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param int $genreId
|
||||
*/
|
||||
public function setGenreId($genreId)
|
||||
{
|
||||
$this->setData('genreId', $genreId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the genre id of this file (i.e. referring to Manuscript, Index, etc)
|
||||
* Foreign key into genres table
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getGenreId()
|
||||
{
|
||||
return $this->getData('genreId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "best" file ID -- If a public ID is set,
|
||||
* use it; otherwise use the internal ID and revision.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBestId()
|
||||
{
|
||||
return strlen($publisherId = (string) $this->getStoredPubId('publisher-id')) ? $publisherId : $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file stage of the file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return int SubmissionFile::SUBMISSION_FILE_...
|
||||
*/
|
||||
public function getFileStage()
|
||||
{
|
||||
return $this->getData('fileStage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file stage of the file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param int $fileStage SubmissionFile::SUBMISSION_FILE_...
|
||||
*/
|
||||
public function setFileStage($fileStage)
|
||||
{
|
||||
$this->setData('fileStage', $fileStage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get modified date of file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
public function getDateModified()
|
||||
{
|
||||
return $this->getData('updatedAt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set modified date of file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param string $updatedAt
|
||||
*/
|
||||
|
||||
public function setDateModified($updatedAt)
|
||||
{
|
||||
return $this->setData('updatedAt', $updatedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get viewable.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getViewable()
|
||||
{
|
||||
return $this->getData('viewable');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set viewable.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param bool $viewable
|
||||
*/
|
||||
public function setViewable($viewable)
|
||||
{
|
||||
return $this->setData('viewable', $viewable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the uploader's user id.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param int $uploaderUserId
|
||||
*/
|
||||
public function setUploaderUserId($uploaderUserId)
|
||||
{
|
||||
$this->setData('uploaderUserId', $uploaderUserId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the uploader's user id.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getUploaderUserId()
|
||||
{
|
||||
return $this->getData('uploaderUserId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type that is associated with this file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getAssocType()
|
||||
{
|
||||
return $this->getData('assocType');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set type that is associated with this file.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param int $assocType
|
||||
*/
|
||||
public function setAssocType($assocType)
|
||||
{
|
||||
$this->setData('assocType', $assocType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submission chapter id.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getChapterId()
|
||||
{
|
||||
return $this->getData('chapterId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the submission chapter id.
|
||||
*
|
||||
* @deprecated 3.3.0.0
|
||||
*
|
||||
* @param int $chapterId
|
||||
*/
|
||||
public function setChapterId($chapterId)
|
||||
{
|
||||
$this->setData('chapterId', $chapterId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to fetch current DOI
|
||||
*
|
||||
*/
|
||||
public function getDoi(): ?string
|
||||
{
|
||||
$doiObject = $this->getData('doiObject');
|
||||
|
||||
if (empty($doiObject)) {
|
||||
return null;
|
||||
} else {
|
||||
return $doiObject->getData('doi');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc \PKP\core\DataObject::getDAO()
|
||||
*/
|
||||
public function getDAO(): DAO
|
||||
{
|
||||
return Repo::submissionFile()->dao;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\submissionFile\SubmissionFile', '\SubmissionFile');
|
||||
foreach ([
|
||||
'SUBMISSION_FILE_SUBMISSION',
|
||||
'SUBMISSION_FILE_NOTE',
|
||||
'SUBMISSION_FILE_REVIEW_FILE',
|
||||
'SUBMISSION_FILE_REVIEW_ATTACHMENT',
|
||||
'SUBMISSION_FILE_FINAL',
|
||||
'SUBMISSION_FILE_COPYEDIT',
|
||||
'SUBMISSION_FILE_PROOF',
|
||||
'SUBMISSION_FILE_PRODUCTION_READY',
|
||||
'SUBMISSION_FILE_ATTACHMENT',
|
||||
'SUBMISSION_FILE_REVIEW_REVISION',
|
||||
'SUBMISSION_FILE_DEPENDENT',
|
||||
'SUBMISSION_FILE_QUERY',
|
||||
'SUBMISSION_FILE_INTERNAL_REVIEW_FILE',
|
||||
'SUBMISSION_FILE_INTERNAL_REVIEW_REVISION',
|
||||
] as $constantName) {
|
||||
define($constantName, constant('\SubmissionFile::' . $constantName));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
/**
|
||||
* @file classes/submissionFile/maps/Schema.php
|
||||
*
|
||||
* Copyright (c) 2014-2020 Simon Fraser University
|
||||
* Copyright (c) 2000-2020 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class Schema
|
||||
*
|
||||
* @brief Map submissionFiles to the properties defined in the submission file schema
|
||||
*/
|
||||
|
||||
namespace PKP\submissionFile\maps;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use Illuminate\Support\Enumerable;
|
||||
use PKP\context\Context;
|
||||
use PKP\core\maps\Schema as BaseSchema;
|
||||
use PKP\services\PKPSchemaService;
|
||||
use PKP\submission\Genre;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class Schema extends BaseSchema
|
||||
{
|
||||
/** */
|
||||
public Enumerable $collection;
|
||||
|
||||
/** */
|
||||
public string $schema = PKPSchemaService::SCHEMA_SUBMISSION_FILE;
|
||||
|
||||
/** @var Genre[] File genres in this context */
|
||||
public array $genres;
|
||||
|
||||
public function __construct(Request $request, Context $context, PKPSchemaService $schemaService)
|
||||
{
|
||||
parent::__construct($request, $context, $schemaService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a submission file
|
||||
*
|
||||
* Includes all properties in the submission file schema.
|
||||
*
|
||||
* @param Genre[] $genres
|
||||
*/
|
||||
public function map(SubmissionFile $item, array $genres): array
|
||||
{
|
||||
$this->genres = $genres;
|
||||
return $this->mapByProperties($this->getProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a submission file
|
||||
*
|
||||
* Includes properties with the apiSummary flag in the submission file schema.
|
||||
*
|
||||
* @param Genre[] $genres
|
||||
*/
|
||||
public function summarize(SubmissionFile $item, array $genres): array
|
||||
{
|
||||
$this->genres = $genres;
|
||||
return $this->mapByProperties($this->getSummaryProps(), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a collection of submission files
|
||||
*
|
||||
* @see self::map
|
||||
*
|
||||
* @param Genre[] $genres
|
||||
*/
|
||||
public function mapMany(Enumerable $collection, array $genres): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) use ($genres) {
|
||||
return $this->map($item, $genres);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize a collection of submission files
|
||||
*
|
||||
* @see self::summarize
|
||||
*
|
||||
* @param Genre[] $genres
|
||||
*/
|
||||
public function summarizeMany(Enumerable $collection, array $genres): Enumerable
|
||||
{
|
||||
$this->collection = $collection;
|
||||
return $collection->map(function ($item) use ($genres) {
|
||||
return $this->summarize($item, $genres);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map schema properties of a submission file to an assoc array
|
||||
*/
|
||||
protected function mapByProperties(array $props, SubmissionFile $submissionFile): array
|
||||
{
|
||||
$output = [];
|
||||
foreach ($props as $prop) {
|
||||
if ($prop === '_href') {
|
||||
$output[$prop] = $this->getApiUrl(
|
||||
'submissions/' . $submissionFile->getData('submissionId') . '/files/' . $submissionFile->getId(),
|
||||
$this->context->getData('urlPath')
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'dependentFiles') {
|
||||
$dependentFiles = Repo::submissionFile()
|
||||
->getCollector()
|
||||
->filterByAssoc(Application::ASSOC_TYPE_SUBMISSION_FILE, [$submissionFile->getId()])
|
||||
->filterBySubmissionIds([$submissionFile->getData('submissionId')])
|
||||
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_DEPENDENT])
|
||||
->includeDependentFiles()
|
||||
->getMany();
|
||||
|
||||
$output[$prop] = $this->summarizeMany($dependentFiles, $this->genres)->values();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'documentType') {
|
||||
$output[$prop] = Services::get('file')->getDocumentType($submissionFile->getData('mimetype'));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'genreId') {
|
||||
$genre = $this->getGenre($submissionFile);
|
||||
$output[$prop] = $genre ? $genre->getId() : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'genreName') {
|
||||
$genre = $this->getGenre($submissionFile);
|
||||
$output[$prop] = $genre ? $genre->getName(null) : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'genreIsDependent') {
|
||||
$genre = $this->getGenre($submissionFile);
|
||||
$output[$prop] = $genre ? (bool) $genre->getDependent() : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'genreIsSupplementary') {
|
||||
$genre = $this->getGenre($submissionFile);
|
||||
$output[$prop] = $genre ? (bool) $genre->getSupplementary() : null;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'revisions') {
|
||||
$files = [];
|
||||
|
||||
$revisions = Repo::submissionFile()->getRevisions($submissionFile->getId());
|
||||
|
||||
foreach ($revisions as $revision) {
|
||||
if ($revision->fileId === $submissionFile->getData('fileId')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$files[] = [
|
||||
'documentType' => Services::get('file')->getDocumentType($revision->mimetype),
|
||||
'fileId' => $revision->fileId,
|
||||
'mimetype' => $revision->mimetype,
|
||||
'path' => $revision->path,
|
||||
'url' => $this->request->getDispatcher()->url(
|
||||
$this->request,
|
||||
Application::ROUTE_COMPONENT,
|
||||
$this->context->getData('urlPath'),
|
||||
'api.file.FileApiHandler',
|
||||
'downloadFile',
|
||||
null,
|
||||
[
|
||||
'fileId' => $revision->fileId,
|
||||
'submissionFileId' => $submissionFile->getId(),
|
||||
'submissionId' => $submissionFile->getData('submissionId'),
|
||||
'stageId' => Repo::submissionFile()->getWorkflowStageId($submissionFile),
|
||||
]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
$output[$prop] = $files;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'uploaderUserName') {
|
||||
$userId = $submissionFile->getData('uploaderUserId');
|
||||
$user = !is_null($userId) ? Repo::user()->get($userId) : null; // userId can be null, see pkp/pkp-lib#8493
|
||||
$output[$prop] = $user?->getUsername() ?? '';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($prop === 'url') {
|
||||
$output[$prop] = $this->request->getDispatcher()->url(
|
||||
$this->request,
|
||||
Application::ROUTE_COMPONENT,
|
||||
$this->context->getData('urlPath'),
|
||||
'api.file.FileApiHandler',
|
||||
'downloadFile',
|
||||
null,
|
||||
[
|
||||
'submissionFileId' => $submissionFile->getId(),
|
||||
'submissionId' => $submissionFile->getData('submissionId'),
|
||||
'stageId' => Repo::submissionFile()->getWorkflowStageId($submissionFile),
|
||||
]
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$output[$prop] = $submissionFile->getData($prop);
|
||||
}
|
||||
|
||||
$output = $this->schemaService->addMissingMultilingualValues(
|
||||
$this->schema,
|
||||
$output,
|
||||
$this->context->getSupportedFormLocales()
|
||||
);
|
||||
|
||||
ksort($output);
|
||||
|
||||
return $this->withExtensions($output, $submissionFile);
|
||||
}
|
||||
|
||||
protected function getGenre(SubmissionFile $submissionFile): ?Genre
|
||||
{
|
||||
foreach ($this->genres as $genre) {
|
||||
if ($genre->getId() === $submissionFile->getData('genreId')) {
|
||||
return $genre;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user