/** * indexes the files associated with a submission. * @param $submission Submission * @param $submissionFile SubmissionFile */ function indexSubmissionFiles($submission, $submissionFile) { // update the submission's search index if this submission is published. if ($submission->getDatePublished()) { import('classes.search.ArticleSearchIndex'); ArticleSearchIndex::articleFilesChanged($submission); } }
/** * Save the metadata and store the catalog data for this published * monograph. */ function execute($request) { parent::execute($request); $submission = $this->getSubmission(); $context = $request->getContext(); $waivePublicationFee = $request->getUserVar('waivePublicationFee') ? true : false; if ($waivePublicationFee) { $markAsPaid = $request->getUserVar('markAsPaid'); import('classes.payment.ojs.OJSPaymentManager'); $paymentManager = new OJSPaymentManager($request); $user = $request->getUser(); $queuedPayment =& $paymentManager->createQueuedPayment($context->getId(), PAYMENT_TYPE_PUBLICATION, $markAsPaid ? $submission->getUserId() : $user->getId(), $submission->getId(), $markAsPaid ? $context->getSetting('publicationFee') : 0, $markAsPaid ? $context->getSetting('currency') : ''); $queuedPaymentId = $paymentManager->queuePayment($queuedPayment); // Since this is a waiver, fulfill the payment immediately $paymentManager->fulfillQueuedPayment($request, $queuedPayment, $markAsPaid ? 'ManualPayment' : 'Waiver'); } else { // Get the issue for publication. $issueDao = DAORegistry::getDAO('IssueDAO'); $issueId = $this->getData('issueId'); $issue = $issueDao->getById($issueId, $context->getId()); $sectionDao = DAORegistry::getDAO('SectionDAO'); $sectionEditorSubmissionDao = DAORegistry::getDAO('SectionEditorSubmissionDAO'); $publishedArticleDao = DAORegistry::getDAO('PublishedArticleDAO'); $publishedArticle = $publishedArticleDao->getPublishedArticleByArticleId($submission->getId(), null, false); /* @var $publishedArticle PublishedArticle */ if ($publishedArticle) { if (!$issue || !$issue->getPublished()) { $fromIssue = $issueDao->getById($publishedArticle->getIssueId(), $context->getId()); if ($fromIssue->getPublished()) { // Insert article tombstone import('classes.article.ArticleTombstoneManager'); $articleTombstoneManager = new ArticleTombstoneManager(); $articleTombstoneManager->insertArticleTombstone($submission, $context); } } } import('classes.search.ArticleSearchIndex'); $articleSearchIndex = new ArticleSearchIndex(); // define the access status for the article if none is set. $accessStatus = $this->getData('accessStatus') != '' ? $this->getData('accessStatus') : ARTICLE_ACCESS_ISSUE_DEFAULT; $articleDao = DAORegistry::getDAO('ArticleDAO'); if (!is_null($this->getData('pages'))) { $submission->setPages($this->getData('pages')); } if (!is_null($this->getData('publicArticleId'))) { $articleDao->changePubId($submission->getId(), 'publisher-id', $this->getData('publicArticleId')); } if ($issue) { // Schedule against an issue. if ($publishedArticle) { $publishedArticle->setIssueId($issueId); $publishedArticle->setSeq(REALLY_BIG_NUMBER); $publishedArticle->setDatePublished($this->getData('datePublished')); $publishedArticle->setAccessStatus($accessStatus); $publishedArticleDao->updatePublishedArticle($publishedArticle); // Re-index the published article metadata. $articleSearchIndex->articleMetadataChanged($publishedArticle); } else { $publishedArticle = $publishedArticleDao->newDataObject(); $publishedArticle->setId($submission->getId()); $publishedArticle->setIssueId($issueId); $publishedArticle->setDatePublished(Core::getCurrentDate()); $publishedArticle->setSeq(REALLY_BIG_NUMBER); $publishedArticle->setAccessStatus($accessStatus); $publishedArticle->setDatePublished($this->getData('datePublished')); $publishedArticleDao->insertPublishedArticle($publishedArticle); // If we're using custom section ordering, and if this is the first // article published in a section, make sure we enter a custom ordering // for it. (Default at the end of the list.) if ($sectionDao->customSectionOrderingExists($issueId)) { if ($sectionDao->getCustomSectionOrder($issueId, $submission->getSectionId()) === null) { $sectionDao->insertCustomSectionOrder($issueId, $submission->getSectionId(), REALLY_BIG_NUMBER); $sectionDao->resequenceCustomSectionOrders($issueId); } } // Index the published article metadata and files for the first time. $articleSearchIndex->articleMetadataChanged($publishedArticle); $articleSearchIndex->articleFilesChanged($publishedArticle); } } else { if ($publishedArticle) { // This was published elsewhere; make sure we don't // mess up sequencing information. $issueId = $publishedArticle->getIssueId(); $publishedArticleDao->deletePublishedArticleByArticleId($ubmission->getId()); // Delete the article from the search index. $articleSearchIndex->articleFileDeleted($ubmission->getId()); } } // Resequence the articles. $publishedArticleDao->resequencePublishedArticles($submission->getSectionId(), $issueId); $submission->stampStatusModified(); $articleDao->updateObject($submission); if ($issue && $issue->getPublished()) { $submission->setStatus(STATUS_PUBLISHED); // delete article tombstone $tombstoneDao = DAORegistry::getDAO('DataObjectTombstoneDAO'); $tombstoneDao->deleteByDataObjectId($submission->getId()); } else { $submission->setStatus(STATUS_QUEUED); } $sectionEditorSubmission = $sectionEditorSubmissionDao->getSectionEditorSubmission($submission->getId()); if ($sectionEditorSubmission) { $sectionEditorSubmissionDao->updateSectionEditorSubmission($sectionEditorSubmission); } $articleSearchIndex->articleChangesFinished(); } }
/** * Schedule/unschedule an article for publication. * @param $args array * @param $request object */ function scheduleForPublication($args, $request) { $articleId = (int) array_shift($args); $issueId = (int) $request->getUserVar('issueId'); $this->validate($articleId, SECTION_EDITOR_ACCESS_EDIT); $journal =& $request->getJournal(); $submission =& $this->submission; $sectionEditorSubmissionDao =& DAORegistry::getDAO('SectionEditorSubmissionDAO'); $publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO'); $sectionDao =& DAORegistry::getDAO('SectionDAO'); $publishedArticle =& $publishedArticleDao->getPublishedArticleByArticleId($articleId); $issueDao =& DAORegistry::getDAO('IssueDAO'); $issue =& $issueDao->getIssueById($issueId, $journal->getId()); if ($publishedArticle) { if (!$issue || !$issue->getPublished()) { $fromIssue =& $issueDao->getIssueById($publishedArticle->getIssueId(), $journal->getId()); if ($fromIssue->getPublished()) { // Insert article tombstone import('classes.article.ArticleTombstoneManager'); $articleTombstoneManager = new ArticleTombstoneManager(); $articleTombstoneManager->insertArticleTombstone($submission, $journal); } } } import('classes.search.ArticleSearchIndex'); $articleSearchIndex = new ArticleSearchIndex(); if ($issue) { // Schedule against an issue. if ($publishedArticle) { $publishedArticle->setIssueId($issueId); $publishedArticle->setSeq(REALLY_BIG_NUMBER); $publishedArticleDao->updatePublishedArticle($publishedArticle); // Re-index the published article metadata. $articleSearchIndex->articleMetadataChanged($publishedArticle); } else { $publishedArticle = new PublishedArticle(); $publishedArticle->setId($submission->getId()); $publishedArticle->setIssueId($issueId); $publishedArticle->setDatePublished(Core::getCurrentDate()); $publishedArticle->setSeq(REALLY_BIG_NUMBER); $publishedArticle->setAccessStatus(ARTICLE_ACCESS_ISSUE_DEFAULT); $publishedArticleDao->insertPublishedArticle($publishedArticle); // If we're using custom section ordering, and if this is the first // article published in a section, make sure we enter a custom ordering // for it. (Default at the end of the list.) if ($sectionDao->customSectionOrderingExists($issueId)) { if ($sectionDao->getCustomSectionOrder($issueId, $submission->getSectionId()) === null) { $sectionDao->insertCustomSectionOrder($issueId, $submission->getSectionId(), REALLY_BIG_NUMBER); $sectionDao->resequenceCustomSectionOrders($issueId); } } // Index the published article metadata and files for the first time. $articleSearchIndex->articleMetadataChanged($publishedArticle); $articleSearchIndex->articleFilesChanged($publishedArticle); } } else { if ($publishedArticle) { // This was published elsewhere; make sure we don't // mess up sequencing information. $issueId = $publishedArticle->getIssueId(); $publishedArticleDao->deletePublishedArticleByArticleId($articleId); // Delete the article from the search index. $articleSearchIndex->articleFileDeleted($articleId); } } // Resequence the articles. $publishedArticleDao->resequencePublishedArticles($submission->getSectionId(), $issueId); $submission->stampStatusModified(); if ($issue && $issue->getPublished()) { $submission->setStatus(STATUS_PUBLISHED); // delete article tombstone $tombstoneDao =& DAORegistry::getDAO('DataObjectTombstoneDAO'); $tombstoneDao->deleteByDataObjectId($submission->getId()); } else { $submission->setStatus(STATUS_QUEUED); } $sectionEditorSubmissionDao->updateSectionEditorSubmission($submission); // Call initialize permissions again to check if copyright year needs to be initialized. $articleDao =& DAORegistry::getDAO('ArticleDAO'); $article = $articleDao->getArticle($articleId); $article->initializePermissions(); $articleDao->updateLocaleFields($article); $articleSearchIndex->articleChangesFinished(); $request->redirect(null, null, 'submissionEditing', array($articleId), null, 'scheduling'); }
function handleArticleNode(&$journal, &$articleNode, &$issue, &$section, &$article, &$publishedArticle, &$errors, &$user, $isCommandLine, &$dependentItems) { $errors = array(); $journalSupportedLocales = array_keys($journal->getSupportedLocaleNames()); // => journal locales must be set up before $publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO'); $articleDao =& DAORegistry::getDAO('ArticleDAO'); $article = new Article(); if ($locale = $articleNode->getAttribute('locale')) { $article->setLocale($locale); } else { $article->setLocale($journal->getPrimaryLocale()); } if (($value = $articleNode->getAttribute('public_id')) != '') { $anotherArticle = $publishedArticleDao->getPublishedArticleByPubId('publisher-id', $value, $journal->getId()); if ($anotherArticle) { $errors[] = array('plugins.importexport.native.import.error.duplicatePublicArticleId', array('articleTitle' => $article->getLocalizedTitle(), 'otherArticleTitle' => $anotherArticle->getLocalizedTitle())); $hasErrors = true; } else { $issue->setStoredPubId('publisher-id', $value); } } $article->setJournalId($journal->getId()); $article->setUserId($user->getId()); $article->setSectionId($section->getId()); $article->setStatus(STATUS_PUBLISHED); $article->setSubmissionProgress(0); $article->setDateSubmitted(Core::getCurrentDate()); $article->stampStatusModified(); $titleExists = false; for ($index = 0; $node = $articleNode->getChildByName('title', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleTitleLocaleUnsupported', array('articleTitle' => $node->getValue(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setTitle($node->getValue(), $locale); $titleExists = true; } if (!$titleExists || $article->getTitle($article->getLocale()) == '') { $errors[] = array('plugins.importexport.native.import.error.articleTitleMissing', array('issueTitle' => $issue->getIssueIdentification(), 'sectionTitle' => $section->getLocalizedTitle())); return false; } for ($index = 0; $node = $articleNode->getChildByName('abstract', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleAbstractLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setAbstract($node->getValue(), $locale); } if ($indexingNode = $articleNode->getChildByName('indexing')) { for ($index = 0; $node = $indexingNode->getChildByName('discipline', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleDisciplineLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setDiscipline($node->getValue(), $locale); } for ($index = 0; $node = $indexingNode->getChildByName('type', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleTypeLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setType($node->getValue(), $locale); } for ($index = 0; $node = $indexingNode->getChildByName('subject', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleSubjectLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setSubject($node->getValue(), $locale); } for ($index = 0; $node = $indexingNode->getChildByName('subject_class', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleSubjectClassLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setSubjectClass($node->getValue(), $locale); } if ($coverageNode = $indexingNode->getChildByName('coverage')) { for ($index = 0; $node = $coverageNode->getChildByName('geographical', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleCoverageGeoLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setCoverageGeo($node->getValue(), $locale); } for ($index = 0; $node = $coverageNode->getChildByName('chronological', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleCoverageChronLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setCoverageChron($node->getValue(), $locale); } for ($index = 0; $node = $coverageNode->getChildByName('sample', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleCoverageSampleLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setCoverageSample($node->getValue(), $locale); } } } for ($index = 0; $node = $articleNode->getChildByName('sponsor', $index); $index++) { $locale = $node->getAttribute('locale'); if ($locale == '') { $locale = $article->getLocale(); } elseif (!in_array($locale, $journalSupportedLocales)) { $errors[] = array('plugins.importexport.native.import.error.articleSponsorLocaleUnsupported', array('articleTitle' => $article->getLocalizedTitle(), 'issueTitle' => $issue->getIssueIdentification(), 'locale' => $locale)); return false; } $article->setSponsor($node->getValue(), $locale); } if ($node = $articleNode->getChildByName('pages')) { $article->setPages($node->getValue()); } if ($language = $articleNode->getAttribute('language')) { $article->setLanguage($language); } /* --- Handle covers --- */ $hasErrors = false; for ($index = 0; $node = $articleNode->getChildByName('cover', $index); $index++) { if (!NativeImportDom::handleArticleCoverNode($journal, $node, $article, $coverErrors, $isCommandLine)) { $errors = array_merge($errors, $coverErrors); $hasErrors = true; } } /* --- Set IDs --- */ if (!NativeImportDom::handlePubIds($articleNode, $article, $journal, $issue, $article, $errors)) { $hasErrors = true; } $articleDao->insertArticle($article); $dependentItems[] = array('article', $article); /* --- Handle authors --- */ for ($index = 0; $node = $articleNode->getChildByName('author', $index); $index++) { if (!NativeImportDom::handleAuthorNode($journal, $node, $issue, $section, $article, $authorErrors, $index)) { $errors = array_merge($errors, $authorErrors); $hasErrors = true; } } if ($hasErrors) { return false; } // Create submission mangement records $signoffDao =& DAORegistry::getDAO('SignoffDAO'); $initialCopyeditSignoff = $signoffDao->build('SIGNOFF_COPYEDITING_INITIAL', ASSOC_TYPE_ARTICLE, $article->getId()); $initialCopyeditSignoff->setUserId(0); $signoffDao->updateObject($initialCopyeditSignoff); $authorCopyeditSignoff = $signoffDao->build('SIGNOFF_COPYEDITING_AUTHOR', ASSOC_TYPE_ARTICLE, $article->getId()); $authorCopyeditSignoff->setUserId(0); $signoffDao->updateObject($authorCopyeditSignoff); $finalCopyeditSignoff = $signoffDao->build('SIGNOFF_COPYEDITING_FINAL', ASSOC_TYPE_ARTICLE, $article->getId()); $finalCopyeditSignoff->setUserId(0); $signoffDao->updateObject($finalCopyeditSignoff); $layoutSignoff = $signoffDao->build('SIGNOFF_LAYOUT', ASSOC_TYPE_ARTICLE, $article->getId()); $layoutSignoff->setUserId(0); $signoffDao->updateObject($layoutSignoff); $authorProofSignoff = $signoffDao->build('SIGNOFF_PROOFREADING_AUTHOR', ASSOC_TYPE_ARTICLE, $article->getId()); $authorProofSignoff->setUserId(0); $signoffDao->updateObject($authorProofSignoff); $proofreaderProofSignoff = $signoffDao->build('SIGNOFF_PROOFREADING_PROOFREADER', ASSOC_TYPE_ARTICLE, $article->getId()); $proofreaderProofSignoff->setUserId(0); $signoffDao->updateObject($proofreaderProofSignoff); $layoutProofSignoff = $signoffDao->build('SIGNOFF_PROOFREADING_LAYOUT', ASSOC_TYPE_ARTICLE, $article->getId()); $layoutProofSignoff->setUserId(0); $signoffDao->updateObject($layoutProofSignoff); // Log the import in the article event log. import('classes.article.log.ArticleLog'); ArticleLog::logEventHeadless($journal, $user->getId(), $article, ARTICLE_LOG_ARTICLE_IMPORT, 'log.imported', array('userName' => $user->getFullName(), 'articleId' => $article->getId())); // Insert published article entry. $publishedArticle = new PublishedArticle(); $publishedArticle->setId($article->getId()); $publishedArticle->setIssueId($issue->getId()); if ($node = $articleNode->getChildByName('date_published')) { $publishedDate = strtotime($node->getValue()); if ($publishedDate === -1) { $errors[] = array('plugins.importexport.native.import.error.invalidDate', array('value' => $node->getValue())); return false; } else { $publishedArticle->setDatePublished($publishedDate); } } $node = $articleNode->getChildByName('open_access'); $publishedArticle->setAccessStatus($node ? ARTICLE_ACCESS_OPEN : ARTICLE_ACCESS_ISSUE_DEFAULT); $publishedArticle->setSeq(REALLY_BIG_NUMBER); $publishedArticle->setViews(0); $publishedArticle->setPublishedArticleId($publishedArticleDao->insertPublishedArticle($publishedArticle)); $publishedArticleDao->resequencePublishedArticles($section->getId(), $issue->getId()); /* --- Galleys (html or otherwise handled simultaneously) --- */ import('classes.file.ArticleFileManager'); $articleFileManager = new ArticleFileManager($article->getId()); /* --- Handle galleys --- */ $hasErrors = false; $galleyCount = 0; for ($index = 0; $index < count($articleNode->children); $index++) { $node = $articleNode->children[$index]; if ($node->getName() == 'htmlgalley') { $isHtml = true; } elseif ($node->getName() == 'galley') { $isHtml = false; } else { continue; } if (!NativeImportDom::handleGalleyNode($journal, $node, $issue, $section, $article, $galleyErrors, $isCommandLine, $isHtml, $galleyCount, $articleFileManager)) { $errors = array_merge($errors, $galleyErrors); $hasErrors = true; } $galleyCount++; } if ($hasErrors) { return false; } /* --- Handle supplemental files --- */ $hasErrors = false; for ($index = 0; $node = $articleNode->getChildByName('supplemental_file', $index); $index++) { if (!NativeImportDom::handleSuppFileNode($journal, $node, $issue, $section, $article, $suppFileErrors, $isCommandLine, $articleFileManager)) { $errors = array_merge($errors, $suppFileErrors); $hasErrors = true; } } if ($hasErrors) { return false; } // Index the inserted article. import('classes.search.ArticleSearchIndex'); $articleSearchIndex = new ArticleSearchIndex(); $articleSearchIndex->articleMetadataChanged($article); $articleSearchIndex->articleFilesChanged($article); $articleSearchIndex->articleChangesFinished(); return true; }
/** * Approve a galley submission file. * @param $args array * @param $request PKPRequest */ function saveApproveProof($args, $request) { $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE); $submission = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION); // Make sure we only alter files associated with a galley. if ($submissionFile->getAssocType() !== ASSOC_TYPE_GALLEY) { fatalError('The requested file is not associated with any galley.'); } if ($submissionFile->getViewable()) { // No longer expose the file to readers. $submissionFile->setViewable(false); } else { // Expose the file to readers (e.g. via e-commerce). $submissionFile->setViewable(true); // Log the approve proof event. import('lib.pkp.classes.log.SubmissionLog'); import('classes.log.SubmissionEventLogEntry'); // constants $user = $request->getUser(); $articleGalleyDao = DAORegistry::getDAO('ArticleGalleyDAO'); $galley = $articleGalleyDao->getGalleyByBestGalleyId($submissionFile->getAssocId(), $submission->getId()); SubmissionLog::logEvent($request, $submission, SUBMISSION_LOG_PROOFS_APPROVED, 'submission.event.proofsApproved', array('formatName' => $galley->getLabel(), 'name' => $user->getFullName(), 'username' => $user->getUsername())); } $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); $submissionFileDao->updateObject($submissionFile); // update the submission's file index import('classes.search.ArticleSearchIndex'); ArticleSearchIndex::articleFilesChanged($submission); return DAO::getDataChangedEvent($submissionFile->getId()); }
/** * @covers ArticleSearchIndex */ public function testIndexArticleFilesViaPluginHook() { // Diverting to the search plugin hook. HookRegistry::register('ArticleSearchIndex::articleFilesChanged', array($this, 'callbackIndexArticleFiles')); // The file DAOs should not be called. $this->registerFileDAOs(false); // Simulate indexing via hook. $article = new Article(); $articleSearchIndex = new ArticleSearchIndex(); $articleSearchIndex->articleFilesChanged($article); // Test whether the hook was called. $calledHooks = HookRegistry::getCalledHooks(); $lastHook = array_pop($calledHooks); self::assertEquals('ArticleSearchIndex::articleFilesChanged', $lastHook[0]); // Remove the test hook. HookRegistry::clear('ArticleSearchIndex::articleFilesChanged'); }