[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
14082024
/
Data
/
htdocs
/
htdocs
/
jurnal-kesmas
/
pages
/
article
/
[
Home
]
File: ArticleHandler.php
<?php /** * @file pages/article/ArticleHandler.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 ArticleHandler * * @ingroup pages_article * * @brief Handle requests for article functions. * */ namespace APP\pages\article; use APP\core\Application; use APP\core\Services; use APP\facades\Repo; use APP\handler\Handler; use APP\issue\IssueAction; use APP\observers\events\UsageEvent; use APP\payment\ojs\OJSCompletedPaymentDAO; use APP\payment\ojs\OJSPaymentManager; use APP\security\authorization\OjsJournalMustPublishPolicy; use APP\submission\Submission; use APP\template\TemplateManager; use Firebase\JWT\JWT; use PKP\citation\CitationDAO; use PKP\config\Config; use PKP\core\PKPApplication; use PKP\db\DAORegistry; use PKP\plugins\Hook; use PKP\plugins\PluginRegistry; use PKP\security\authorization\ContextRequiredPolicy; use PKP\security\Validation; use PKP\submission\Genre; use PKP\submission\GenreDAO; use PKP\submission\PKPSubmission; use PKP\submissionFile\SubmissionFile; class ArticleHandler extends Handler { /** @var \APP\journal\Journal Context associated with the request */ public $context; /** @var ?\APP\issue\Issue Issue associated with the request */ public $issue; /** @var \APP\submission\Submission Submission associated with the request */ public $article; /** @var \PKP\category\Category Category associated with the request */ public $categories; /** @var \APP\publication\Publication Publication associated with the request */ public $publication; /** @var \PKP\galley\Galley galley associated with the request */ public $galley; /** @var int submissionFileId associated with the request */ public $submissionFileId; /** * @copydoc PKPHandler::authorize() */ public function authorize($request, &$args, $roleAssignments) { // Permit the use of the Authorization header and an API key for access to unpublished/subscription content if ($header = array_search('Authorization', array_flip(getallheaders()))) { [$bearer, $jwt] = explode(' ', $header); if (strcasecmp($bearer, 'Bearer') == 0 && !empty($jwt)) { $secret = Config::getVar('security', 'api_key_secret', ''); if (!$secret) { $templateMgr = TemplateManager::getManager($request); $templateMgr->assign('message', 'api.500.apiSecretKeyMissing'); return $templateMgr->display('frontend/pages/message.tpl'); } try { $apiToken = JWT::decode($jwt, $secret, ['HS256']); // Compatibility with old API keys // https://github.com/pkp/pkp-lib/issues/6462 if (substr($apiToken, 0, 2) === '""') { $apiToken = json_decode($apiToken); } $this->setApiToken($apiToken); } catch (\Exception $e) { $templateMgr = TemplateManager::getManager($request); $templateMgr->assign('message', 'api.400.invalidApiToken'); return $templateMgr->display('frontend/pages/message.tpl'); } } } $this->addPolicy(new ContextRequiredPolicy($request)); $this->addPolicy(new OjsJournalMustPublishPolicy($request)); return parent::authorize($request, $args, $roleAssignments); } /** * @see PKPHandler::initialize() * * @param \APP\core\Request $request * @param array $args Arguments list */ public function initialize($request, $args = []) { $urlPath = empty($args) ? 0 : array_shift($args); // Get the submission that matches the requested urlPath $submission = ctype_digit((string) $urlPath) ? Repo::submission()->get((int) $urlPath, $request->getContext()->getId()) : Repo::submission()->getByUrlPath($urlPath, $request->getContext()->getId()); $user = $request->getUser(); // Serve 404 if no submission available OR submission is unpublished and no user is logged in OR submission is unpublished and we have a user logged in but the user does not have access to preview if (!$submission || ($submission->getData('status') !== PKPSubmission::STATUS_PUBLISHED && !$user) || ($submission->getData('status') !== PKPSubmission::STATUS_PUBLISHED && $user && !Repo::submission()->canPreview($user, $submission))) { $request->getDispatcher()->handle404(); } // If the urlPath does not match the urlPath of the current // publication, redirect to the current URL $currentUrlPath = $submission->getBestId(); if ($currentUrlPath && $currentUrlPath != $urlPath) { $newArgs = array_merge([$currentUrlPath], $args); $request->redirect(null, $request->getRequestedPage(), $request->getRequestedOp(), $newArgs); } $this->article = $submission; // Get the requested publication or if none requested get the current publication $subPath = empty($args) ? 0 : array_shift($args); if ($subPath === 'version') { $publicationId = (int) array_shift($args); $galleyId = empty($args) ? 0 : array_shift($args); foreach ($this->article->getData('publications') as $publication) { if ($publication->getId() === $publicationId) { $this->publication = $publication; } } if (!$this->publication) { $request->getDispatcher()->handle404(); } } else { $this->publication = $this->article->getCurrentPublication(); $galleyId = $subPath; } if ($this->publication->getData('status') !== PKPSubmission::STATUS_PUBLISHED && !Repo::submission()->canPreview($user, $submission)) { $request->getDispatcher()->handle404(); } if ($galleyId && in_array($request->getRequestedOp(), ['view', 'download'])) { $galleys = $this->publication->getData('galleys'); foreach ($galleys as $galley) { if ($galley->getBestGalleyId() == $galleyId) { $this->galley = $galley; break; // In some cases, a URL to a galley may use the ID when it should use // the urlPath. Redirect to the galley's correct URL. } elseif (ctype_digit($galleyId) && $galley->getId() == $galleyId) { $request->redirect(null, $request->getRequestedPage(), $request->getRequestedOp(), [$submission->getBestId(), $galley->getBestGalleyId()]); } } // Redirect to the most recent version of the submission if the request // points to an outdated galley but doesn't use the specific versioned // URL. This can happen when a galley's urlPath is changed between versions. if (!$this->galley) { $publications = $submission->getPublishedPublications(); foreach ($publications as $publication) { foreach ($publication->getData('galleys') as $galley) { if ($galley->getBestGalleyId() == $galleyId) { $request->redirect(null, $request->getRequestedPage(), $request->getRequestedOp(), [$submission->getBestId()]); } } } $request->getDispatcher()->handle404(); } // Store the file id if it exists if (!empty($args)) { $this->submissionFileId = array_shift($args); } } if ($this->publication->getData('issueId')) { // TODO: Previously fetched issue from cache. Reimplement when caching added. $issue = Repo::issue()->get($this->publication->getData('issueId')); $issue = $issue->getJournalId() == $submission->getData('contextId') ? $issue : null; $this->issue = $issue; } } /** * View Article. (Either article landing page or galley view.) * * @param array $args * @param \APP\core\Request $request */ public function view($args, $request) { $context = $request->getContext(); $user = $request->getUser(); $issue = $this->issue; $article = $this->article; $publication = $this->publication; $templateMgr = TemplateManager::getManager($request); $templateMgr->assign([ 'issue' => $issue, 'article' => $article, 'publication' => $publication, 'currentPublication' => $article->getCurrentPublication(), 'galley' => $this->galley, 'fileId' => $this->submissionFileId, // DEPRECATED in 3.4.0: https://github.com/pkp/pkp-lib/issues/6545 'submissionFileId' => $this->submissionFileId, ]); $this->setupTemplate($request); // Get the earliest published publication $firstPublication = $article->getData('publications')->reduce(function ($a, $b) { return empty($a) || strtotime((string) $b->getData('datePublished')) < strtotime((string) $a->getData('datePublished')) ? $b : $a; }, 0); $templateMgr->assign([ 'firstPublication' => $firstPublication, ]); $templateMgr->assign([ 'ccLicenseBadge' => Application::get()->getCCLicenseBadge($publication->getData('licenseUrl')), 'publication' => $publication, 'section' => Repo::section()->get($publication->getData('sectionId')), ]); if ($this->galley && !$this->userCanViewGalley($request, $article->getId(), $this->galley->getId())) { fatalError('Cannot view galley.'); } $templateMgr->assign([ 'categories' => Repo::category()->getCollector() ->filterByPublicationIds([$publication->getId()]) ->getMany() ->toArray() ]); // Get galleys sorted into primary and supplementary groups $galleys = $publication->getData('galleys'); $primaryGalleys = []; $supplementaryGalleys = []; if ($galleys) { $genreDao = DAORegistry::getDAO('GenreDAO'); /** @var GenreDAO $genreDao */ $primaryGenres = $genreDao->getPrimaryByContextId($context->getId())->toArray(); $primaryGenreIds = array_map(function ($genre) { return $genre->getId(); }, $primaryGenres); $supplementaryGenres = $genreDao->getBySupplementaryAndContextId(true, $context->getId())->toArray(); $supplementaryGenreIds = array_map(function ($genre) { return $genre->getId(); }, $supplementaryGenres); foreach ($galleys as $galley) { $remoteUrl = $galley->getRemoteURL(); $file = Repo::submissionFile()->get((int) $galley->getData('submissionFileId')); if (!$remoteUrl && !$file) { continue; } if ($remoteUrl || in_array($file->getGenreId(), $primaryGenreIds)) { $primaryGalleys[] = $galley; } elseif (in_array($file->getGenreId(), $supplementaryGenreIds)) { $supplementaryGalleys[] = $galley; } } } $templateMgr->assign([ 'primaryGalleys' => $primaryGalleys, 'supplementaryGalleys' => $supplementaryGalleys, 'userGroupsById' => Repo::userGroup()->getCollector()->filterByPublicationIds([$this->publication->getId()])->getMany()->toArray() ]); // Citations if ($publication->getData('citationsRaw')) { $citationDao = DAORegistry::getDAO('CitationDAO'); /** @var CitationDAO $citationDao */ $parsedCitations = $citationDao->getByPublicationId($publication->getId()); $templateMgr->assign([ 'parsedCitations' => $parsedCitations->toArray(), ]); } // Assign deprecated values to the template manager for // compatibility with older themes $templateMgr->assign([ 'licenseTerms' => $context->getLocalizedData('licenseTerms'), 'licenseUrl' => $publication->getData('licenseUrl'), 'copyrightHolder' => $publication->getLocalizedData('copyrightHolder'), 'copyrightYear' => $publication->getData('copyrightYear'), 'pubIdPlugins' => PluginRegistry::loadCategory('pubIds', true), 'keywords' => $publication->getData('keywords'), ]); // Fetch and assign the galley to the template if ($this->galley && $this->galley->getRemoteURL()) { $request->redirectUrl($this->galley->getRemoteURL()); } if (empty($this->galley)) { // No galley: Prepare the article landing page. // Ask robots not to index outdated versions and point to the canonical url for the latest version if ($publication->getId() !== $article->getCurrentPublication()->getId()) { $templateMgr->addHeader('noindex', '<meta name="robots" content="noindex">'); $url = $request->getDispatcher()->url($request, PKPApplication::ROUTE_PAGE, null, 'article', 'view', $article->getBestId()); $templateMgr->addHeader('canonical', '<link rel="canonical" href="' . $url . '">'); } // Get the subscription status if displaying the abstract; // if access is open, we can display links to the full text. // The issue may not exist, if this is an editorial user // and scheduling hasn't been completed yet for the article. $issueAction = new IssueAction(); $subscriptionRequired = false; if ($issue) { $subscriptionRequired = $issueAction->subscriptionRequired($issue, $context); } $subscribedUser = $issueAction->subscribedUser($user, $context, isset($issue) ? $issue->getId() : null, isset($article) ? $article->getId() : null); $subscribedDomain = $issueAction->subscribedDomain($request, $context, isset($issue) ? $issue->getId() : null, isset($article) ? $article->getId() : null); $completedPaymentDao = DAORegistry::getDAO('OJSCompletedPaymentDAO'); /** @var OJSCompletedPaymentDAO $completedPaymentDao */ $templateMgr->assign( 'hasAccess', !$subscriptionRequired || $publication->getData('accessStatus') == Submission::ARTICLE_ACCESS_OPEN || $subscribedUser || $subscribedDomain || ($user && $issue && $completedPaymentDao->hasPaidPurchaseIssue($user->getId(), $issue->getId())) || ($user && $completedPaymentDao->hasPaidPurchaseArticle($user->getId(), $article->getId())) ); $paymentManager = Application::get()->getPaymentManager($context); if ($paymentManager->onlyPdfEnabled()) { $templateMgr->assign('restrictOnlyPdf', true); } if ($paymentManager->purchaseArticleEnabled()) { $templateMgr->assign('purchaseArticleEnabled', true); } if (!Hook::call('ArticleHandler::view', [&$request, &$issue, &$article, $publication])) { $templateMgr->display('frontend/pages/article.tpl'); event(new UsageEvent(Application::ASSOC_TYPE_SUBMISSION, $context, $article, null, null, $this->issue)); return; } } else { // Ask robots not to index outdated versions if ($publication->getId() !== $article->getCurrentPublication()->getId()) { $templateMgr->addHeader('noindex', '<meta name="robots" content="noindex">'); } // Galley: Prepare the galley file download. if (!Hook::call('ArticleHandler::view::galley', [&$request, &$issue, &$this->galley, &$article, $publication])) { if ($this->publication->getId() !== $this->article->getCurrentPublication()->getId()) { $redirectPath = [ $article->getBestId(), 'version', $publication->getId(), $this->galley->getBestGalleyId() ]; } else { $redirectPath = [ $article->getBestId(), $this->galley->getBestGalleyId() ]; } $request->redirect(null, null, 'download', $redirectPath); } } } /** * Download an article file * For deprecated OJS 2.x URLs; see https://github.com/pkp/pkp-lib/issues/1541 * * @param array $args * @param \APP\core\Request $request */ public function viewFile($args, $request) { $articleId = $args[0] ?? 0; $galleyId = $args[1] ?? 0; $submissionFileId = isset($args[2]) ? (int) $args[2] : 0; header('HTTP/1.1 301 Moved Permanently'); $request->redirect(null, null, 'download', [$articleId, $galleyId, $submissionFileId]); } /** * Download a supplementary file. * For deprecated OJS 2.x URLs; see https://github.com/pkp/pkp-lib/issues/1541 * * @param array $args * @param \APP\core\Request $request */ public function downloadSuppFile($args, $request) { $articleId = $args[0] ?? 0; $article = Repo::submission()->get($articleId); if (!$article) { $dispatcher = $request->getDispatcher(); $dispatcher->handle404(); } $suppId = $args[1] ?? 0; $submissionFiles = Repo::submissionFile() ->getCollector() ->filterBySubmissionIds([$article->getId()]) ->getMany(); foreach ($submissionFiles as $submissionFile) { if ($submissionFile->getData('old-supp-id') == $suppId) { $articleGalleys = Repo::galley()->getCollector() ->filterByPublicationIds([$article->getCurrentPublication()->getId()]) ->getMany(); foreach ($articleGalleys as $articleGalley) { $galleyFile = Repo::submissionFile()->get($articleGalley->getData('submissionFileId')); if ($galleyFile && $galleyFile->getData('submissionFileId') == $submissionFile->getId()) { header('HTTP/1.1 301 Moved Permanently'); $request->redirect(null, null, 'download', [$articleId, $articleGalley->getId(), $submissionFile->getId()]); } } } } $dispatcher = $request->getDispatcher(); $dispatcher->handle404(); } /** * Download an article file * * @param array $args * @param \APP\core\Request $request */ public function download($args, $request) { if (!isset($this->galley)) { $request->getDispatcher()->handle404(); } if ($this->galley->getRemoteURL()) { $request->redirectUrl($this->galley->getRemoteURL()); } elseif ($this->userCanViewGalley($request, $this->article->getId(), $this->galley->getId())) { if (!$this->submissionFileId) { $this->submissionFileId = $this->galley->getData('submissionFileId'); } // If no file ID could be determined, treat it as a 404. if (!$this->submissionFileId) { $request->getDispatcher()->handle404(); } // If the file ID is not the galley's file ID, ensure it is a dependent file, or else 404. if ($this->submissionFileId != $this->galley->getData('submissionFileId')) { $dependentFileIds = Repo::submissionFile() ->getCollector() ->filterByAssoc( Application::ASSOC_TYPE_SUBMISSION_FILE, [$this->galley->getData('submissionFileId')] ) ->filterByFileStages([SubmissionFile::SUBMISSION_FILE_DEPENDENT]) ->includeDependentFiles() ->getIds() ->toArray(); if (!in_array($this->submissionFileId, $dependentFileIds)) { $request->getDispatcher()->handle404(); } } if (!Hook::call('ArticleHandler::download', [$this->article, &$this->galley, &$this->submissionFileId])) { $submissionFile = Repo::submissionFile()->get($this->submissionFileId); if (!Services::get('file')->fs->has($submissionFile->getData('path'))) { $request->getDispatcher()->handle404(); } $filename = Services::get('file')->formatFilename($submissionFile->getData('path'), $submissionFile->getLocalizedData('name')); // if the file is a galley file (i.e. not a dependent file e.g. CSS or images), fire an usage event. if ($this->galley->getData('submissionFileId') == $this->submissionFileId) { $assocType = Application::ASSOC_TYPE_SUBMISSION_FILE; /** @var GenreDAO */ $genreDao = DAORegistry::getDAO('GenreDAO'); $genre = $genreDao->getById($submissionFile->getData('genreId')); // TO-DO: is this correct ? if ($genre->getCategory() != Genre::GENRE_CATEGORY_DOCUMENT || $genre->getSupplementary() || $genre->getDependent()) { $assocType = Application::ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER; } event(new UsageEvent($assocType, $request->getContext(), $this->article, $this->galley, $submissionFile, $this->issue)); } $returner = true; Hook::call('FileManager::downloadFileFinished', [&$returner]); Services::get('file')->download($submissionFile->getData('fileId'), $filename); } } else { header('HTTP/1.0 403 Forbidden'); echo '403 Forbidden<br>'; } } /** * Determines whether a user can view this article galley or not. * * @param \APP\core\Request $request * @param string $articleId * @param int|string $galleyId */ public function userCanViewGalley($request, $articleId, $galleyId = null) { $issueAction = new IssueAction(); $context = $request->getContext(); $submission = $this->article; $issue = $this->issue; $contextId = $context->getId(); $user = $request->getUser(); $userId = $user ? $user->getId() : 0; // If this is an editorial user who can view unpublished/unscheduled // articles, bypass further validation. Likewise for its author. if ($submission && $user && Repo::submission()->canPreview($user, $submission)) { return true; } // Make sure the reader has rights to view the article/issue. if ($issue && $issue->getPublished() && $submission->getStatus() == PKPSubmission::STATUS_PUBLISHED) { $subscriptionRequired = $issueAction->subscriptionRequired($issue, $context); $isSubscribedDomain = $issueAction->subscribedDomain($request, $context, $issue->getId(), $submission->getId()); // Check if login is required for viewing. if (!$isSubscribedDomain && !Validation::isLoggedIn() && $context->getData('restrictArticleAccess') && isset($galleyId) && $galleyId) { Validation::redirectLogin(); } // bypass all validation if subscription based on domain or ip is valid // or if the user is just requesting the abstract if ((!$isSubscribedDomain && $subscriptionRequired) && (isset($galleyId) && $galleyId)) { // Subscription Access $subscribedUser = $issueAction->subscribedUser($user, $context, $issue->getId(), $submission->getId()); $paymentManager = Application::get()->getPaymentManager($context); $purchasedIssue = false; if (!$subscribedUser && $paymentManager->purchaseIssueEnabled()) { $completedPaymentDao = DAORegistry::getDAO('OJSCompletedPaymentDAO'); /** @var OJSCompletedPaymentDAO $completedPaymentDao */ $purchasedIssue = $completedPaymentDao->hasPaidPurchaseIssue($userId, $issue->getId()); } if (!(!$subscriptionRequired || $submission->getCurrentPublication()->getData('accessStatus') == Submission::ARTICLE_ACCESS_OPEN || $subscribedUser || $purchasedIssue)) { if ($paymentManager->purchaseArticleEnabled() || $paymentManager->membershipEnabled()) { /* if only pdf files are being restricted, then approve all non-pdf galleys * and continue checking if it is a pdf galley */ if ($paymentManager->onlyPdfEnabled()) { if ($this->galley && !$this->galley->isPdfGalley()) { $this->issue = $issue; $this->article = $submission; return true; } } if (!Validation::isLoggedIn()) { Validation::redirectLogin('payment.loginRequired.forArticle'); } /* if the article has been paid for then forget about everything else * and just let them access the article */ $completedPaymentDao = DAORegistry::getDAO('OJSCompletedPaymentDAO'); /** @var OJSCompletedPaymentDAO $completedPaymentDao */ $dateEndMembership = $user->getData('dateEndMembership', 0); if ($completedPaymentDao->hasPaidPurchaseArticle($userId, $submission->getId()) || (!is_null($dateEndMembership) && $dateEndMembership > time())) { $this->issue = $issue; $this->article = $submission; return true; } elseif ($paymentManager->purchaseArticleEnabled()) { $queuedPayment = $paymentManager->createQueuedPayment($request, OJSPaymentManager::PAYMENT_TYPE_PURCHASE_ARTICLE, $user->getId(), $submission->getId(), $context->getData('purchaseArticleFee')); $paymentManager->queuePayment($queuedPayment); $paymentForm = $paymentManager->getPaymentForm($queuedPayment); $paymentForm->display($request); exit; } } if (!isset($galleyId) || $galleyId) { if (!Validation::isLoggedIn()) { Validation::redirectLogin('reader.subscriptionRequiredLoginText'); } $request->redirect(null, 'about', 'subscriptions'); } } } } else { $request->redirect(null, 'search'); } return true; } }