first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
+464
View File
@@ -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;
}
}
+490
View File
@@ -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')));
}
}
}
+723
View File
@@ -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));
}
}
+178
View File
@@ -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');
}
+92
View File
@@ -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);
}
+211
View File
@@ -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');
}
+229
View File
@@ -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');
}
+438
View File
@@ -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');
}
+323
View File
@@ -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;
}
}
+218
View File
@@ -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;
}
}