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
+126
View File
@@ -0,0 +1,126 @@
<?php
/**
* @file pages/about/AboutContextHandler.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 AboutContextHandler
*
* @ingroup pages_about
*
* @brief Handle requests for context-level about functions.
*/
namespace PKP\pages\about;
use APP\core\Application;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\Role;
class AboutContextHandler extends Handler
{
/**
* @see PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$context = $request->getContext();
if (!$context || !$context->getData('restrictSiteAccess')) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setCacheability(TemplateManager::CACHEABILITY_PUBLIC);
}
$this->addPolicy(new ContextRequiredPolicy($request));
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Display about page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function index($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$templateMgr->display('frontend/pages/about.tpl');
}
/**
* Display editorialTeam page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function editorialTeam($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$templateMgr->display('frontend/pages/editorialTeam.tpl');
}
/**
* Display submissions page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function submissions($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$context = $request->getContext();
$this->setupTemplate($request);
$templateMgr->assign('submissionChecklist', $context->getLocalizedData('submissionChecklist'));
// Get sections for this context
$canSubmitAll = false;
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
if ($userRoles && !empty(array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR], $userRoles))) {
$canSubmitAll = true;
}
$sections = Repo::section()
->getCollector()
->filterByContextIds([$context->getId()])
->excludeEditorOnly(!$canSubmitAll)
->getMany()
->all();
$templateMgr->assign('sections', $sections);
$templateMgr->display('frontend/pages/submissions.tpl');
}
/**
* Display contact page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function contact($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$templateMgr->assign([
'mailingAddress' => $context->getData('mailingAddress'),
'contactPhone' => $context->getData('contactPhone'),
'contactEmail' => $context->getData('contactEmail'),
'contactName' => $context->getData('contactName'),
'supportName' => $context->getData('supportName'),
'supportPhone' => $context->getData('supportPhone'),
'supportEmail' => $context->getData('supportEmail'),
'contactTitle' => $context->getLocalizedData('contactTitle'),
'contactAffiliation' => $context->getLocalizedData('contactAffiliation'),
]);
$templateMgr->display('frontend/pages/contact.tpl');
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/**
* @file pages/about/AboutSiteHandler.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 AboutSiteHandler
*
* @ingroup pages_about
*
* @brief Handle requests for site-wide about functions.
*/
namespace PKP\pages\about;
use APP\core\Application;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\config\Config;
use PKP\core\PKPApplication;
use PKP\db\DAORegistry;
use PKP\site\VersionDAO;
class AboutSiteHandler extends Handler
{
/**
* Display aboutThisPublishingSystem page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function aboutThisPublishingSystem($args, $request)
{
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
$version = $versionDao->getCurrentVersion();
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'appVersion' => $version->getVersionString(false),
'contactUrl' => $request->getDispatcher()->url(
Application::get()->getRequest(),
PKPApplication::ROUTE_PAGE,
null,
'about',
'contact'
),
]);
$templateMgr->display('frontend/pages/aboutThisPublishingSystem.tpl');
}
/**
* Display privacy policy page.
*
* @param array $args
* @param \PKP\core\PKPRequest $request
*/
public function privacy($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$enableSiteWidePrivacyStatement = Config::getVar('general', 'sitewide_privacy_statement');
if (!$enableSiteWidePrivacyStatement && $context) {
$privacyStatement = $context->getLocalizedData('privacyStatement');
} else {
$privacyStatement = $request->getSite()->getLocalizedData('privacyStatement');
}
if (!$privacyStatement) {
$dispatcher = $this->getDispatcher();
$dispatcher->handle404();
}
$templateMgr->assign('privacyStatement', $privacyStatement);
$templateMgr->display('frontend/pages/privacy.tpl');
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
/**
* @defgroup pages_about About page
*/
/**
* @file pages/about/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_about
*
* @brief Handle requests for about the context functions.
*
*/
switch ($op) {
case 'index':
case 'editorialTeam':
case 'submissions':
case 'contact':
return new \PKP\pages\about\AboutContextHandler();
case 'privacy':
case 'aboutThisPublishingSystem':
return new \PKP\pages\about\AboutSiteHandler();
}
+809
View File
@@ -0,0 +1,809 @@
<?php
/**
* @file pages/admin/AdminHandler.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 AdminHandler
*
* @ingroup pages_admin
*
* @brief Handle requests for site administration functions.
*/
namespace PKP\pages\admin;
use APP\core\Application;
use APP\core\Services;
use APP\facades\Repo;
use APP\file\PublicFileManager;
use APP\handler\Handler;
use APP\template\TemplateManager;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use PDO;
use PKP\announcement\Collector;
use PKP\cache\CacheManager;
use PKP\components\forms\highlight\HighlightForm;
use PKP\components\listPanels\HighlightsListPanel;
use PKP\components\forms\announcement\PKPAnnouncementForm;
use PKP\components\forms\context\PKPAnnouncementSettingsForm;
use PKP\components\listPanels\PKPAnnouncementsListPanel;
use PKP\config\Config;
use PKP\core\JSONMessage;
use PKP\core\PKPContainer;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\highlight\Collector as HighlightCollector;
use PKP\job\resources\HttpFailedJobResource;
use PKP\scheduledTask\ScheduledTaskHelper;
use PKP\security\authorization\PKPSiteAccessPolicy;
use PKP\security\Role;
use PKP\session\SessionDAO;
use PKP\site\VersionCheck;
use PKP\site\VersionDAO;
class AdminHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_SITE_ADMIN],
[
'index',
'contexts',
'settings',
'wizard',
'systemInfo',
'phpinfo',
'expireSessions',
'clearTemplateCache',
'clearDataCache',
'downloadScheduledTaskLogFile',
'clearScheduledTaskLogFiles',
'jobs',
'failedJobs',
'failedJobDetails',
]
);
}
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new PKPSiteAccessPolicy($request, null, $roleAssignments));
$returner = parent::authorize($request, $args, $roleAssignments);
// Admin shouldn't access this page from a specific context
if ($request->getContext()) {
return false;
}
return $returner;
}
/**
* @copydoc PKPHandler::initialize()
*/
public function initialize($request)
{
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageComponent' => 'AdminPage',
]);
if ($request->getRequestedOp() !== 'index') {
$router = $request->getRouter();
$templateMgr->assign([
'breadcrumbs' => [
[
'id' => 'admin',
'url' => $router->url($request, 'index', 'admin'),
'name' => __('navigation.admin'),
]
]
]);
}
// Interact with the beacon (if enabled) and determine if a new version exists
$latestVersion = VersionCheck::checkIfNewVersionExists();
// Display a warning message if there is a new application version available
if (Config::getVar('general', 'show_upgrade_warning') && $latestVersion) {
$currentVersion = VersionCheck::getCurrentDBVersion();
$templateMgr->assign([
'newVersionAvailable' => true,
'currentVersion' => $currentVersion,
'latestVersion' => $latestVersion,
]);
}
return parent::initialize($request);
}
/**
* Display site admin index page.
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('admin.siteAdmin'),
]);
$templateMgr->display('admin/index.tpl');
}
/**
* Display a list of the contexts hosted on the site.
*
* @param array $args
* @param PKPRequest $request
*/
public function contexts($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'contexts',
'name' => __('admin.hostedContexts'),
];
$templateMgr->assign([
'breadcrumbs' => $breadcrumbs,
'pageTitle' => __('admin.hostedContexts'),
]);
$templateMgr->display('admin/contexts.tpl');
}
/**
* Display the administration settings page.
*
* @param array $args
* @param PKPRequest $request
*/
public function settings($args, $request)
{
$this->setupTemplate($request);
$site = $request->getSite();
$dispatcher = $request->getDispatcher();
$apiUrl = $dispatcher->url($request, Application::ROUTE_API, Application::CONTEXT_ID_ALL, 'site');
$themeApiUrl = $dispatcher->url($request, Application::ROUTE_API, Application::CONTEXT_ID_ALL, 'site/theme');
$temporaryFileApiUrl = $dispatcher->url($request, Application::ROUTE_API, Application::CONTEXT_ID_ALL, 'temporaryFiles');
$announcementsApiUrl = $dispatcher->url($request, Application::ROUTE_API, Application::CONTEXT_ID_ALL, 'announcements');
$publicFileManager = new PublicFileManager();
$baseUrl = $request->getBaseUrl() . '/' . $publicFileManager->getSiteFilesPath();
$locales = $site->getSupportedLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
$contexts = Services::get('context')->getManySummary();
$siteAppearanceForm = new \PKP\components\forms\site\PKPSiteAppearanceForm($apiUrl, $locales, $site, $baseUrl, $temporaryFileApiUrl);
$siteConfigForm = new \PKP\components\forms\site\PKPSiteConfigForm($apiUrl, $locales, $site);
$siteInformationForm = new \PKP\components\forms\site\PKPSiteInformationForm($apiUrl, $locales, $site);
$siteBulkEmailsForm = new \PKP\components\forms\site\PKPSiteBulkEmailsForm($apiUrl, $site, $contexts);
$themeForm = new \PKP\components\forms\context\PKPThemeForm($themeApiUrl, $locales);
$siteStatisticsForm = new \PKP\components\forms\site\PKPSiteStatisticsForm($apiUrl, $locales, $site);
$highlightsListPanel = $this->getHighlightsListPanel();
$announcementSettingsForm = new PKPAnnouncementSettingsForm($apiUrl, $locales, $site);
$announcementsForm = new PKPAnnouncementForm($announcementsApiUrl, $locales, Repo::announcement()->getFileUploadBaseUrl(), $temporaryFileApiUrl);
$announcementsListPanel = $this->getAnnouncementsListPanel($announcementsApiUrl, $announcementsForm);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setConstants([
'FORM_ANNOUNCEMENT_SETTINGS' => FORM_ANNOUNCEMENT_SETTINGS,
]);
$templateMgr->setState([
'announcementsEnabled' => (bool) $site->getData('enableAnnouncements'),
'components' => [
$announcementsListPanel->id => $announcementsListPanel->getConfig(),
FORM_SITE_APPEARANCE => $siteAppearanceForm->getConfig(),
FORM_SITE_CONFIG => $siteConfigForm->getConfig(),
FORM_SITE_INFO => $siteInformationForm->getConfig(),
FORM_SITE_BULK_EMAILS => $siteBulkEmailsForm->getConfig(),
FORM_THEME => $themeForm->getConfig(),
FORM_SITE_STATISTICS => $siteStatisticsForm->getConfig(),
$highlightsListPanel->id => $highlightsListPanel->getConfig(),
FORM_ANNOUNCEMENT_SETTINGS => $announcementSettingsForm->getConfig(),
],
]);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'settings',
'name' => __('admin.siteSettings'),
];
$templateMgr->assign([
'breadcrumbs' => $breadcrumbs,
'pageTitle' => __('admin.siteSettings'),
'componentAvailability' => $this->siteSettingsAvailability($request),
]);
$templateMgr->display('admin/settings.tpl');
}
/**
* Business logic for site settings single/multiple contexts availability
*
* @param PKPRequest $request
*
* @return array [siteComponent, availability (bool)]
*/
private function siteSettingsAvailability($request)
{
$tabsSingleContextAvailability = [
'siteSetup',
'languages',
'bulkEmails',
'statistics',
];
$tabs = [
'siteSetup',
'siteAppearance',
'sitePlugins',
'siteConfig',
'siteInfo',
'languages',
'navigationMenus',
'highlights',
'bulkEmails',
'siteTheme',
'siteAppearanceSetup',
'statistics',
'announcements',
];
if (!Config::getVar('features', 'site_announcements')) {
$tabs = array_filter(
$tabs,
function($tab) { return $tab !== 'announcements'; }
);
}
$singleContextSite = (Services::get('context')->getCount() == 1);
$tabsAvailability = [];
foreach ($tabs as $tab) {
$tabsAvailability[$tab] = true;
if ($singleContextSite && !in_array($tab, $tabsSingleContextAvailability)) {
$tabsAvailability[$tab] = false;
}
}
return $tabsAvailability;
}
/**
* Display a settings wizard for a journal
*
* @param array $args
* @param PKPRequest $request
*/
public function wizard($args, $request)
{
$this->setupTemplate($request);
$router = $request->getRouter();
$dispatcher = $request->getDispatcher();
if (!isset($args[0]) || !ctype_digit((string) $args[0])) {
$request->getDispatcher()->handle404();
}
$contextService = Services::get('context');
$context = $contextService->get((int) $args[0]);
if (empty($context)) {
$request->getDispatcher()->handle404();
}
$apiUrl = $dispatcher->url($request, Application::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId());
$themeApiUrl = $dispatcher->url($request, Application::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId() . '/theme');
$sitemapUrl = $router->url($request, $context->getPath(), 'sitemap');
$locales = $context->getSupportedFormLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
$contextForm = new \APP\components\forms\context\ContextForm($apiUrl, $locales, $request->getBaseUrl(), $context);
$themeForm = new \PKP\components\forms\context\PKPThemeForm($themeApiUrl, $locales, $context);
$indexingForm = new \PKP\components\forms\context\PKPSearchIndexingForm($apiUrl, $locales, $context, $sitemapUrl);
$components = [
FORM_CONTEXT => $contextForm->getConfig(),
FORM_SEARCH_INDEXING => $indexingForm->getConfig(),
FORM_THEME => $themeForm->getConfig(),
];
$bulkEmailsEnabled = in_array($context->getId(), (array) $request->getSite()->getData('enableBulkEmails'));
if ($bulkEmailsEnabled) {
$userGroups = Repo::userGroup()->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
$restrictBulkEmailsForm = new \PKP\components\forms\context\PKPRestrictBulkEmailsForm($apiUrl, $context, $userGroups);
$components[$restrictBulkEmailsForm->id] = $restrictBulkEmailsForm->getConfig();
}
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'components' => $components,
]);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'contexts',
'name' => __('admin.hostedContexts'),
'url' => $router->url($request, 'index', 'admin', 'contexts'),
];
$breadcrumbs[] = [
'id' => 'wizard',
'name' => __('manager.settings.wizard'),
];
$templateMgr->assign([
'breadcrumbs' => $breadcrumbs,
'bulkEmailsEnabled' => $bulkEmailsEnabled,
'editContext' => $context,
'pageTitle' => __('manager.settings.wizard'),
]);
$templateMgr->display('admin/contextSettings.tpl');
}
/**
* Show system information summary.
*
* @param array $args
* @param PKPRequest $request
*/
public function systemInfo($args, $request)
{
$this->setupTemplate($request);
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
$currentVersion = $versionDao->getCurrentVersion();
if ($request->getUserVar('versionCheck')) {
$latestVersionInfo = VersionCheck::getLatestVersion();
$latestVersionInfo['patch'] = VersionCheck::getPatch($latestVersionInfo);
} else {
$latestVersionInfo = null;
}
$versionDao = DAORegistry::getDAO('VersionDAO'); /** @var VersionDAO $versionDao */
$versionHistory = $versionDao->getVersionHistory();
$pdo = DB::getPDO();
$serverInfo = [
'admin.server.platform' => PHP_OS,
'admin.server.phpVersion' => phpversion(),
'admin.server.apacheVersion' => $_SERVER['SERVER_SOFTWARE'],
'admin.server.dbDriver' => $pdo->getAttribute(PDO::ATTR_DRIVER_NAME),
'admin.server.dbVersion' => $pdo->getAttribute(PDO::ATTR_SERVER_VERSION),
];
$templateMgr = TemplateManager::getManager($request);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'wizard',
'name' => __('admin.systemInformation'),
];
$templateMgr->assign([
'breadcrumbs' => $breadcrumbs,
'currentVersion' => $currentVersion,
'latestVersionInfo' => $latestVersionInfo,
'pageTitle' => __('admin.systemInformation'),
'versionHistory' => $versionHistory,
'serverInfo' => $serverInfo,
'configData' => Config::getData(),
]);
$templateMgr->display('admin/systemInfo.tpl');
}
/**
* Show full PHP configuration information.
*/
public function phpinfo()
{
phpinfo();
}
/**
* Expire all user sessions (will log out all users currently logged in).
*
* @param array $args
* @param PKPRequest $request
*/
public function expireSessions($args, $request)
{
if (!$request->checkCSRF()) {
return new JSONMessage(false);
}
$sessionDao = DAORegistry::getDAO('SessionDAO'); /** @var SessionDAO $sessionDao */
$sessionDao->deleteAllSessions();
$request->redirect(null, 'login');
}
/**
* Clear compiled templates.
*
* @param array $args
* @param PKPRequest $request
*/
public function clearTemplateCache($args, $request)
{
if (!$request->checkCSRF()) {
return new JSONMessage(false);
}
$templateMgr = TemplateManager::getManager($request);
$templateMgr->clearTemplateCache();
$templateMgr->clearCssCache();
$request->redirect(null, 'admin');
}
/**
* Clear the data cache.
*
* @param array $args
* @param PKPRequest $request
*/
public function clearDataCache($args, $request)
{
if (!$request->checkCSRF()) {
return new JSONMessage(false);
}
// Clear the CacheManager's caches
$cacheManager = CacheManager::getManager();
$cacheManager->flush();
//clear laravel cache
$cacheManager = PKPContainer::getInstance()['cache'];
$cacheManager->store()->flush();
$request->redirect(null, 'admin');
}
/**
* Download scheduled task execution log file.
*
* @param array $args
* @param PKPRequest $request
*/
public function downloadScheduledTaskLogFile($args, $request)
{
$file = basename($request->getUserVar('file'));
ScheduledTaskHelper::downloadExecutionLog($file);
}
/**
* Clear scheduled tasks execution logs.
*
* @param array $args
* @param PKPRequest $request
*/
public function clearScheduledTaskLogFiles($args, $request)
{
if (!$request->checkCSRF()) {
return new JSONMessage(false);
}
ScheduledTaskHelper::clearExecutionLogs();
$request->redirect(null, 'admin');
}
/**
* List the jobs waiting to be executed in the queue
*
* @param array $args
* @param PKPRequest $request
*/
public function jobs($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'jobs',
'name' => __('navigation.tools.jobs'),
];
$templateMgr->setState($this->getJobsTableState($request));
$templateMgr->assign([
'pageComponent' => 'JobsPage',
'breadcrumbs' => $breadcrumbs,
'pageTitle' => 'navigation.tools.jobs',
]);
$templateMgr->display('admin/jobs.tpl');
}
/**
* Build the state data for the queued jobs table
*/
protected function getJobsTableState(PKPRequest $request): array
{
return [
'i18nDescription' => __('admin.jobs.totalCount'),
'label' => __('admin.jobs.viewQueuedJobs'),
'columns' => [
[
'name' => 'id',
'label' => __('admin.jobs.list.id'),
'value' => 'id',
],
[
'name' => 'title',
'label' => __('admin.jobs.list.displayName'),
'value' => 'displayName',
],
[
'name' => 'queue',
'label' => __('admin.jobs.list.queue'),
'value' => 'queue',
],
[
'name' => 'attempts',
'label' => __('admin.jobs.list.attempts'),
'value' => 'attempts',
],
[
'name' => 'created_at',
'label' => __('admin.jobs.list.createdAt'),
'value' => 'created_at',
]
],
'apiUrl' => $request->getDispatcher()->url($request, Application::ROUTE_API, 'index', 'jobs/all'),
];
}
/**
* List the queue jobs failied to execute
*
* @param array $args
* @param PKPRequest $request
*/
public function failedJobs($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'failedJobs',
'name' => __('navigation.tools.jobs.failed'),
];
$templateMgr->setState($this->getFailedJobsTableState($request));
$templateMgr->assign([
'pageComponent' => 'FailedJobsPage',
'breadcrumbs' => $breadcrumbs,
'pageTitle' => 'navigation.tools.jobs.failed',
]);
$templateMgr->display('admin/failedJobs.tpl');
}
/**
* Build the state data for the queued jobs table
*/
protected function getFailedJobsTableState(PKPRequest $request): array
{
return [
'i18nDescription' => __('admin.jobs.failed.totalCount'),
'label' => __('navigation.tools.jobs.failed.view'),
'columns' => [
[
'name' => 'id',
'label' => __('admin.jobs.list.id'),
'value' => 'id',
],
[
'name' => 'title',
'label' => __('admin.jobs.list.displayName'),
'value' => 'displayName',
],
[
'name' => 'queue',
'label' => __('admin.jobs.list.queue'),
'value' => 'queue',
],
[
'name' => 'connection',
'label' => __('admin.jobs.list.connection'),
'value' => 'connection',
],
[
'name' => 'failed_at',
'label' => __('admin.jobs.list.failedAt'),
'value' => 'failed_at',
],
[
'name' => 'actions',
'label' => __('admin.jobs.list.actions'),
'value' => 'action',
],
],
'apiUrl' => $request->getDispatcher()->url($request, Application::ROUTE_API, 'index', 'jobs/failed/all'),
'apiUrlRedispatchAll' => $request->getDispatcher()->url($request, Application::ROUTE_API, 'index', 'jobs/redispatch/all'),
];
}
/**
* Show the failed jobs details
*
* @param array $args
* @param PKPRequest $request
*/
public function failedJobDetails($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$failedJob = Repo::failedJob()->get((int) $args[0]);
if (!$failedJob) {
$request->getDispatcher()->handle404();
}
$rows = collect(array_merge(HttpFailedJobResource::toResourceArray($failedJob), [
'payload' => $failedJob->first()->getRawOriginal('payload'),
]))
->map(fn ($value, $attribute) => is_array($value) ? null : [
'attribute' => '<b>' . __('admin.jobs.list.' . Str::of($attribute)->snake()->replace('_', ' ')->camel()->value()) . '</b>',
'value' => isValidJson($value) ? json_encode(json_decode($value, true), JSON_PRETTY_PRINT) : $value
])
->filter()
->values();
$breadcrumbs = $templateMgr->getTemplateVars('breadcrumbs');
$breadcrumbs[] = [
'id' => 'FailedJobDetailsPage',
'name' => __('navigation.tools.jobs.failed.details'),
];
$templateMgr->setState([
'label' => __('navigation.tools.job.failed.details.view', ['id' => $failedJob->id]),
'columns' => [
[
'name' => 'attribute',
'label' => __('admin.job.failed.list.attribute'),
'value' => 'attribute',
],
[
'name' => 'value',
'label' => __('admin.job.failed.list.attribute.value'),
'value' => 'value',
],
],
'rows' => $rows,
]);
$templateMgr->assign([
'pageComponent' => 'FailedJobDetailsPage',
'breadcrumbs' => $breadcrumbs,
'pageTitle' => 'navigation.tools.jobs.failed.details',
]);
$templateMgr->display('admin/failedJobDetails.tpl');
}
/**
* Get the highlights list panel
*/
protected function getHighlightsListPanel(): HighlightsListPanel
{
if (!Config::getVar('features', 'highlights')) {
return new HighlightsListPanel(
'highlights',
'',
[]
);
}
$request = Application::get()->getRequest();
$dispatcher = $request->getDispatcher();
$apiUrl = $dispatcher->url(
$request,
Application::ROUTE_API,
Application::CONTEXT_ID_ALL,
'highlights'
);
$highlightForm = new HighlightForm(
$apiUrl,
Repo::highlight()->getFileUploadBaseUrl(),
$dispatcher->url(
Application::get()->getRequest(),
Application::ROUTE_API,
Application::CONTEXT_ID_ALL,
'temporaryFiles'
)
);
$items = Repo::highlight()
->getCollector()
->withSiteHighlights(HighlightCollector::SITE_ONLY)
->getMany();
return new HighlightsListPanel(
'highlights',
__('common.highlights'),
[
'apiUrl' => $apiUrl,
'form' => $highlightForm,
'items' => Repo::highlight()
->getSchemaMap()
->summarizeMany($items)
->values(),
'itemsMax' => $items->count(),
]
);
}
/*
* Get the list panel for site-wide announcements
*/
protected function getAnnouncementsListPanel(string $apiUrl, PKPAnnouncementForm $form): PKPAnnouncementsListPanel
{
$collector = Repo::announcement()
->getCollector()
->withSiteAnnouncements(Collector::SITE_ONLY);
$itemsMax = $collector->getCount();
$items = Repo::announcement()->getSchemaMap()->summarizeMany(
$collector->limit(30)->getMany()
);
return new PKPAnnouncementsListPanel(
'announcements',
__('manager.setup.announcements'),
[
'apiUrl' => $apiUrl,
'form' => $form,
'getParams' => [
'contextIds' => [Application::CONTEXT_ID_NONE],
'count' => 30,
],
'items' => $items->values(),
'itemsMax' => $itemsMax,
]
);
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* @defgroup pages_admin Administration Pages
*/
/**
* @file lib/pkp/pages/admin/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_admin
*
* @brief Handle requests for site administration functions.
*
*/
switch ($op) {
case 'index':
case 'contexts':
case 'settings':
case 'saveSettings':
case 'wizard':
case 'systemInfo':
case 'phpinfo':
case 'expireSessions':
case 'clearTemplateCache':
case 'clearDataCache':
case 'downloadScheduledTaskLogFile':
case 'clearScheduledTaskLogFiles':
case 'jobs':
case 'failedJobs':
case 'failedJobDetails':
define('HANDLER_CLASS', 'PKP\pages\admin\AdminHandler');
break;
}
@@ -0,0 +1,115 @@
<?php
/**
* @file pages/announcement/AnnouncementHandler.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 AnnouncementHandler
*
* @ingroup pages_announcement
*
* @brief Handle requests for public announcement functions.
*/
namespace PKP\pages\announcement;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\announcement\Collector;
use PKP\config\Config;
use PKP\core\PKPRequest;
use PKP\security\authorization\ContextRequiredPolicy;
class AnnouncementHandler extends Handler
{
//
// Public handler methods.
//
/**
* Show public announcements page.
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
if (!$this->isAnnouncementsEnabled($request)) {
$request->getDispatcher()->handle404();
}
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('announcementsIntroduction', $this->getAnnouncementsIntro($request));
// TODO the announcements list should support pagination
$collector = Repo::announcement()
->getCollector()
->filterByActive();
if ($request->getContext()) {
$collector->filterByContextIds([$request->getContext()->getId()]);
} else {
$collector->withSiteAnnouncements(Collector::SITE_ONLY);
}
$announcements = $collector->getMany();
$templateMgr->assign('announcements', $announcements->toArray());
$templateMgr->display('frontend/pages/announcements.tpl');
}
/**
* View announcement details.
*
* @param array $args first parameter is the ID of the announcement to display
* @param PKPRequest $request
*/
public function view($args, $request)
{
if (!$this->isAnnouncementsEnabled($request)) {
$request->getDispatcher()->handle404();
}
$this->validate();
$this->setupTemplate($request);
$announcementId = (int) array_shift($args);
$announcement = Repo::announcement()->get($announcementId);
if (
$announcement
&& $announcement->getAssocType() == Application::getContextAssocType()
&& $announcement->getAssocId() == $request->getContext()?->getId()
&& (
$announcement->getDateExpire() == null || strtotime($announcement->getDateExpire()) > time()
)
) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('announcement', $announcement);
$templateMgr->assign('announcementTitle', $announcement->getLocalizedTitleFull());
return $templateMgr->display('frontend/pages/announcement.tpl');
}
$request->redirect(null, 'announcement');
}
protected function isAnnouncementsEnabled(Request $request): bool
{
if (!Config::getVar('features', 'site_announcements') && !$request->getContext()) {
return false;
}
$contextOrSite = $request->getContext() ?? $request->getSite();
return $contextOrSite->getData('enableAnnouncements');
}
protected function getAnnouncementsIntro(Request $request): ?string
{
$contextOrSite = $request->getContext() ?? $request->getSite();
return $contextOrSite->getLocalizedData('announcementsIntroduction');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
/**
* @defgroup pages_announcement Announcement Pages
*/
/**
* @file lib/pkp/pages/announcement/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_announcement
*
* @brief Handle requests for public announcement functions.
*
*/
switch ($op) {
case 'index':
case 'view':
define('HANDLER_CLASS', 'PKP\pages\announcement\AnnouncementHandler');
break;
}
@@ -0,0 +1,387 @@
<?php
/**
* @file pages/authorDashboard/PKPAuthorDashboardHandler.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 PKPAuthorDashboardHandler
*
* @ingroup pages_authorDashboard
*
* @brief Handle requests for the author dashboard.
*/
namespace PKP\pages\authorDashboard;
use APP\core\Application;
use APP\core\Request;
use APP\core\Services;
use APP\decision\Decision;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\publication\Publication;
use APP\submission\Submission;
use APP\template\TemplateManager;
use Illuminate\Support\Enumerable;
use PKP\components\forms\publication\PKPCitationsForm;
use PKP\components\forms\publication\PKPMetadataForm;
use PKP\components\forms\publication\TitleAbstractForm;
use PKP\components\listPanels\ContributorsListPanel;
use PKP\context\Context;
use PKP\core\JSONMessage;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\log\SubmissionEmailLogDAO;
use PKP\log\SubmissionEmailLogEntry;
use PKP\security\authorization\AuthorDashboardAccessPolicy;
use PKP\security\Role;
use PKP\submission\GenreDAO;
use PKP\submission\PKPSubmission;
use PKP\submission\reviewRound\ReviewRoundDAO;
use PKP\submissionFile\SubmissionFile;
use PKP\workflow\WorkflowStageDAO;
abstract class PKPAuthorDashboardHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_AUTHOR],
[
'submission',
'readSubmissionEmail',
]
);
}
//
// Implement template methods from PKPHandler
//
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new AuthorDashboardAccessPolicy($request, $args, $roleAssignments), true);
return parent::authorize($request, $args, $roleAssignments);
}
//
// Public handler operations
//
/**
* Displays the author dashboard.
*
* @param array $args
* @param PKPRequest $request
*/
public function submission($args, $request)
{
// Pass the authorized submission on to the template.
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
return $templateMgr->display('authorDashboard/authorDashboard.tpl');
}
/**
* Fetches information about a specific email and returns it.
*
* @param array $args
* @param Request $request
*
* @return JSONMessage JSON object
*/
public function readSubmissionEmail($args, $request)
{
$submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /** @var SubmissionEmailLogDAO $submissionEmailLogDao */
$user = $request->getUser();
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$submissionEmailId = $request->getUserVar('submissionEmailId');
$submissionEmailFactory = $submissionEmailLogDao->getByEventType($submission->getId(), SubmissionEmailLogEntry::SUBMISSION_EMAIL_EDITOR_NOTIFY_AUTHOR, $user->getId());
while ($email = $submissionEmailFactory->next()) { // validate the email id for this user.
if ($email->getId() == $submissionEmailId) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('submissionEmail', $email);
return $templateMgr->fetchJson('authorDashboard/submissionEmail.tpl');
}
}
}
/**
* Get the SubmissionFile::SUBMISSION_FILE_... file stage based on the current
* WORKFLOW_STAGE_... workflow stage.
*
* @param int $currentStage WORKFLOW_STAGE_...
*
* @return ?int SubmissionFile::SUBMISSION_FILE_...
*/
protected function _fileStageFromWorkflowStage($currentStage)
{
switch ($currentStage) {
case WORKFLOW_STAGE_ID_SUBMISSION:
return SubmissionFile::SUBMISSION_FILE_SUBMISSION;
case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
return SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION;
case WORKFLOW_STAGE_ID_EDITING:
return SubmissionFile::SUBMISSION_FILE_FINAL;
default:
return null;
}
}
//
// Protected helper methods
//
/**
* Setup common template variables.
*/
public function setupTemplate($request)
{
parent::setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); /** @var Submission $submission */
$user = $request->getUser();
$submissionContext = $request->getContext();
if ($submission->getContextId() !== $submissionContext->getId()) {
$submissionContext = Services::get('context')->get($submission->getContextId());
}
$contextUserGroups = Repo::userGroup()->getByRoleIds([Role::ROLE_ID_AUTHOR], $submission->getData('contextId'));
$genreDao = DAORegistry::getDAO('GenreDAO'); /** @var GenreDAO $genreDao */
$contextGenres = $genreDao->getEnabledByContextId($submission->getData('contextId'))->toArray();
$workflowStages = WorkflowStageDAO::getWorkflowStageKeysAndPaths();
$stageNotifications = [];
foreach (array_keys($workflowStages) as $stageId) {
$stageNotifications[$stageId] = false;
}
// Add an upload revisions button when in the review stage
// and the last decision is to request revisions
$uploadFileUrl = '';
if (in_array($submission->getData('stageId'), [WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW])) {
$fileStage = $this->_fileStageFromWorkflowStage($submission->getData('stageId'));
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
$lastReviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $submission->getData('stageId'));
if ($fileStage && is_a($lastReviewRound, 'ReviewRound')) {
$editorDecisions = Repo::decision()->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByStageIds([$submission->getData('stageId')])
->filterByReviewRoundIds([$lastReviewRound->getId()])
->getMany();
if (!$editorDecisions->isEmpty()) {
$lastDecision = $editorDecisions->last();
$revisionDecisions = [
Decision::PENDING_REVISIONS,
Decision::RESUBMIT
];
if (in_array($lastDecision->getData('decision'), $revisionDecisions)) {
$actionArgs['submissionId'] = $submission->getId();
$actionArgs['stageId'] = $submission->getData('stageId');
$actionArgs['uploaderRoles'] = Role::ROLE_ID_AUTHOR;
$actionArgs['fileStage'] = $fileStage;
$actionArgs['reviewRoundId'] = $lastReviewRound->getId();
$uploadFileUrl = $request->getDispatcher()->url(
$request,
PKPApplication::ROUTE_COMPONENT,
null,
'wizard.fileUpload.FileUploadWizardHandler',
'startWizard',
null,
$actionArgs
);
}
}
}
}
$locales = $submissionContext->getSupportedSubmissionLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
$latestPublication = $submission->getLatestPublication();
$submissionApiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $submissionContext->getData('urlPath'), 'submissions/' . $submission->getId());
$latestPublicationApiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $submissionContext->getData('urlPath'), 'submissions/' . $submission->getId() . '/publications/' . $latestPublication->getId());
$submissionLibraryUrl = $request->getDispatcher()->url(
$request,
PKPApplication::ROUTE_COMPONENT,
null,
'modals.documentLibrary.DocumentLibraryHandler',
'documentLibrary',
null,
['submissionId' => $submission->getId()]
);
$titleAbstractForm = $this->getTitleAbstractForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext);
$citationsForm = new PKPCitationsForm($latestPublicationApiUrl, $latestPublication);
// Import constants
import('classes.components.forms.publication.PublishForm');
$templateMgr->setConstants([
'STATUS_QUEUED' => PKPSubmission::STATUS_QUEUED,
'STATUS_PUBLISHED' => PKPSubmission::STATUS_PUBLISHED,
'STATUS_DECLINED' => PKPSubmission::STATUS_DECLINED,
'STATUS_SCHEDULED' => PKPSubmission::STATUS_SCHEDULED,
'FORM_TITLE_ABSTRACT' => FORM_TITLE_ABSTRACT,
'FORM_CITATIONS' => FORM_CITATIONS,
]);
// Get the submission props without the full publication details. We'll
// retrieve just the publication information that we need separately to
// reduce the amount of data passed to the browser
$submissionProps = Repo::submission()->getSchemaMap()->summarizeWithoutPublication($submission);
// Get an array of publications
$publications = $submission->getData('publications'); /** @var Enumerable $publications */
$publicationList = $publications->map(function ($publication) {
return [
'id' => $publication->getId(),
'datePublished' => $publication->getData('datePublished'),
'status' => $publication->getData('status'),
'version' => $publication->getData('version')
];
})->values();
// Get full details of the working publication and the current publication
$mapper = Repo::publication()->getSchemaMap($submission, $contextUserGroups, $contextGenres);
$workingPublicationProps = $mapper->map($submission->getLatestPublication());
$currentPublicationProps = $submission->getLatestPublication()->getId() === $submission->getCurrentPublication()->getId()
? $workingPublicationProps
: $mapper->map($submission->getCurrentPublication());
// Check if current author can edit metadata
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
$canEditPublication = true;
if (!in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles) && !Repo::submission()->canEditPublication($submission->getId(), $user->getId())) {
$canEditPublication = false;
}
$authorItems = [];
foreach ($latestPublication->getData('authors') as $contributor) {
$authorItems[] = Repo::author()->getSchemaMap()->map($contributor);
}
$contributorsListPanel = $this->getContributorsListPanel(
$submission,
$submissionContext,
$locales,
$authorItems,
$canEditPublication
);
// Check if current author can access ArticleGalleyGrid within production stage
$canAccessProductionStage = true;
$userAllowedStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
if (!array_key_exists(WORKFLOW_STAGE_ID_PRODUCTION, $userAllowedStages)) {
$canAccessProductionStage = false;
}
$state = [
'canEditPublication' => $canEditPublication,
'components' => [
FORM_TITLE_ABSTRACT => $titleAbstractForm->getConfig(),
FORM_CITATIONS => $citationsForm->getConfig(),
$contributorsListPanel->id => $contributorsListPanel->getConfig(),
],
'currentPublication' => $currentPublicationProps,
'publicationFormIds' => [
FORM_TITLE_ABSTRACT,
FORM_CITATIONS,
],
'representationsGridUrl' => $canAccessProductionStage ? $this->_getRepresentationsGridUrl($request, $submission) : '',
'submission' => $submissionProps,
'publicationList' => $publicationList,
'workingPublication' => $workingPublicationProps,
'submissionApiUrl' => $submissionApiUrl,
'submissionLibraryLabel' => __('grid.libraryFiles.submission.title'),
'submissionLibraryUrl' => $submissionLibraryUrl,
'supportsReferences' => !!$submissionContext->getData('citations'),
'statusLabel' => __('semicolon', ['label' => __('common.status')]),
'uploadFileModalLabel' => __('editor.submissionReview.uploadFile'),
'uploadFileUrl' => $uploadFileUrl,
'versionLabel' => __('semicolon', ['label' => __('admin.version')]),
];
// Add the metadata form if one or more metadata fields are enabled
$vocabSuggestionUrlBase = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $submissionContext->getData('urlPath'), 'vocabs', null, null, ['vocab' => '__vocab__']);
$metadataForm = new PKPMetadataForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext, $vocabSuggestionUrlBase, true);
$metadataFormConfig = $metadataForm->getConfig();
$metadataEnabled = count($metadataForm->fields);
if ($metadataEnabled) {
$templateMgr->setConstants([
'FORM_METADATA' => FORM_METADATA,
]);
$state['components'][FORM_METADATA] = $metadataFormConfig;
$state['publicationFormIds'][] = FORM_METADATA;
}
$templateMgr->setState($state);
$templateMgr->assign([
'metadataEnabled' => $metadataEnabled,
'pageComponent' => 'WorkflowPage',
'pageTitle' => implode(__('common.titleSeparator'), array_filter([
$latestPublication->getShortAuthorString(),
$submission->getCurrentPublication()->getLocalizedTitle(null, 'html')
])),
'submission' => $submission,
'workflowStages' => $workflowStages,
'canAccessProductionStage' => $canAccessProductionStage,
]);
}
/**
* Get the contributor's list panel
*/
protected function getContributorsListPanel(Submission $submission, Context $context, array $locales, array $authorItems, ?bool $canEditPublication): ContributorsListPanel
{
return new ContributorsListPanel(
'contributors',
__('publication.contributors'),
$submission,
$context,
$locales,
$authorItems,
$canEditPublication
);
}
/**
* Get the URL for the galley/publication formats grid with a placeholder for
* the publicationId value
*
* @param Request $request
* @param Submission $submission
*
* @return string
*/
abstract protected function _getRepresentationsGridUrl($request, $submission);
/**
* Get the form for entering the title/abstract details
*/
abstract protected function getTitleAbstractForm(string $latestPublicationApiUrl, array $locales, Publication $latestPublication, Context $context): TitleAbstractForm;
}
+190
View File
@@ -0,0 +1,190 @@
<?php
/**
* @file pages/catalog/PKPCatalogHandler.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 PKPCatalogHandler
*
* @ingroup pages_catalog
*
* @brief Handle requests for the public-facing catalog.
*/
namespace PKP\pages\catalog;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\submission\Collector;
use APP\submission\Submission;
use APP\template\TemplateManager;
use PKP\config\Config;
use PKP\core\PKPRequest;
use PKP\db\DAO;
use PKP\file\ContextFileManager;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\Role;
class PKPCatalogHandler extends Handler
{
//
// Overridden methods from Handler
//
/**
* @see PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new ContextRequiredPolicy($request));
return parent::authorize($request, $args, $roleAssignments);
}
/**
* View the content of a category.
*
* @param array $args [
*
* @option string Category path
* @option int Page number if available
* ]
*
* @param PKPRequest $request
*/
public function category($args, $request)
{
$page = isset($args[1]) ? (int) $args[1] : 1;
$templateMgr = TemplateManager::getManager($request);
$context = $request->getContext();
// Get the category
$category = Repo::category()->getCollector()
->filterByPaths([$args[0]])
->filterByContextIds([$context->getId()])
->getMany()
->first();
if (!$category) {
$this->getDispatcher()->handle404();
}
$this->setupTemplate($request);
$orderOption = $category->getSortOption() ? $category->getSortOption() : Collector::ORDERBY_DATE_PUBLISHED . '-' . DAO::SORT_DIRECTION_DESC;
[$orderBy, $orderDir] = explode('-', $orderOption);
$count = $context->getData('itemsPerPage') ? $context->getData('itemsPerPage') : Config::getVar('interface', 'items_per_page');
$offset = $page > 1 ? ($page - 1) * $count : 0;
$collector = Repo::submission()
->getCollector()
->filterByContextIds([$context->getId()])
->filterByCategoryIds([$category->getId()])
->filterByStatus([Submission::STATUS_PUBLISHED])
->orderBy($orderBy, $orderDir === DAO::SORT_DIRECTION_ASC ? Collector::ORDER_DIR_ASC : Collector::ORDER_DIR_DESC);
// Featured items are only in OMP at this time
if (method_exists($collector, 'orderByFeatured')) {
$collector->orderByFeatured(true);
}
$total = $collector->getCount();
$submissions = $collector->limit($count)->offset($offset)->getMany();
// Provide the parent category and a list of subcategories
$parentCategory = $category->getParentId() ? Repo::category()->get($category->getParentId()) : null;
$subcategories = Repo::category()->getCollector()
->filterByParentIds([$category->getId()])
->getMany();
$this->_setupPaginationTemplate($request, count($submissions), $page, $count, $offset, $total);
$templateMgr->assign([
'category' => $category,
'parentCategory' => $parentCategory,
'subcategories' => iterator_to_array($subcategories),
'publishedSubmissions' => $submissions->toArray(),
'authorUserGroups' => Repo::userGroup()->getCollector()
->filterByRoleIds([Role::ROLE_ID_AUTHOR])
->filterByContextIds([$context->getId()])->getMany()->remember(),
]);
return $templateMgr->display('frontend/pages/catalogCategory.tpl');
}
/**
* Serve the full sized image for a category.
*
* @param array $args
* @param PKPRequest $request
*/
public function fullSize($args, $request)
{
switch ($request->getUserVar('type')) {
case 'category':
$context = $request->getContext();
$category = Repo::category()->get((int) $request->getUserVar('id'));
if (!$category || $category->getContextId() != $context->getId()) {
$this->getDispatcher()->handle404();
}
$imageInfo = $category->getImage();
$contextFileManager = new ContextFileManager($context->getId());
$contextFileManager->downloadByPath($contextFileManager->getBasePath() . '/categories/' . $imageInfo['name'], null, true);
break;
default:
fatalError('invalid type specified');
}
}
/**
* Serve the thumbnail for a category.
*
* @param array $args
* @param PKPRequest $request
*/
public function thumbnail($args, $request)
{
switch ($request->getUserVar('type')) {
case 'category':
$context = $request->getContext();
$category = Repo::category()->get((int) $request->getUserVar('id'));
if (!$category || $category->getContextId() != $context->getId()) {
$this->getDispatcher()->handle404();
}
$imageInfo = $category->getImage();
$contextFileManager = new ContextFileManager($context->getId());
$contextFileManager->downloadByPath($contextFileManager->getBasePath() . '/categories/' . $imageInfo['thumbnailName'], null, true);
break;
default:
fatalError('invalid type specified');
}
}
/**
* Assign the pagination template variables
*
* @param PKPRequest $request
* @param int $submissionsCount Number of monographs being shown
* @param int $page Page number being shown
* @param int $count Max number of monographs being shown
* @param int $offset Starting position of monographs
* @param int $total Total number of monographs available
*/
protected function _setupPaginationTemplate($request, $submissionsCount, $page, $count, $offset, $total)
{
$showingStart = $offset + 1;
$showingEnd = min($offset + $count, $offset + $submissionsCount);
$nextPage = $total > $showingEnd ? $page + 1 : null;
$prevPage = $showingStart > 1 ? $page - 1 : null;
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'showingStart' => $showingStart,
'showingEnd' => $showingEnd,
'total' => $total,
'nextPage' => $nextPage,
'prevPage' => $prevPage,
]);
}
}
@@ -0,0 +1,225 @@
<?php
/**
* @file pages/dashboard/DashboardHandler.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 DashboardHandler
*
* @ingroup pages_dashboard
*
* @brief Handle requests for user's dashboard.
*/
namespace PKP\pages\dashboard;
use APP\core\Application;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\core\JSONMessage;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\security\authorization\PKPSiteAccessPolicy;
use PKP\security\Role;
use PKP\submission\GenreDAO;
use PKP\submission\PKPSubmission;
define('SUBMISSIONS_LIST_ACTIVE', 'active');
define('SUBMISSIONS_LIST_ARCHIVE', 'archive');
define('SUBMISSIONS_LIST_MY_QUEUE', 'myQueue');
define('SUBMISSIONS_LIST_UNASSIGNED', 'unassigned');
class DashboardHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_AUTHOR, Role::ROLE_ID_REVIEWER, Role::ROLE_ID_ASSISTANT],
['index', 'tasks', 'myQueue', 'unassigned', 'active', 'archives']
);
}
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new PKPSiteAccessPolicy($request, null, $roleAssignments));
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Display about index page.
*
* @param PKPRequest $request
* @param array $args
*/
public function index($args, $request)
{
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
if (!$context) {
$request->redirect(null, 'user');
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$currentUser = $request->getUser();
$userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
$apiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), '_submissions');
$lists = [];
$includeIssuesFilter = array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT], $userRoles);
$includeAssignedEditorsFilter = array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles);
$includeCategoriesFilter = array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT], $userRoles);
// Get all available categories
$categories = [];
$categoryCollection = Repo::category()->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
foreach ($categoryCollection as $category) {
$categories[] = [
'id' => $category->getId(),
'title' => $category->getLocalizedTitle(),
];
}
// My Queue
$collector = Repo::submission()->getCollector()
->filterByContextIds([(int) $request->getContext()->getId()])
->filterByStatus([PKPSubmission::STATUS_QUEUED])
->assignedTo([(int) $request->getUser()->getId()]);
$itemsMax = $collector->getCount();
$items = $collector->limit(30)->getMany();
$userGroups = Repo::userGroup()->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
/** @var GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genres = $genreDao->getByContextId($context->getId())->toArray();
$items = Repo::submission()->getSchemaMap()->mapManyToSubmissionsList($items, $userGroups, $genres);
$myQueueListPanel = new \APP\components\listPanels\SubmissionsListPanel(
SUBMISSIONS_LIST_MY_QUEUE,
__('common.queue.long.myAssigned'),
[
'apiUrl' => $apiUrl,
'getParams' => [
'status' => PKPSubmission::STATUS_QUEUED,
'assignedTo' => [(int) $request->getUser()->getId()],
],
'includeIssuesFilter' => $includeIssuesFilter,
'includeCategoriesFilter' => $includeCategoriesFilter,
'includeActiveSectionFiltersOnly' => true,
'items' => $items->values(),
'itemsMax' => $itemsMax,
'categories' => $categories,
]
);
$lists[$myQueueListPanel->id] = $myQueueListPanel->getConfig();
if (!empty(array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles))) {
// Unassigned
$unassignedListPanel = new \APP\components\listPanels\SubmissionsListPanel(
SUBMISSIONS_LIST_UNASSIGNED,
__('common.queue.long.submissionsUnassigned'),
[
'apiUrl' => $apiUrl,
'getParams' => [
'status' => PKPSubmission::STATUS_QUEUED,
'assignedTo' => \PKP\submission\Collector::UNASSIGNED,
],
'lazyLoad' => true,
'includeIssuesFilter' => $includeIssuesFilter,
'includeCategoriesFilter' => $includeCategoriesFilter,
'includeActiveSectionFiltersOnly' => true,
'categories' => $categories,
]
);
$lists[$unassignedListPanel->id] = $unassignedListPanel->getConfig();
// Active
$activeListPanel = new \APP\components\listPanels\SubmissionsListPanel(
SUBMISSIONS_LIST_ACTIVE,
__('common.queue.long.active'),
[
'apiUrl' => $apiUrl,
'getParams' => [
'status' => PKPSubmission::STATUS_QUEUED,
],
'lazyLoad' => true,
'includeIssuesFilter' => $includeIssuesFilter,
'includeCategoriesFilter' => $includeCategoriesFilter,
'includeAssignedEditorsFilter' => $includeAssignedEditorsFilter,
'categories' => $categories,
]
);
$lists[$activeListPanel->id] = $activeListPanel->getConfig();
}
// Archived
$params = [
'status' => [PKPSubmission::STATUS_DECLINED, PKPSubmission::STATUS_PUBLISHED, PKPSubmission::STATUS_SCHEDULED],
];
if (empty(array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $userRoles))) {
$params['assignedTo'] = (int) $currentUser->getId();
}
$archivedListPanel = new \APP\components\listPanels\SubmissionsListPanel(
SUBMISSIONS_LIST_ARCHIVE,
__('submissions.archived'),
[
'apiUrl' => $apiUrl,
'getParams' => $params,
'lazyLoad' => true,
'includeIssuesFilter' => $includeIssuesFilter,
'includeCategoriesFilter' => $includeCategoriesFilter,
'includeAssignedEditorsFilter' => $includeAssignedEditorsFilter,
'categories' => $categories,
]
);
$lists[$archivedListPanel->id] = $archivedListPanel->getConfig();
$templateMgr->setState(['components' => $lists]);
$templateMgr->assign([
'pageTitle' => __('navigation.submissions'),
]);
return $templateMgr->display('dashboard/index.tpl');
}
/**
* View tasks tab
*
* @param array $args
* @param PKPRequest $request
*
* @return JSONMessage JSON object
*/
public function tasks($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
return $templateMgr->fetchJson('dashboard/tasks.tpl');
}
}
+266
View File
@@ -0,0 +1,266 @@
<?php
/**
* @file pages/decision/DecisionHandler.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2003-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class DecisionHandler
*
* @ingroup pages_decision
*
* @brief Handle requests to take an editorial decision.
*/
namespace PKP\pages\decision;
use APP\core\Application;
use APP\core\PageRouter;
use APP\core\Request;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\submission\Submission;
use APP\template\TemplateManager;
use PKP\context\Context;
use PKP\core\Dispatcher;
use PKP\db\DAORegistry;
use PKP\decision\DecisionType;
use PKP\decision\types\interfaces\DecisionRetractable;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\authorization\DecisionWritePolicy;
use PKP\security\authorization\internal\SubmissionRequiredPolicy;
use PKP\security\authorization\UserRequiredPolicy;
use PKP\security\Role;
use PKP\stageAssignment\StageAssignmentDAO;
use PKP\submission\Genre;
use PKP\submission\GenreDAO;
use PKP\submission\reviewRound\ReviewRound;
use PKP\submission\reviewRound\ReviewRoundDAO;
use Stringy\Stringy;
class DecisionHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
protected DecisionType $decisionType;
protected Submission $submission;
protected ?ReviewRound $reviewRound = null;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR],
['record']
);
}
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments): bool
{
/** @var PageRouter */
$router = $request->getRouter();
$op = $router->getRequestedOp($request);
if (!$op || $op !== 'record') {
return false;
}
$this->addPolicy(new UserRequiredPolicy($request));
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
$this->addPolicy(new SubmissionRequiredPolicy($request, $args, 'submissionId'));
$this->addPolicy(new DecisionWritePolicy($request, $args, (int) $request->getUserVar('decision'), $request->getUser()));
return parent::authorize($request, $args, $roleAssignments);
}
public function record($args, $request)
{
$this->setupTemplate($request);
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
$reviewRoundId = (int) $request->getUserVar('reviewRoundId');
$this->decisionType = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_DECISION_TYPE);
$this->submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
// Don't allow a decision unless the submission is at the correct stage
if ($this->submission->getData('stageId') !== $this->decisionType->getStageId()) {
$request->getDispatcher()->handle404();
}
// Don't allow a decision in a review stage unless there is a valid review round
if (in_array($this->decisionType->getStageId(), [WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW])) {
if (!$reviewRoundId) {
$request->getDispatcher()->handle404();
}
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
$this->reviewRound = $reviewRoundDao->getById($reviewRoundId);
if (!$this->reviewRound || $this->reviewRound->getSubmissionId() !== $this->submission->getId()) {
$request->getDispatcher()->handle404();
}
}
// For a retractable decision, don't allow if it can not be retracted
if ($this->decisionType instanceof DecisionRetractable && !$this->decisionType->canRetract($this->submission, $reviewRoundId)) {
$request->getDispatcher()->handle404();
}
// Don't allow a recommendation unless at least one deciding editor exists
if (Repo::decision()->isRecommendation($this->decisionType->getDecision())) {
/** @var StageAssignmentDAO $stageAssignmentDao */
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO');
$assignedEditorIds = $stageAssignmentDao->getDecidingEditorIds($this->submission->getId(), $this->decisionType->getStageId());
if (!$assignedEditorIds) {
$request->getDispatcher()->handle404();
}
}
$steps = $this->decisionType->getSteps(
$this->submission,
$context,
$request->getUser(),
$this->reviewRound
);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'abandonDecisionLabel' => __('editor.decision.cancelDecision'),
'cancelConfirmationPrompt' => __('editor.decision.cancelDecision.confirmation'),
'decision' => $this->decisionType->getDecision(),
'decisionCompleteLabel' => $this->decisionType->getCompletedLabel(),
'decisionCompleteDescription' => $this->decisionType->getCompletedMessage($this->submission),
'emailTemplatesApiUrl' => $dispatcher->url(
$request,
Application::ROUTE_API,
$context->getData('urlPath'),
'emailTemplates'
),
'fileGenres' => $this->getFileGenres($context),
'keepWorkingLabel' => __('common.keepWorking'),
'reviewRoundId' => $this->reviewRound ? $this->reviewRound->getId() : null,
'stageId' => $this->submission->getStageId(),
'stepErrorMessage' => __('editor.decision.stepError'),
'steps' => $steps->getState(),
'submissionUrl' => $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'workflow',
'access',
[$this->submission->getId()]
),
'submissionApiUrl' => $dispatcher->url(
$request,
Application::ROUTE_API,
$context->getData('urlPath'),
'submissions/' . $this->submission->getId()
),
'submissionListUrl' => $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'submissions',
),
'viewAllSubmissionsLabel' => __('submission.list.viewAllSubmissions'),
'viewSubmissionLabel' => __('submission.list.viewSubmission'),
]);
$templateMgr->assign([
'breadcrumbs' => $this->getBreadcrumb($this->submission, $context, $request, $dispatcher),
'decisionType' => $this->decisionType,
'pageComponent' => 'DecisionPage',
'pageWidth' => TemplateManager::PAGE_WIDTH_WIDE,
'pageTitle' => join(
__('common.titleSeparator'),
[
$this->decisionType->getLabel(),
$this->submission->getCurrentPublication()->getShortAuthorString()
? $this->submission->getCurrentPublication()->getShortAuthorString()
: $this->submission->getCurrentPublication()->getLocalizedFullTitle(),
]
),
'reviewRound' => $this->reviewRound,
'submission' => $this->submission,
]);
$templateMgr->display('decision/record.tpl');
}
protected function getBreadcrumb(Submission $submission, Context $context, Request $request, Dispatcher $dispatcher)
{
$currentPublication = $submission->getCurrentPublication();
$submissionTitle = Stringy::create(
join(
__('common.commaListSeparator'),
[
$currentPublication->getShortAuthorString(),
$currentPublication->getLocalizedFullTitle(null, 'html'),
]
)
);
if ($submissionTitle->length() > 50) {
$submissionTitle = $submissionTitle->safeTruncate(50)
->append('...');
}
return [
[
'id' => 'submissions',
'name' => __('navigation.submissions'),
'url' => $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'submissions'
),
],
[
'id' => 'submission',
'name' => $submissionTitle,
'format' => 'html',
'url' => $dispatcher->url(
$request,
Application::ROUTE_PAGE,
$context->getData('urlPath'),
'workflow',
'access',
[$submission->getId()]
),
],
[
'id' => 'decision',
'name' => $this->decisionType->getLabel(),
]
];
}
protected function getFileGenres(Context $context): array
{
$fileGenres = [];
/** @var GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genreResults = $genreDao->getEnabledByContextId($context->getId());
/** @var Genre $genre */
while ($genre = $genreResults->next()) {
$fileGenres[] = [
'id' => $genre->getId(),
'name' => $genre->getLocalizedName(),
'isPrimary' => !$genre->getSupplementary() && !$genre->getDependent(),
];
}
return $fileGenres;
}
}
+160
View File
@@ -0,0 +1,160 @@
<?php
/**
* @file /pages/dois/PKPDoisHandler.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 PKPDoisHandler
*
* @ingroup pages_doi
*
* @brief Handle requests for DOI management functions.
*/
namespace PKP\pages\dois;
use APP\components\forms\context\DoiSetupSettingsForm;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\components\forms\context\PKPDoiRegistrationSettingsForm;
use PKP\components\forms\context\PKPDoiSetupSettingsForm;
use PKP\context\Context;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\plugins\Hook;
use PKP\plugins\IPKPDoiRegistrationAgency;
use PKP\security\authorization\DoisEnabledPolicy;
use PKP\security\authorization\PolicySet;
use PKP\security\authorization\RoleBasedHandlerOperationPolicy;
use PKP\security\Role;
abstract class PKPDoisHandler extends Handler
{
public $_isBackendPage = true;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
['index', 'management']
);
}
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new \PKP\security\authorization\ContextRequiredPolicy($request, $roleAssignments));
// DOIs must be enabled to access DOI management page
$this->addPolicy(new DoisEnabledPolicy($request->getContext()));
$rolePolicy = new PolicySet(PolicySet::COMBINING_PERMIT_OVERRIDES);
foreach ($roleAssignments as $role => $operations) {
$rolePolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, $role, $operations));
}
$this->addPolicy($rolePolicy);
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Displays the DOI management page
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
$this->setupTemplate($request);
$context = $request->getContext();
$enabledDoiTypes = $context->getData(Context::SETTING_ENABLED_DOI_TYPES) ?? [];
$versionDois = $context->getData(Context::SETTING_DOI_VERSIONING) ?? false;
$templateMgr = TemplateManager::getManager($request);
$commonArgs = [
'doiPrefix' => $context->getData(Context::SETTING_DOI_PREFIX),
'doiApiUrl' => $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'dois'),
'lazyLoad' => true,
'enabledDoiTypes' => $enabledDoiTypes,
'versionDois' => $versionDois,
'registrationAgencyInfo' => $this->_getRegistrationAgencyInfo($context),
];
Hook::call('DoisHandler::setListPanelArgs', [&$commonArgs]);
$stateComponents = $this->getAppStateComponents($request, $enabledDoiTypes, $commonArgs);
// DOI settings
$router = $request->getRouter();
$dispatcher = $request->getDispatcher();
$contextApiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId());
$supportedFormLocales = $context->getSupportedFormLocaleNames();
$locales = array_map(fn (string $key, string $name) => ['key' => $key, 'label' => $name], array_keys($supportedFormLocales), $supportedFormLocales);
$doiSetupSettingsForm = new DoiSetupSettingsForm($contextApiUrl, $locales, $context);
$doiRegistrationSettingsForm = new PKPDoiRegistrationSettingsForm($contextApiUrl, $locales, $context);
$stateComponents[PKPDoiSetupSettingsForm::FORM_DOI_SETUP_SETTINGS] = $doiSetupSettingsForm->getConfig();
$stateComponents[PKPDoiRegistrationSettingsForm::FORM_DOI_REGISTRATION_SETTINGS] = $doiRegistrationSettingsForm->getConfig();
$templateMgr->setState(['components' => $stateComponents]);
$templateMgr->assign($this->getTemplateVariables($enabledDoiTypes));
$templateMgr->display('management/dois.tpl');
}
/**
* Set app-specific state components to appear on DOI management page
*/
abstract protected function getAppStateComponents(\APP\core\Request $request, array $enabledDoiTypes, array $commonArgs): array;
/**
* Set Smarty template variables. Which tabs to display are set by the APP.
*/
protected function getTemplateVariables(array $enabledDoiTypes): array
{
return [
'pageTitle' => __('doi.manager.displayName'),
'pageComponent' => 'DoiPage',
];
}
protected function _getRegistrationAgencyInfo(Context $context): \stdClass
{
$info = new \stdClass();
$info->isConfigured = false;
$info->displayName = '';
$info->errorMessageKey = null;
$info->registeredMessageKey = null;
$info->errorMessagePreamble = null;
$info->registeredMessagePreamble = null;
/** @var IPKPDoiRegistrationAgency $plugin */
$plugin = $context->getConfiguredDoiAgency();
if ($plugin != null) {
$info->isConfigured = $plugin->isPluginConfigured($context);
$info->displayName = $plugin->getRegistrationAgencyName();
$info->errorMessageKey = $plugin->getErrorMessageKey();
$info->registeredMessageKey = $plugin->getRegisteredMessageKey();
$info->errorMessagePreamble = __('manager.dois.registrationAgency.errorMessagePreamble', ['registrationAgency' => $plugin->getRegistrationAgencyName()]);
$info->registeredMessagePreamble = __('manager.dois.registrationAgency.registeredMessagePreamble', ['registrationAgency' => $plugin->getRegistrationAgencyName()]);
}
return $info;
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
/**
* @file pages/help/HelpHandler.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 HelpHandler
*
* @ingroup pages_help
*
* @brief Handle requests for help functions.
*/
namespace PKP\pages\help;
use APP\handler\Handler;
use PKP\core\JSONMessage;
use PKP\core\PKPRequest;
use PKP\facades\Locale;
use PKP\i18n\LocaleConversion;
class HelpHandler extends Handler
{
/**
* Display help.
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
$path = 'docs/manual/';
$urlPart = join('/', $request->getRequestedArgs());
$filename = $urlPart . '.md';
$language = LocaleConversion::getIso1FromLocale(Locale::getLocale());
$summaryFile = $path . $language . '/SUMMARY.md';
// Default to English
if (!file_exists($path . $language) || !file_exists($summaryFile) || filesize($summaryFile) == 0) {
$language = 'en';
}
if (!preg_match('#^([[a-zA-Z0-9_-]+/)+[a-zA-Z0-9_-]+\.\w+$#', $filename) || !file_exists($path . $filename)) {
$request->redirect(null, null, null, [$language, 'SUMMARY']);
}
// Use the summary document to find next/previous links.
// (Yes, we're grepping markdown outside the parser, but this is much faster.)
$previousLink = $nextLink = null;
if (preg_match_all('/\(([^)]+)\)/sm', file_get_contents($summaryFile), $matches)) {
$matches = $matches[1];
if (($i = array_search(substr($urlPart, strpos($urlPart, '/') + 1), $matches)) !== false) {
if ($i > 0) {
$previousLink = $matches[$i - 1];
}
if ($i < count($matches) - 1) {
$nextLink = $matches[$i + 1];
}
}
}
// Use a URL filter to prepend the current path to relative URLs.
$parser = new \Michelf\Markdown();
$parser->url_filter_func = function ($url) use ($filename) {
return (empty(parse_url($url)['host']) ? dirname($filename) . '/' : '') . $url;
};
return new JSONMessage(
true,
[
'content' => $parser->transform(file_get_contents($path . $filename)),
'previous' => $previousLink,
'next' => $nextLink,
]
);
}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* @defgroup pages_help Help Pages
*/
/**
* @file lib/pkp/pages/help/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_help
*
* @brief Handle requests for help functions.
*
*/
$op = 'index';
define('HANDLER_CLASS', 'PKP\pages\help\HelpHandler');
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* @file pages/index/IndexHandler.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 PKPIndexHandler
*
* @ingroup pages_index
*
* @brief Handle site index requests.
*/
namespace PKP\pages\index;
use APP\facades\Repo;
use APP\handler\Handler;
use Illuminate\Support\LazyCollection;
use PKP\config\Config;
use PKP\announcement\Collector;
use PKP\context\Context;
use PKP\site\Site;
use PKP\template\PKPTemplateManager;
class PKPIndexHandler extends Handler
{
/**
* Set up templates with announcement data.
*
* @protected
*
* @param Context $context
* @param PKPTemplateManager $templateMgr
*/
protected function _setupAnnouncements(Context|Site $contextOrSite, $templateMgr)
{
$enableAnnouncements = $contextOrSite->getData('enableAnnouncements');
$numAnnouncementsHomepage = $contextOrSite->getData('numAnnouncementsHomepage');
if ($enableAnnouncements && $numAnnouncementsHomepage) {
$collector = Repo::announcement()
->getCollector()
->filterByActive()
->limit((int) $numAnnouncementsHomepage);
if (is_a($contextOrSite, Context::class)) {
$collector->filterByContextIds([$contextOrSite->getId()]);
} else {
$collector->withSiteAnnouncements(Collector::SITE_ONLY);
}
$announcements = $collector->getMany();
$templateMgr->assign([
'announcements' => $announcements->toArray(),
'numAnnouncementsHomepage' => $numAnnouncementsHomepage,
]);
}
}
/**
* Get the Highlights for this context
*/
protected function getHighlights(?Context $context = null): LazyCollection
{
if (!Config::getVar('features', 'highlights')) {
return LazyCollection::make();
}
$collector = Repo::highlight()->getCollector();
if ($context) {
$collector->filterByContextIds([$context->getId()]);
} else {
$collector->withSiteHighlights($collector::SITE_ONLY);
}
return $collector->getMany();
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
/**
* @file pages/install/InstallHandler.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 InstallHandler
*
* @ingroup pages_install
*
* @brief Handle installation requests.
*/
namespace PKP\pages\install;
use APP\core\Application;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\core\PKPRequest;
use PKP\facades\Locale;
use PKP\install\form\InstallForm;
use PKP\install\form\UpgradeForm;
class InstallHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* If no context is selected, list all.
* Otherwise, display the index page for the selected context.
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
// Make sure errors are displayed to the browser during install.
@ini_set('display_errors', true);
$this->validate(null, $request);
$this->setupTemplate($request);
if (($setLocale = $request->getUserVar('setLocale')) != null && Locale::isLocaleValid($setLocale)) {
$request->setCookieVar('currentLocale', $setLocale);
}
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('installer.appInstallation'),
]);
$installForm = new InstallForm($request);
$installForm->initData();
$installForm->display($request);
}
/**
* Redirect to index if system has already been installed.
*
* @param PKPRequest $request
* @param null|mixed $requiredContexts
*/
public function validate($requiredContexts = null, $request = null)
{
if (Application::isInstalled()) {
$request->redirect(null, 'index');
}
}
/**
* Execute installer.
*
* @param array $args
* @param PKPRequest $request
*/
public function install($args, $request)
{
$this->validate(null, $request);
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$installForm = new InstallForm($request);
$installForm->readInputData();
if ($installForm->validate()) {
$templateMgr->assign([
'pageTitle' => __('installer.installationComplete'),
]);
$installForm->execute();
} else {
$templateMgr->assign([
'pageTitle' => __('installer.appInstallation'),
]);
$errors = $installForm->getErrorsArray();
$error = array_shift($errors);
$installForm->installError($error, false);
}
}
/**
* Display upgrade form.
*
* @param array $args
* @param PKPRequest $request
*/
public function upgrade($args, $request)
{
$this->validate(null, $request);
$this->setupTemplate($request);
if (($setLocale = $request->getUserVar('setLocale')) != null && Locale::isLocaleValid($setLocale)) {
$request->setCookieVar('currentLocale', $setLocale);
}
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('installer.upgradeApplication'),
]);
$installForm = new UpgradeForm($request);
$installForm->initData();
$installForm->display($request);
}
/**
* Execute upgrade.
*
* @param array $args
* @param PKPRequest $request
*/
public function installUpgrade($args, $request)
{
$this->validate(null, $request);
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('installer.upgradeApplication'),
]);
$installForm = new UpgradeForm($request);
$installForm->readInputData();
if ($installForm->validate()) {
$installForm->execute();
} else {
$installForm->display($request);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* @defgroup pages_install Installation Pages
*/
/**
* @file pages/install/index.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.
*
* @ingroup pages_install
*
* @brief Handle installation requests.
*
*/
switch ($op) {
case 'index':
case 'install':
case 'upgrade':
case 'installUpgrade':
define('HANDLER_CLASS', 'PKP\pages\install\InstallHandler');
break;
}
@@ -0,0 +1,121 @@
<?php
/**
* @file pages/libraryFiles/LibraryFileHandler.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 LibraryFileHandler
*
* @ingroup pages_libraryFiles
*
* @brief Class defining a handler for library file access
*/
namespace PKP\pages\libraryFiles;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\file\LibraryFileManager;
use APP\handler\Handler;
use PKP\context\LibraryFileDAO;
use PKP\db\DAORegistry;
use PKP\security\Role;
class LibraryFileHandler extends Handler
{
/** @var Handler the Handler that calls the LibraryFileHandler functions */
public $_callingHandler = null;
/**
* Constructor.
*
* @param Handler $callingHandler
*/
public function __construct($callingHandler)
{
$this->_callingHandler = $callingHandler;
}
//
// Public handler methods
//
/**
* Download a library public file.
*
* @param array $args
* @param Request $request
*/
public function downloadPublic($args, $request)
{
$context = $request->getContext();
$libraryFileManager = new LibraryFileManager($context->getId());
$libraryFileDao = DAORegistry::getDAO('LibraryFileDAO'); /** @var LibraryFileDAO $libraryFileDao */
$publicFileId = $args[0];
$libraryFile = $libraryFileDao->getById($publicFileId, $context->getId());
if ($libraryFile && $libraryFile->getPublicAccess()) {
$libraryFileManager->downloadByPath($libraryFile->getFilePath(), null, true);
} else {
header('HTTP/1.0 403 Forbidden');
echo '403 Forbidden<br>';
return;
}
}
/**
* Download a library file.
*
* @param array $args
* @param Request $request
*/
public function downloadLibraryFile($args, $request)
{
$context = $request->getContext();
$libraryFileManager = new LibraryFileManager($context->getId());
$libraryFileDao = DAORegistry::getDAO('LibraryFileDAO'); /** @var LibraryFileDAO $libraryFileDao */
$libraryFile = $libraryFileDao->getById($request->getUserVar('libraryFileId'), $context->getId());
if ($libraryFile) {
// If this file has a submission ID, ensure that the current
// user has access to that submission.
if ($libraryFile->getSubmissionId()) {
$allowedAccess = false;
// Managers are always allowed access.
if ($this->_callingHandler) {
$userRoles = $this->_callingHandler->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
if (array_intersect($userRoles, [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN])) {
$allowedAccess = true;
}
}
// Check for specific assignments.
$assignedUsers = Repo::user()->getCollector()
->assignedTo($libraryFile->getSubmissionId(), WORKFLOW_STAGE_ID_SUBMISSION)
->getMany();
$user = $request->getUser();
foreach ($assignedUsers as $assignedUser) {
if ($assignedUser->getId() == $user->getId()) {
$allowedAccess = true;
break;
}
}
} else {
$allowedAccess = true; // this is a Context submission document, default to access policy.
}
if ($allowedAccess) {
$libraryFileManager->downloadByPath($libraryFile->getFilePath());
} else {
header('HTTP/1.0 403 Forbidden');
echo '403 Forbidden<br>';
return;
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
/**
* @file lib/pkp/pages/publicLibraryFiles/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_publicLibraryFiles
*
* @brief Handle requests for public library files.
*
*/
switch ($op) {
case 'downloadPublic':
case 'downloadLibraryFile':
define('HANDLER_CLASS', 'PKP\pages\libraryFiles\LibraryFileHandler');
break;
}
+503
View File
@@ -0,0 +1,503 @@
<?php
/**
* @file pages/login/LoginHandler.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 LoginHandler
*
* @ingroup pages_login
*
* @brief Handle login/logout requests.
*/
namespace PKP\pages\login;
use APP\core\Application;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\template\TemplateManager;
use Exception;
use Illuminate\Support\Facades\Mail;
use PKP\config\Config;
use PKP\context\Context;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\core\PKPString;
use PKP\form\validation\FormValidatorReCaptcha;
use PKP\mail\mailables\PasswordResetRequested;
use PKP\security\authorization\RoleBasedHandlerOperationPolicy;
use PKP\security\Role;
use PKP\security\Validation;
use PKP\session\SessionManager;
use PKP\site\Site;
use PKP\user\form\LoginChangePasswordForm;
use PKP\user\form\ResetPasswordForm;
use PKP\user\User;
class LoginHandler extends Handler
{
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
switch ($op = $request->getRequestedOp()) {
case 'signInAsUser':
$this->addPolicy(new RoleBasedHandlerOperationPolicy($request, [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], ['signInAsUser']));
break;
}
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Display user login form.
* Redirect to user index page if user is already validated.
*/
public function index($args, $request)
{
$this->setupTemplate($request);
if (Validation::isLoggedIn()) {
$this->sendHome($request);
}
if (Config::getVar('security', 'force_login_ssl') && $request->getProtocol() != 'https') {
// Force SSL connections for login
$request->redirectSSL();
}
$sessionManager = SessionManager::getManager();
$session = $sessionManager->getUserSession();
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'loginMessage' => $request->getUserVar('loginMessage'),
'username' => $session->getSessionVar('email') ?? $session->getSessionVar('username'),
'remember' => $request->getUserVar('remember'),
'source' => $request->getUserVar('source'),
'showRemember' => Config::getVar('general', 'session_lifetime') > 0,
]);
// For force_login_ssl with base_url[...]: make sure SSL used for login form
$loginUrl = $request->url(null, 'login', 'signIn');
if (Config::getVar('security', 'force_login_ssl')) {
$loginUrl = PKPString::regexp_replace('/^http:/', 'https:', $loginUrl);
}
$templateMgr->assign('loginUrl', $loginUrl);
$isCaptchaEnabled = Config::getVar('captcha', 'recaptcha') && Config::getVar('captcha', 'captcha_on_login');
if ($isCaptchaEnabled) {
$templateMgr->assign('recaptchaPublicKey', Config::getVar('captcha', 'recaptcha_public_key'));
}
$templateMgr->display('frontend/pages/userLogin.tpl');
}
/**
* After a login has completed, direct the user somewhere.
*
* @param PKPRequest $request
*/
public function _redirectAfterLogin($request)
{
$context = $this->getTargetContext($request);
// If there's a context, send them to the dashboard after login.
if ($context && $request->getUserVar('source') == '' && array_intersect(
[Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_AUTHOR, Role::ROLE_ID_REVIEWER, Role::ROLE_ID_ASSISTANT],
(array) $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES)
)) {
return $request->redirect($context->getPath(), 'dashboard');
}
$request->getRouter()->redirectHome($request);
}
/**
* Validate a user's credentials and log the user in.
*/
public function signIn($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
if (Validation::isLoggedIn()) {
$this->sendHome($request);
}
if (Config::getVar('security', 'force_login_ssl') && $request->getProtocol() != 'https') {
// Force SSL connections for login
$request->redirectSSL();
}
$error = null;
$isCaptchaEnabled = Config::getVar('captcha', 'captcha_on_login') && Config::getVar('captcha', 'recaptcha');
if ($isCaptchaEnabled) {
$templateMgr->assign('recaptchaPublicKey', Config::getVar('captcha', 'recaptcha_public_key'));
try {
FormValidatorReCaptcha::validateResponse($request->getUserVar('g-recaptcha-response'), $request->getRemoteAddr(), $request->getServerHost());
} catch (Exception $exception) {
$error = 'common.captcha.error.missing-input-response';
}
}
$username = $request->getUserVar('username');
$reason = null;
$user = $error || !strlen($username ?? '')
? null
: Validation::login($username, $request->getUserVar('password'), $reason, !!$request->getUserVar('remember'));
if ($user) {
if ($user->getMustChangePassword()) {
// User must change their password in order to log in
Validation::logout();
$request->redirect(null, null, 'changePassword', $user->getUsername());
}
$source = $request->getUserVar('source');
if (preg_match('#^/\w#', (string) $source) === 1) {
$request->redirectUrl($source);
}
$redirectNonSsl = Config::getVar('security', 'force_login_ssl') && !Config::getVar('security', 'force_ssl');
if ($redirectNonSsl) {
$request->redirectNonSSL();
}
$this->_redirectAfterLogin($request);
}
if ($reason) {
$error = 'user.login.accountDisabledWithReason';
} elseif ($reason !== null) {
$error = 'user.login.accountDisabled';
}
$error ??= 'user.login.loginError';
$templateMgr->assign([
'username' => $username,
'remember' => $request->getUserVar('remember'),
'source' => $request->getUserVar('source'),
'showRemember' => Config::getVar('general', 'session_lifetime') > 0,
'error' => $error,
'reason' => $reason,
]);
$templateMgr->display('frontend/pages/userLogin.tpl');
}
/**
* Log a user out.
*/
public function signOut($args, $request)
{
$this->setupTemplate($request);
if (Validation::isLoggedIn()) {
Validation::logout();
}
$source = $request->getUserVar('source');
if (isset($source) && !empty($source)) {
$request->redirectUrl($request->getProtocol() . '://' . $request->getServerHost() . $source, false);
} else {
$request->redirect(null, $request->getRequestedPage());
}
}
/**
* Display form to reset a user's password.
*/
public function lostPassword($args, $request)
{
if (Validation::isLoggedIn()) {
$this->sendHome($request);
}
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->display('frontend/pages/userLostPassword.tpl');
}
/**
* Send a request to reset a user's password
*/
public function requestResetPassword($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$email = $request->getUserVar('email');
$user = Repo::user()->getByEmail($email, true); /** @var User $user */
if ($user !== null) {
if ($user->getDisabled()) {
$templateMgr
->assign([
'error' => 'user.login.lostPassword.confirmationSentFailedWithReason',
'reason' => empty($reason = $user->getDisabledReason() ?? '')
? __('user.login.accountDisabled')
: __('user.login.accountDisabledWithReason', ['reason' => htmlspecialchars($reason)])
])
->display('frontend/pages/userLostPassword.tpl');
return;
}
// Send email confirming password reset
$site = $request->getSite(); /** @var Site $site */
$context = $request->getContext(); /** @var Context $context */
$template = Repo::emailTemplate()->getByKey(
$context ? $context->getId() : PKPApplication::CONTEXT_SITE,
PasswordResetRequested::getEmailTemplateKey()
);
$mailable = (new PasswordResetRequested($site))
->recipients($user)
->from($site->getLocalizedContactEmail(), $site->getLocalizedContactName())
->body($template->getLocalizedData('body'))
->subject($template->getLocalizedData('subject'));
Mail::send($mailable);
}
$templateMgr->assign([
'pageTitle' => 'user.login.resetPassword',
'message' => 'user.login.lostPassword.confirmationSent',
'backLink' => $request->url(null, $request->getRequestedPage(), null, null),
'backLinkLabel' => 'user.login',
])->display('frontend/pages/message.tpl');
}
/**
* Present the password reset form to reset user's password
*
* @param array $args first param contains the username of the user whose password is to be reset
*/
public function resetPassword($args, $request)
{
if (Validation::isLoggedIn()) {
$this->sendHome($request);
}
$this->_isBackendPage = true;
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setupBackendPage();
$templateMgr->assign([
'pageTitle' => 'user.login.resetPassword',
]);
$username = $args[0] ?? null;
$confirmHash = $request->getUserVar('confirm');
if ($username == null || ($user = Repo::user()->getByUsername($username, true)) == null) {
return $request->redirect(null, null, 'lostPassword');
}
if ($user->getDisabled()) {
$templateMgr
->assign([
'backLink' => $request->url(null, $request->getRequestedPage()),
'backLinkLabel' => 'user.login',
'messageTranslated' => __('user.login.lostPassword.confirmationSentFailedWithReason', [
'reason' => empty($reason = $user->getDisabledReason() ?? '')
? __('user.login.accountDisabled')
: __('user.login.accountDisabledWithReason', ['reason' => htmlspecialchars($reason)])
]),
])
->display('frontend/pages/message.tpl');
return;
}
$passwordResetForm = new ResetPasswordForm($user, $request->getSite(), $confirmHash);
$passwordResetForm->initData();
$passwordResetForm->validatePasswordResetHash()
? $passwordResetForm->display($request)
: $passwordResetForm->displayInvalidHashErrorMessage($request);
}
/**
* Reset a user's password
*
* @param array $args first param contains the username of the user whose password is to be reset
*/
public function updateResetPassword($args, $request)
{
$this->_isBackendPage = true;
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$username = $request->getUserVar('username');
$confirmHash = $request->getUserVar('hash');
if ($username == null || ($user = Repo::user()->getByUsername($username, true)) == null) {
return $request->redirect(null, null, 'lostPassword');
}
$passwordResetForm = new ResetPasswordForm($user, $request->getSite(), $confirmHash);
$passwordResetForm->readInputData();
if (!$passwordResetForm->validatePasswordResetHash()) {
return $passwordResetForm->displayInvalidHashErrorMessage($request);
}
if ($passwordResetForm->validate()) {
if ($passwordResetForm->execute()) {
$templateMgr->assign([
'pageTitle' => 'user.login.resetPassword',
'message' => 'user.login.resetPassword.passwordUpdated',
'backLink' => $request->url(null, $request->getRequestedPage(), null, null, ['username' => $user->getUsername()]),
'backLinkLabel' => 'user.login',
]);
$templateMgr->display('frontend/pages/message.tpl');
}
} else {
$passwordResetForm->display($request);
}
}
/**
* Display form to change user's password.
*
* @param array $args first argument may contain user's username
*/
public function changePassword($args, $request)
{
$this->_isBackendPage = true;
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setupBackendPage();
$templateMgr->assign([
'pageTitle' => __('user.changePassword'),
]);
$passwordForm = new LoginChangePasswordForm($request->getSite());
$passwordForm->initData();
if (isset($args[0])) {
$passwordForm->setData('username', $args[0]);
}
$passwordForm->display($request);
}
/**
* Save user's new password.
*/
public function savePassword($args, $request)
{
$this->_isBackendPage = true;
$this->setupTemplate($request);
$passwordForm = new LoginChangePasswordForm($request->getSite());
$passwordForm->readInputData();
if ($passwordForm->validate()) {
if ($passwordForm->execute()) {
$user = Validation::login($passwordForm->getData('username'), $passwordForm->getData('password'), $reason);
$sessionManager = SessionManager::getManager();
$sessionManager->invalidateSessions($user->getId(), $sessionManager->getUserSession()->getId());
}
$this->sendHome($request);
} else {
$passwordForm->display($request);
}
}
/**
* Sign in as another user.
*
* @param array $args ($userId)
* @param PKPRequest $request
*/
public function signInAsUser($args, $request)
{
if (isset($args[0]) && !empty($args[0])) {
$userId = (int)$args[0];
$session = $request->getSession();
if (Validation::getAdministrationLevel($userId, $session->getUserId()) !== Validation::ADMINISTRATION_FULL) {
$this->setupTemplate($request);
// We don't have administrative rights
// over this user. Display an error.
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => 'manager.people',
'errorMsg' => 'manager.people.noAdministrativeRights',
'backLink' => $request->url(null, null, 'people', 'all'),
'backLinkLabel' => 'manager.people.allUsers',
]);
return $templateMgr->display('frontend/pages/error.tpl');
}
$newUser = Repo::user()->get($userId, true);
if (isset($newUser) && $session->getUserId() != $newUser->getId()) {
$session->setSessionVar('signedInAs', $session->getUserId());
$session->setSessionVar('userId', $userId);
$session->setUserId($userId);
$session->setSessionVar('username', $newUser->getUsername());
$this->_redirectByURL($request);
}
}
$request->redirect(null, $request->getRequestedPage());
}
/**
* Restore original user account after signing in as a user.
*
* @param array $args
* @param PKPRequest $request
*/
public function signOutAsUser($args, $request)
{
$session = $request->getSession();
$signedInAs = $session->getSessionVar('signedInAs');
if (isset($signedInAs) && !empty($signedInAs)) {
$signedInAs = (int)$signedInAs;
$oldUser = Repo::user()->get($signedInAs, true);
$session->unsetSessionVar('signedInAs');
if (isset($oldUser)) {
$session->setSessionVar('userId', $signedInAs);
$session->setUserId($signedInAs);
$session->setSessionVar('username', $oldUser->getUsername());
}
}
$this->_redirectByURL($request);
}
/**
* Redirect to redirectURL if exists else send to Home
*
* @param PKPRequest $request
*/
public function _redirectByURL($request)
{
$requestVars = $request->getUserVars();
if (isset($requestVars['redirectUrl']) && !empty($requestVars['redirectUrl'])) {
$request->redirectUrl($requestVars['redirectUrl']);
} else {
$this->sendHome($request);
}
}
/**
* Send the user "home" (typically to the dashboard, but that may not
* always be available).
*
* @param PKPRequest $request
*/
protected function sendHome($request)
{
if ($request->getContext()) {
$request->redirect(null, 'submissions');
} else {
$request->redirect(null, 'user');
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
* @defgroup pages_login Login Pages
*/
/**
* @file lib/pkp/pages/login/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @brief Handle login/logout requests.
*
* @ingroup pages_login
*/
switch ($op) {
case 'index':
case 'signIn':
case 'signOut':
case 'lostPassword':
case 'requestResetPassword':
case 'resetPassword':
case 'updateResetPassword':
case 'changePassword':
case 'savePassword':
case 'signInAsUser':
case 'signOutAsUser':
define('HANDLER_CLASS', 'PKP\pages\login\LoginHandler');
break;
}
@@ -0,0 +1,700 @@
<?php
/**
* @file pages/management/ManagementHandler.php
*
* Copyright (c) 2013-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 ManagementHandler
*
* @ingroup pages_management
*
* @brief Base class for all management page handlers.
*/
namespace PKP\pages\management;
use APP\components\forms\context\DoiSetupSettingsForm;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\file\PublicFileManager;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\components\forms\announcement\PKPAnnouncementForm;
use PKP\components\forms\context\PKPDoiRegistrationSettingsForm;
use PKP\components\forms\context\PKPEmailSetupForm;
use PKP\components\forms\context\PKPInformationForm;
use PKP\components\forms\context\PKPNotifyUsersForm;
use PKP\components\forms\context\PKPReviewSetupForm;
use PKP\components\forms\emailTemplate\EmailTemplateForm;
use PKP\components\forms\highlight\HighlightForm;
use PKP\components\forms\submission\SubmissionGuidanceSettings;
use PKP\components\listPanels\HighlightsListPanel;
use PKP\config\Config;
use PKP\context\Context;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\mail\Mailable;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\Role;
use PKP\site\VersionCheck;
class ManagementHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
//
// Overridden methods from Handler
//
/**
* @see PKPHandler::initialize()
*/
public function initialize($request)
{
parent::initialize($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('pageComponent', 'SettingsPage');
}
/**
* @see PKPHandler::authorize()
*
* @param PKPRequest $request
* @param array $args
* @param array $roleAssignments
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Route requests to the appropriate operation.
*
* @param array $args
* @param PKPRequest $request
*/
public function settings($args, $request)
{
$path = array_shift($args);
switch ($path) {
case 'index':
case '':
case 'context':
$this->context($args, $request);
break;
case 'website':
$this->website($args, $request);
break;
case 'workflow':
$this->workflow($args, $request);
break;
case 'manageEmails':
$this->manageEmails($args, $request);
break;
case 'distribution':
$this->distribution($args, $request);
break;
case 'access':
$this->access($args, $request);
break;
case 'announcements':
$this->announcements($args, $request);
break;
case 'institutions':
$this->institutions($args, $request);
break;
default:
assert(false);
$request->getDispatcher()->handle404();
}
}
/**
* Display settings for a journal/press
*
* @param array $args
* @param PKPRequest $request
*/
public function context($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
$apiUrl = $this->getContextApiUrl($request);
$publicFileApiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), '_uploadPublicFile');
$locales = $this->getSupportedFormLocales($context);
$contactForm = new \PKP\components\forms\context\PKPContactForm($apiUrl, $locales, $context);
$mastheadForm = new \APP\components\forms\context\MastheadForm($apiUrl, $locales, $context, $publicFileApiUrl);
$templateMgr->setState([
'components' => [
FORM_CONTACT => $contactForm->getConfig(),
FORM_MASTHEAD => $mastheadForm->getConfig(),
],
]);
// Interact with the beacon (if enabled) and determine if a new version exists
$latestVersion = VersionCheck::checkIfNewVersionExists();
// Display a warning message if there is a new version of OJS available
if (Config::getVar('general', 'show_upgrade_warning') && $latestVersion) {
$currentVersion = VersionCheck::getCurrentDBVersion();
$templateMgr->assign([
'newVersionAvailable' => true,
'currentVersion' => $currentVersion->getVersionString(),
'latestVersion' => $latestVersion,
]);
// Get contact information for site administrator
$userGroups = Repo::userGroup()->getByRoleIds([Role::ROLE_ID_SITE_ADMIN], PKPApplication::CONTEXT_SITE);
$adminUserGroup = $userGroups->first();
$siteAdmin = Repo::user()->getCollector()
->filterByUserGroupIds([$adminUserGroup->getId()])
->getMany()
->first();
$templateMgr->assign('siteAdmin', $siteAdmin);
}
$templateMgr->assign('pageTitle', __('manager.setup'));
$templateMgr->display('management/context.tpl');
}
/**
* Display website settings
*
* @param array $args
* @param PKPRequest $request
*/
public function website($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
$router = $request->getRouter();
$contextApiUrl = $this->getContextApiUrl($request);
$themeApiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId() . '/theme');
$temporaryFileApiUrl = $this->getTemporaryFileApiUrl($context);
$publicFileApiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), '_uploadPublicFile');
$publicFileManager = new PublicFileManager();
$baseUrl = $request->getBaseUrl() . '/' . $publicFileManager->getContextFilesPath($context->getId());
$locales = $this->getSupportedFormLocales($context);
$announcementSettingsForm = new \PKP\components\forms\context\PKPAnnouncementSettingsForm($contextApiUrl, $locales, $context);
$appearanceAdvancedForm = new \APP\components\forms\context\AppearanceAdvancedForm($contextApiUrl, $locales, $context, $baseUrl, $temporaryFileApiUrl, $publicFileApiUrl);
$appearanceSetupForm = new \APP\components\forms\context\AppearanceSetupForm($contextApiUrl, $locales, $context, $baseUrl, $temporaryFileApiUrl, $publicFileApiUrl);
$informationForm = $this->getInformationForm($contextApiUrl, $locales, $context, $publicFileApiUrl);
$listsForm = new \PKP\components\forms\context\PKPListsForm($contextApiUrl, $locales, $context);
$privacyForm = new \PKP\components\forms\context\PKPPrivacyForm($contextApiUrl, $locales, $context, $publicFileApiUrl);
$themeForm = new \PKP\components\forms\context\PKPThemeForm($themeApiUrl, $locales, $context);
$dateTimeForm = new \PKP\components\forms\context\PKPDateTimeForm($contextApiUrl, $locales, $context);
$templateMgr->setConstants([
'FORM_ANNOUNCEMENT_SETTINGS' => FORM_ANNOUNCEMENT_SETTINGS,
]);
$components = [
FORM_ANNOUNCEMENT_SETTINGS => $announcementSettingsForm->getConfig(),
FORM_APPEARANCE_ADVANCED => $appearanceAdvancedForm->getConfig(),
FORM_APPEARANCE_SETUP => $appearanceSetupForm->getConfig(),
FORM_LISTS => $listsForm->getConfig(),
FORM_PRIVACY => $privacyForm->getConfig(),
FORM_THEME => $themeForm->getConfig(),
FORM_DATE_TIME => $dateTimeForm->getConfig(),
];
if (Config::getVar('features', 'highlights')) {
$highlightsListPanel = $this->getHighlightsListPanel();
$components[$highlightsListPanel->id] = $highlightsListPanel->getConfig();
$templateMgr->assign('enableHighlights', true);
}
if ($informationForm) {
$components[FORM_INFORMATION] = $informationForm->getConfig();
}
$templateMgr->setState([
'components' => $components,
'announcementsNavLink' => [
'name' => __('announcement.announcements'),
'url' => $router->url($request, null, 'management', 'settings', 'announcements'),
'isCurrent' => false,
],
]);
$templateMgr->assign([
'includeInformationForm' => (bool) $informationForm,
'pageTitle' => __('manager.website.title'),
]);
$templateMgr->display('management/website.tpl');
}
/**
* Display workflow settings
*
* @param array $args
* @param PKPRequest $request
*/
public function workflow($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
$contextApiUrl = $this->getContextApiUrl($request);
$locales = $this->getSupportedFormLocales($context);
$disableSubmissionsForm = new \PKP\components\forms\context\PKPDisableSubmissionsForm($contextApiUrl, $locales, $context);
$emailSetupForm = $this->getEmailSetupForm($contextApiUrl, $locales, $context);
$metadataSettingsForm = new \APP\components\forms\context\MetadataSettingsForm($contextApiUrl, $context);
$submissionGuidanceSettingsForm = new SubmissionGuidanceSettings($contextApiUrl, $locales, $context);
$templateMgr->setState([
'components' => [
FORM_DISABLE_SUBMISSIONS => $disableSubmissionsForm->getConfig(),
$emailSetupForm->id => $emailSetupForm->getConfig(),
FORM_METADATA_SETTINGS => $metadataSettingsForm->getConfig(),
$submissionGuidanceSettingsForm->id => $submissionGuidanceSettingsForm->getConfig(),
],
]);
$templateMgr->assign([
'pageTitle' => __('manager.workflow.title'),
'hasReviewStage' => $this->hasReviewStage(),
]);
}
/**
* Display distribution settings
*
* @param array $args
* @param PKPRequest $request
*/
public function distribution($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$router = $request->getRouter();
$dispatcher = $request->getDispatcher();
$apiUrl = $this->getContextApiUrl($request);
$doiRegistrationSettingsApiUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId() . '/registrationAgency');
$sitemapUrl = $router->url($request, $context->getPath(), 'sitemap');
$paymentsUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), '_payments');
$locales = $this->getSupportedFormLocales($context);
$licenseForm = new \APP\components\forms\context\LicenseForm($apiUrl, $locales, $context);
$doiSetupSettingsForm = new DoiSetupSettingsForm($apiUrl, $locales, $context);
$doiRegistrationSettingsForm = new PKPDoiRegistrationSettingsForm($doiRegistrationSettingsApiUrl, $locales, $context);
$searchIndexingForm = new \PKP\components\forms\context\PKPSearchIndexingForm($apiUrl, $locales, $context, $sitemapUrl);
$paymentSettingsForm = new \PKP\components\forms\context\PKPPaymentSettingsForm($paymentsUrl, $locales, $context);
$site = $request->getSite();
$contextStatisticsForm = new \PKP\components\forms\context\PKPContextStatisticsForm($apiUrl, $locales, $site, $context);
$displayStatisticsTab = ($site->getData('enableGeoUsageStats') && $site->getData('enableGeoUsageStats') !== 'disabled') ||
$site->getData('enableInstitutionUsageStats') ||
($site->getData('isSushiApiPublic') === null || $site->getData('isSushiApiPublic'));
$templateMgr->setConstants([
'FORM_PAYMENT_SETTINGS' => FORM_PAYMENT_SETTINGS,
'FORM_CONTEXT_STATISTICS' => FORM_CONTEXT_STATISTICS,
'FORM_DOI_REGISTRATION_SETTINGS' => PKPDoiRegistrationSettingsForm::FORM_DOI_REGISTRATION_SETTINGS,
]);
$templateMgr->setState([
'components' => [
FORM_LICENSE => $licenseForm->getConfig(),
\PKP\components\forms\context\PKPDoiSetupSettingsForm::FORM_DOI_SETUP_SETTINGS => $doiSetupSettingsForm->getConfig(),
PKPDoiRegistrationSettingsForm::FORM_DOI_REGISTRATION_SETTINGS => $doiRegistrationSettingsForm->getConfig(),
FORM_SEARCH_INDEXING => $searchIndexingForm->getConfig(),
FORM_PAYMENT_SETTINGS => $paymentSettingsForm->getConfig(),
FORM_CONTEXT_STATISTICS => $contextStatisticsForm->getConfig(),
],
// Add an institutions link to be added/removed when statistics form is submitted
'institutionsNavLink' => [
'name' => __('institution.institutions'),
'url' => $router->url($request, null, 'management', 'settings', 'institutions'),
'isCurrent' => false,
],
]);
$templateMgr->assign([
'pageTitle' => __('manager.distribution.title'),
'displayStatisticsTab' => $displayStatisticsTab,
]);
}
/**
* Display list of announcements and announcement types
*
* @param array $args
* @param PKPRequest $request
*/
public function announcements($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$apiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $request->getContext()->getPath(), 'announcements');
$context = $request->getContext();
$locales = $this->getSupportedFormLocales($context);
$announcementForm = new PKPAnnouncementForm(
$apiUrl,
$locales,
Repo::announcement()->getFileUploadBaseUrl($context),
$this->getTemporaryFileApiUrl($context),
$request->getContext()
);
$collector = Repo::announcement()
->getCollector()
->filterByContextIds([$request->getContext()->getId()]);
$itemsMax = $collector->getCount();
$items = Repo::announcement()->getSchemaMap()->summarizeMany(
$collector->limit(30)->getMany()
);
$announcementsListPanel = new \PKP\components\listPanels\PKPAnnouncementsListPanel(
'announcements',
__('manager.setup.announcements'),
[
'apiUrl' => $apiUrl,
'form' => $announcementForm,
'getParams' => [
'contextIds' => [$request->getContext()->getId()],
'count' => 30,
],
'items' => $items->values(),
'itemsMax' => $itemsMax,
]
);
$templateMgr->setState([
'components' => [
$announcementsListPanel->id => $announcementsListPanel->getConfig(),
],
]);
$templateMgr->assign([
'pageTitle' => __('manager.setup.announcements'),
]);
$templateMgr->display('management/announcements.tpl');
}
/**
* Display list of institutions
*/
public function institutions(array $args, Request $request): void
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$apiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $request->getContext()->getPath(), 'institutions');
$context = $request->getContext();
$locales = $this->getSupportedFormLocales($context);
$institutionForm = new \PKP\components\forms\institution\PKPInstitutionForm($apiUrl, $locales);
$collector = Repo::institution()
->getCollector()
->filterByContextIds([$request->getContext()->getId()]);
$itemsMax = $collector->getCount();
$items = Repo::institution()->getSchemaMap()->summarizeMany(
$collector->limit(30)->getMany()
);
$institutionsListPanel = new \PKP\components\listPanels\PKPInstitutionsListPanel(
'institutions',
__('manager.setup.institutions'),
[
'apiUrl' => $apiUrl,
'form' => $institutionForm,
'getParams' => [
'contextIds' => [$request->getContext()->getId()],
'count' => 30,
],
'items' => $items->values(),
'itemsMax' => $itemsMax,
]
);
$templateMgr->setState([
'components' => [
$institutionsListPanel->id => $institutionsListPanel->getConfig(),
],
]);
$templateMgr->assign([
'pageTitle' => __('manager.setup.institutions'),
]);
$templateMgr->display('management/institutions.tpl');
}
/**
* Display Access and Security page.
*
* @param array $args
* @param PKPRequest $request
*/
public function access($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
$apiUrl = $this->getContextApiUrl($request);
$notifyUrl = $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), '_email');
$userAccessForm = new \APP\components\forms\context\UserAccessForm($apiUrl, $context);
$isBulkEmailsEnabled = in_array($context->getId(), (array) $request->getSite()->getData('enableBulkEmails'));
$notifyUsersForm = $isBulkEmailsEnabled ? new PKPNotifyUsersForm($notifyUrl, $context) : null;
$templateMgr->assign([
'pageComponent' => 'AccessPage',
'pageTitle' => __('navigation.access'),
'enableBulkEmails' => $isBulkEmailsEnabled,
]);
$templateMgr->setConstants([
'FORM_NOTIFY_USERS' => PKPNotifyUsersForm::FORM_NOTIFY_USERS,
]);
$templateMgr->setState([
'components' => [
FORM_USER_ACCESS => $userAccessForm->getConfig(),
PKPNotifyUsersForm::FORM_NOTIFY_USERS => $notifyUsersForm ? $notifyUsersForm->getConfig() : null,
],
]);
$templateMgr->display('management/access.tpl');
}
/**
* Display the page to manage emails
*/
public function manageEmails(array $args, Request $request): void
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$emailTemplatesApiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'emailTemplates');
$mailablesApiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'mailables');
$templateMgr->assign([
'pageComponent' => 'ManageEmailsPage',
'pageTitle' => __('manager.manageEmails'),
]);
$templateMgr->setState([
'fromFilters' => $this->getEmailFromFilters(),
'groupFilters' => $this->getEmailGroupFilters(),
'i18nRemoveTemplate' => __('manager.mailables.removeTemplate'),
'i18nRemoveTemplateMessage' => __('manager.mailables.removeTemplate.confirm'),
'i18nResetTemplate' => __('manager.mailables.resetTemplate'),
'i18nResetTemplateMessage' => __('manager.mailables.resetTemplate.confirm'),
'i18nResetAll' => __('manager.emails.resetAll'),
'i18nResetAllMessage' => __('manager.emails.resetAll.message'),
'mailables' => Repo::mailable()->getMany($context, null, false, true)
->map(fn (string $class) => Repo::mailable()->summarizeMailable($class))
->sortBy('name')
->values(),
'mailablesApiUrl' => $mailablesApiUrl,
'templatesApiUrl' => $emailTemplatesApiUrl,
'templateForm' => $this->getEmailTemplateForm($context, $emailTemplatesApiUrl)->getConfig(),
'toFilters' => $this->getEmailToFilters(),
]);
$templateMgr->display('management/manageEmails.tpl');
}
protected function getEmailTemplateForm(Context $context, string $apiUrl): EmailTemplateForm
{
$locales = $context->getSupportedFormLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
return new EmailTemplateForm($apiUrl, $locales);
}
protected function getEmailGroupFilters(): array
{
return [
Mailable::GROUP_SUBMISSION => __('submission.submission'),
Mailable::GROUP_REVIEW => __('submission.review'),
Mailable::GROUP_COPYEDITING => __('submission.copyediting'),
Mailable::GROUP_PRODUCTION => __('submission.production'),
Mailable::GROUP_OTHER => __('common.other'),
];
}
protected function getEmailFromFilters(): array
{
return [
Role::ROLE_ID_SUB_EDITOR => __('user.role.editor'),
Role::ROLE_ID_REVIEWER => __('user.role.reviewer'),
Role::ROLE_ID_ASSISTANT => __('user.role.assistant'),
Role::ROLE_ID_READER => __('user.role.reader'),
Mailable::FROM_SYSTEM => __('mailable.system'),
];
}
protected function getEmailToFilters(): array
{
return [
Role::ROLE_ID_SUB_EDITOR => __('user.role.editor'),
Role::ROLE_ID_REVIEWER => __('user.role.reviewer'),
Role::ROLE_ID_ASSISTANT => __('user.role.assistant'),
Role::ROLE_ID_AUTHOR => __('user.role.author'),
Role::ROLE_ID_READER => __('user.role.reader'),
];
}
protected function getEmailSetupForm(string $contextApiUrl, array $locales, Context $context): PKPEmailSetupForm
{
return new PKPEmailSetupForm($contextApiUrl, $locales, $context);
}
protected function hasReviewStage(): bool
{
return count(
array_intersect(
[WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
Application::get()->getApplicationStages()
)
);
}
protected function getInformationForm(string $contextApiUrl, array $locales, Context $context, string $publicFileApiUrl): ?PKPInformationForm
{
return new PKPInformationForm(
$contextApiUrl,
$locales,
$context,
$publicFileApiUrl
);
}
/**
* Return Context API Url
*/
protected function getContextApiUrl(PKPRequest $request): string
{
$context = $request->getContext();
$dispatcher = $request->getDispatcher();
return $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'contexts/' . $context->getId());
}
/**
* Return context's supportedFormLocales
*/
protected function getSupportedFormLocales(Context $context): array
{
$locales = $context->getSupportedFormLocaleNames();
return array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
}
/**
* Add support for review related forms in workflow.
*/
protected function addReviewFormWorkflowSupport(PKPRequest $request): void
{
$templateMgr = TemplateManager::getManager($request);
$context = $request->getContext();
$contextApiUrl = $this->getContextApiUrl($request);
$locales = $this->getSupportedFormLocales($context);
$reviewGuidanceForm = new \APP\components\forms\context\ReviewGuidanceForm($contextApiUrl, $locales, $context);
$reviewSetupForm = new PKPReviewSetupForm($contextApiUrl, $locales, $context);
$components = $templateMgr->getState('components');
$components[$reviewGuidanceForm->id] = $reviewGuidanceForm->getConfig();
$components[$reviewSetupForm->id] = $reviewSetupForm->getConfig();
$templateMgr->setState(['components' => $components]);
}
/**
* Get the HighlightsListPanel
*/
protected function getHighlightsListPanel(): HighlightsListPanel
{
$request = Application::get()->getRequest();
$context = $request->getContext();
$apiUrl = $request->getDispatcher()->url(
$request,
Application::ROUTE_API,
$context->getPath(),
'highlights'
);
$highlightForm = new HighlightForm(
$apiUrl,
Repo::highlight()->getFileUploadBaseUrl($context),
$this->getTemporaryFileApiUrl($context),
$context
);
$items = Repo::highlight()
->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
return new HighlightsListPanel(
'highlights',
__('common.highlights'),
[
'apiUrl' => $apiUrl,
'form' => $highlightForm,
'items' => Repo::highlight()
->getSchemaMap()
->summarizeMany($items)
->values(),
'itemsMax' => $items->count(),
]
);
}
/**
* Get the API url for uploading temporary files
*/
protected function getTemporaryFileApiUrl(Context $context): string
{
return Application::get()
->getDispatcher()
->url(
Application::get()->getRequest(),
Application::ROUTE_API,
$context->getPath(),
'temporaryFiles'
);
}
}
@@ -0,0 +1,158 @@
<?php
/**
* @file pages/management/PKPToolsHandler.php
*
* Copyright (c) 2013-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 PKPToolsHandler
*
* @ingroup pages_management
*
* @brief Handle requests for Tool pages.
*/
namespace PKP\pages\management;
use APP\facades\Repo;
use APP\notification\NotificationManager;
use APP\template\TemplateManager;
use PKP\core\JSONMessage;
use PKP\core\PKPRequest;
use PKP\notification\PKPNotification;
use PKP\plugins\ImportExportPlugin;
use PKP\plugins\PluginRegistry;
use PKP\security\Role;
define('IMPORTEXPORT_PLUGIN_CATEGORY', 'importexport');
class PKPToolsHandler extends ManagementHandler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN],
['tools', 'importexport', 'permissions']
);
}
/**
* Route to other Tools operations
*
* @param array $args
* @param PKPRequest $request
*/
public function tools($args, $request)
{
$path = array_shift($args);
switch ($path) {
case '':
case 'index':
$this->index($args, $request);
break;
case 'permissions':
$this->permissions($args, $request);
break;
case 'resetPermissions':
$this->resetPermissions($args, $request);
break;
default: assert(false);
}
}
/**
* Display tools index page.
*
* @param PKPRequest $request
* @param array $args
*/
public function index($args, $request)
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$templateMgr->assign('pageTitle', __('navigation.tools'));
$templateMgr->display('management/tools/index.tpl');
}
/**
* Import or export data.
*
* @param array $args
* @param PKPRequest $request
*/
public function importexport($args, $request)
{
$this->setupTemplate($request);
PluginRegistry::loadCategory(IMPORTEXPORT_PLUGIN_CATEGORY);
$templateMgr = TemplateManager::getManager($request);
if (array_shift($args) === 'plugin') {
$pluginName = array_shift($args);
/** @var ImportExportPlugin */
$plugin = PluginRegistry::getPlugin(IMPORTEXPORT_PLUGIN_CATEGORY, $pluginName);
if ($plugin) {
return $plugin->display($args, $request);
}
}
$templateMgr->assign('plugins', PluginRegistry::getPlugins(IMPORTEXPORT_PLUGIN_CATEGORY));
return $templateMgr->fetchJson('management/tools/importexport.tpl');
}
//
// Protected methods.
//
/**
* Display the permissions area.
*
* @param array $args
* @param PKPRequest $request
*/
public function permissions($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
return $templateMgr->fetchJson('management/tools/permissions.tpl');
}
/**
* Reset article/monograph permissions
*
* @param array $args
* @param PKPRequest $request
*/
public function resetPermissions($args, $request)
{
if (!$request->checkCSRF()) {
return new JSONMessage(false);
}
$context = $request->getContext();
if (!$context) {
return;
}
Repo::submission()->resetPermissions($context->getId());
$user = $request->getUser();
$notificationManager = new NotificationManager();
$notificationManager->createTrivialNotification($user->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS, ['contents' => __('manager.setup.resetPermissions.success')]);
// This is an ugly hack to force the PageHandler to return JSON, so this
// method can communicate properly with the AjaxFormHandler. Returning a
// JSONMessage, or JSONMessage::toString(), doesn't seem to do it.
echo json_encode(true);
exit;
}
}
@@ -0,0 +1,153 @@
<?php
/**
* @file pages/navigationMenu/NavigationMenuItemHandler.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 NavigationMenuItemHandler
*
* @ingroup pages_navigationMenu
*
* @brief Handle requests for navigationMenuItem functions.
*/
namespace PKP\pages\navigationMenu;
use APP\core\Application;
use APP\core\Services;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\navigationMenu\NavigationMenuItem;
use PKP\navigationMenu\NavigationMenuItemDAO;
use PKP\security\Role;
class NavigationMenuItemHandler extends Handler
{
/** @var NavigationMenuItem The nmi to view */
public $nmi;
public function __construct($nmi)
{
$this->nmi = $nmi;
}
//
// Implement methods from Handler.
//
/**
* @copydoc Handler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
return parent::authorize($request, $args, $roleAssignments);
}
//
// Public handler methods.
//
/**
* View NavigationMenuItem content preview page.
*
* @param array $args
* @param PKPRequest $request
*/
public function preview($args, $request)
{
$path = array_shift($args);
$context = $request->getContext();
// Ensure that if we're previewing, the current user is a manager or admin.
$roles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES);
if (count(array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $roles)) == 0) {
fatalError('The current user is not permitted to preview.');
}
// Assign the template vars needed and display
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$navigationMenuItemDao = DAORegistry::getDAO('NavigationMenuItemDAO'); /** @var NavigationMenuItemDAO $navigationMenuItemDao */
$navigationMenuItem = $navigationMenuItemDao->newDataObject();
$navigationMenuItem->setContent((array) $request->getUserVar('content'), null);
$navigationMenuItem->setTitle((array) $request->getUserVar('title'), null);
Services::get('navigationMenu')->transformNavMenuItemTitle($templateMgr, $navigationMenuItem);
$templateMgr->assign('title', $navigationMenuItem->getLocalizedTitle());
$vars = [];
if ($context) {
$vars = [
'{$contactName}' => $context->getData('contactName'),
'{$contactEmail}' => $context->getData('contactEmail'),
'{$supportName}' => $context->getData('supportName'),
'{$supportPhone}' => $context->getData('supportPhone'),
'{$supportEmail}' => $context->getData('supportEmail'),
];
}
$templateMgr->assign('content', strtr($navigationMenuItem->getLocalizedContent(), $vars));
$templateMgr->display('frontend/pages/navigationMenuItemViewContent.tpl');
}
/**
* View NavigationMenuItem content page.
*
* @param array $args
* @param PKPRequest $request
*/
public function view($args, $request)
{
$path = array_shift($args);
$context = $request->getContext();
$contextId = \PKP\core\PKPApplication::CONTEXT_ID_NONE;
if ($context) {
$contextId = $context->getId();
}
// Assign the template vars needed and display
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$navigationMenuItemDao = DAORegistry::getDAO('NavigationMenuItemDAO'); /** @var NavigationMenuItemDAO $navigationMenuItemDao */
$navigationMenuItem = $navigationMenuItemDao->getByPath($contextId, $path);
if (isset($this->nmi)) {
$templateMgr->assign('title', $this->nmi->getLocalizedTitle());
$vars = [];
if ($context) {
$vars = [
'{$contactName}' => $context->getData('contactName'),
'{$contactEmail}' => $context->getData('contactEmail'),
'{$supportName}' => $context->getData('supportName'),
'{$supportPhone}' => $context->getData('supportPhone'),
'{$supportEmail}' => $context->getData('supportEmail'),
];
}
$templateMgr->assign('content', strtr($this->nmi->getLocalizedContent(), $vars));
$templateMgr->display('frontend/pages/navigationMenuItemViewContent.tpl');
} else {
return false;
}
}
/**
* Handle index request (redirect to "view")
*
* @param array $args Arguments array.
* @param PKPRequest $request Request object.
*/
public function index($args, $request)
{
$request->redirect(null, null, 'view', $request->getRequestedOp());
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* @defgroup pages_navigationMenu NavigationMenu Pages
*/
/**
* @file lib/pkp/pages/navigationMenu/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_navigationMenu
*
* @brief Handle requests for NavigationMenus functions.
*
*/
switch ($op) {
case 'index':
case 'view':
case 'preview':
define('HANDLER_CLASS', 'PKP\pages\navigationMenu\NavigationMenuItemHandler');
break;
}
@@ -0,0 +1,219 @@
<?php
/**
* @file pages/notification/NotificationHandler.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 NotificationHandler
*
* @ingroup pages_help
*
* @brief Handle requests for viewing notifications.
*/
namespace PKP\pages\notification;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\notification\Notification;
use APP\notification\NotificationManager;
use APP\template\TemplateManager;
use PKP\core\JSONMessage;
use PKP\db\DAORegistry;
use PKP\db\DAOResultFactory;
use PKP\notification\form\PKPNotificationsUnsubscribeForm;
use PKP\notification\NotificationDAO;
class NotificationHandler extends Handler
{
/**
* Return formatted notification data using Json.
*
* @param array $args
* @param Request $request
*
* @return JSONMessage JSON object
*/
public function fetchNotification($args, $request)
{
$this->setupTemplate($request);
$user = $request->getUser();
$userId = $user ? $user->getId() : null;
$context = $request->getContext();
$notificationDao = DAORegistry::getDAO('NotificationDAO'); /** @var NotificationDAO $notificationDao */
$notifications = [];
// Get the notification options from request.
$notificationOptions = $request->getUserVar('requestOptions');
if (!$user) {
$notifications = [];
} elseif (is_array($notificationOptions)) {
// Retrieve the notifications.
$notifications = $this->_getNotificationsByOptions($notificationOptions, $context->getId(), $userId);
} else {
// No options, get only TRIVIAL notifications.
$notifications = $notificationDao->getByUserId($userId, Notification::NOTIFICATION_LEVEL_TRIVIAL);
$notifications = $notifications->toArray();
}
$json = new JSONMessage();
if (is_array($notifications) && !empty($notifications)) {
$formattedNotificationsData = [];
$notificationManager = new NotificationManager();
// Format in place notifications.
$formattedNotificationsData['inPlace'] = $notificationManager->formatToInPlaceNotification($request, $notifications);
// Format general notifications.
$formattedNotificationsData['general'] = $notificationManager->formatToGeneralNotification($request, $notifications);
// Delete trivial notifications from database.
$notificationManager->deleteTrivialNotifications($notifications);
$json->setContent($formattedNotificationsData);
}
return $json;
}
/**
* Notification Unsubscribe handler
*
* @param array $args
* @param Request $request
*/
public function unsubscribe($args, $request)
{
$validationToken = $request->getUserVar('validate');
$notificationId = $request->getUserVar('id');
$notification = $this->_validateUnsubscribeRequest($validationToken, $notificationId);
$notificationsUnsubscribeForm = new PKPNotificationsUnsubscribeForm($notification, $validationToken);
// Show the form on a get request
if (!$request->isPost()) {
$notificationsUnsubscribeForm->display($request);
return;
}
// Otherwise process the result
$this->setupTemplate($request);
$notificationsUnsubscribeForm->readInputData();
$templateMgr = TemplateManager::getManager($request);
$unsubscribeResult = false;
if ($notificationsUnsubscribeForm->validate()) {
$notificationsUnsubscribeForm->execute();
$unsubscribeResult = true;
}
$userId = $notification->getUserId();
$user = Repo::user()->get($userId, true);
$contextDao = Application::getContextDAO();
$context = $contextDao->getById($notification->getContextId());
$templateMgr->assign([
'contextName' => $context?->getLocalizedName(),
'userEmail' => $user?->getEmail(),
'unsubscribeResult' => $unsubscribeResult,
]);
$templateMgr->display('notification/unsubscribeNotificationsResult.tpl');
}
/**
* Performs all unsubscribe validation token validations
*
* @param string $validationToken
* @param int $notificationId
*
* @return Notification
*/
public function _validateUnsubscribeRequest($validationToken, $notificationId)
{
if ($validationToken == null || $notificationId == null) {
$this->getDispatcher()->handle404();
}
/** @var NotificationDAO $notificationDao */
$notificationDao = DAORegistry::getDAO('NotificationDAO');
/** @var Notification $notification */
$notification = $notificationDao->getById($notificationId);
if (!isset($notification) || $notification->getId() == null) {
$this->getDispatcher()->handle404();
}
$notificationManager = new NotificationManager();
if (!$notificationManager->validateUnsubscribeToken($validationToken, $notification)) {
$this->getDispatcher()->handle404();
}
return $notification;
}
/**
* Get the notifications using options.
*
* @param array $notificationOptions
* @param int $contextId
* @param int $userId
*
* @return array
*/
public function _getNotificationsByOptions($notificationOptions, $contextId, $userId = null)
{
$notificationDao = DAORegistry::getDAO('NotificationDAO'); /** @var NotificationDAO $notificationDao */
$notificationsArray = [];
$notificationMgr = new NotificationManager();
foreach ($notificationOptions as $level => $levelOptions) {
if ($levelOptions) {
foreach ($levelOptions as $type => $typeOptions) {
if ($typeOptions) {
$notificationMgr->isVisibleToAllUsers($type, $typeOptions['assocType'], $typeOptions['assocId']) ? $workingUserId = null : $workingUserId = $userId;
$notificationsResultFactory = $notificationDao->getByAssoc($typeOptions['assocType'], $typeOptions['assocId'], $workingUserId, $type, $contextId);
$notificationsArray = $this->_addNotificationsToArray($notificationsResultFactory, $notificationsArray);
} else {
if ($userId) {
$notificationsResultFactory = $notificationDao->getByUserId($userId, $level, $type, $contextId);
$notificationsArray = $this->_addNotificationsToArray($notificationsResultFactory, $notificationsArray);
}
}
}
} else {
if ($userId) {
$notificationsResultFactory = $notificationDao->getByUserId($userId, $level, null, $contextId);
$notificationsArray = $this->_addNotificationsToArray($notificationsResultFactory, $notificationsArray);
}
}
$notificationsResultFactory = null;
}
return $notificationsArray;
}
/**
* Add notifications from a result factory to an array of
* existing notifications.
*
* @param DAOResultFactory<Notification> $resultFactory
* @param array $notificationArray
*/
public function _addNotificationsToArray($resultFactory, $notificationArray)
{
return array_merge($notificationArray, $resultFactory->toArray());
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
/**
* @defgroup pages_notification Notification Pages
*/
/**
* @file pages/notification/index.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.
*
* @ingroup pages_notification
*
* @brief Handle requests for viewing notifications.
*
*/
switch ($op) {
case 'fetchNotification':
case 'unsubscribe':
define('HANDLER_CLASS', 'PKP\pages\notification\NotificationHandler');
break;
}
@@ -0,0 +1,228 @@
<?php
/**
* @file pages/reviewer/PKPReviewerHandler.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 PKPReviewerHandler
*
* @ingroup pages_reviewer
*
* @brief Handle requests for reviewer functions.
*/
namespace PKP\pages\reviewer;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\notification\NotificationManager;
use APP\submission\Submission;
use APP\template\TemplateManager;
use Exception;
use Illuminate\Support\Facades\Mail;
use PKP\core\JSONMessage;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\facades\Locale;
use PKP\notification\PKPNotification;
use PKP\submission\reviewAssignment\ReviewAssignment;
use PKP\submission\reviewer\form\PKPReviewerReviewStep3Form;
use PKP\submission\reviewer\form\ReviewerReviewForm;
use PKP\submission\reviewer\ReviewerAction;
class PKPReviewerHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Display the submission review page.
*/
public function submission(array $args, PKPRequest $request): void
{
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
$reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId());
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$reviewStep = max($reviewAssignment->getStep(), 1);
$userStep = (int) $request->getUserVar('step');
$step = (int) (!empty($userStep) ? $userStep : $reviewStep);
if ($step > $reviewStep) {
$step = $reviewStep;
} // Reviewer can't go past incomplete steps
if ($step < 1 || $step > 4) {
throw new Exception('Invalid step!');
}
$templateMgr->assign([
'pageTitle' => __('semicolon', ['label' => __('submission.review')]) . $reviewSubmission->getLocalizedTitle(),
'reviewStep' => $reviewStep,
'selected' => $step - 1,
'submission' => $reviewSubmission,
]);
$templateMgr->display('reviewer/review/reviewStepHeader.tpl');
}
/**
* Display a step tab contents in the submission review page.
*/
public function step(array $args, PKPRequest $request): JSONMessage
{
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
$reviewId = $reviewAssignment->getId();
assert(!empty($reviewId));
$reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId());
$this->setupTemplate($request);
$reviewStep = max($reviewAssignment->getStep(), 1); // Get the current saved step from the DB
$userStep = (int) $request->getUserVar('step');
$step = (int) (!empty($userStep) ? $userStep : $reviewStep);
if ($step > $reviewStep) {
$step = $reviewStep;
} // Reviewer can't go past incomplete steps
if ($step < 1 || $step > 4) {
fatalError('Invalid step!');
}
if ($step < 4) {
$reviewerForm = $this->getReviewForm($step, $request, $reviewSubmission, $reviewAssignment);
$reviewerForm->initData();
return new JSONMessage(true, $reviewerForm->fetch($request));
} else {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'submission' => $reviewSubmission,
'step' => 4,
'reviewAssignment' => $reviewAssignment,
]);
return $templateMgr->fetchJson('reviewer/review/reviewCompleted.tpl');
}
}
/**
* Save a review step.
*/
public function saveStep(array $args, PKPRequest $request): JSONMessage
{
$step = (int)$request->getUserVar('step');
if ($step < 1 || $step > 3) {
fatalError('Invalid step!');
}
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
if ($reviewAssignment->getDateCompleted()) {
fatalError('Review already completed!');
}
$reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId());
$reviewerForm = $this->getReviewForm($step, $request, $reviewSubmission, $reviewAssignment);
$reviewerForm->readInputData();
// Save the available form data, but do not submit
if ($request->getUserVar('isSave')) {
/** @var PKPReviewerReviewStep3Form $reviewerForm */
$reviewerForm->saveForLater();
$notificationMgr = new NotificationManager();
$user = $request->getUser();
$notificationMgr->createTrivialNotification($user->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS, ['contents' => __('common.changesSaved')]);
return \PKP\db\DAO::getDataChangedEvent();
}
// Submit the form data and move forward
else {
if ($reviewerForm->validate()) {
$reviewerForm->execute();
$json = new JSONMessage(true);
$json->setEvent('setStep', $step + 1);
return $json;
} else {
$this->setupTemplate($request);
return new JSONMessage(true, $reviewerForm->fetch($request));
}
}
}
/**
* Show a form for the reviewer to enter regrets into.
*/
public function showDeclineReview(array $args, PKPRequest $request): JSONMessage
{
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
$reviewSubmission = Repo::submission()->get($reviewAssignment->getSubmissionId());
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('submissionId', $reviewSubmission->getId());
// Provide the email body to the template
$reviewerAction = new ReviewerAction();
$mailable = $reviewerAction->getResponseEmail($reviewSubmission, $reviewAssignment, true, null);
$messageBody = Mail::compileParams($mailable->view, $mailable->getData(Locale::getLocale()));
$templateMgr->assign('declineMessageBody', $messageBody);
return $templateMgr->fetchJson('reviewer/review/modal/regretMessage.tpl');
}
/**
* Save the reviewer regrets form and decline the review.
*/
public function saveDeclineReview(array $args, PKPRequest $request): JSONMessage
{
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
if ($reviewAssignment->getDateCompleted()) {
fatalError('Review already completed!');
}
$declineReviewMessage = $request->getUserVar('declineReviewMessage');
// Decline the review
$reviewerAction = new ReviewerAction();
$submission = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_SUBMISSION);
$reviewerAction->confirmReview($request, $reviewAssignment, $submission, 1, $declineReviewMessage);
$dispatcher = $request->getDispatcher();
return $request->redirectUrlJson($dispatcher->url($request, PKPApplication::ROUTE_PAGE, null, 'index'));
}
/**
* Get a review form for the current step.
*/
public function getReviewForm(
int $step, // current step
PKPRequest $request,
Submission $reviewSubmission,
ReviewAssignment $reviewAssignment
): ReviewerReviewForm {
switch ($step) {
case 1:
return new \PKP\submission\reviewer\form\PKPReviewerReviewStep1Form($request, $reviewSubmission, $reviewAssignment);
case 2:
return new \PKP\submission\reviewer\form\PKPReviewerReviewStep2Form($request, $reviewSubmission, $reviewAssignment);
case 3:
return new \PKP\submission\reviewer\form\PKPReviewerReviewStep3Form($request, $reviewSubmission, $reviewAssignment);
default:
return null;
}
}
//
// Private helper methods
//
public function _retrieveStep(): int
{
$reviewAssignment = $this->getAuthorizedContextObject(PKPApplication::ASSOC_TYPE_REVIEW_ASSIGNMENT); /** @var ReviewAssignment $reviewAssignment */
$reviewId = (int) $reviewAssignment->getId();
assert(!empty($reviewId));
return $reviewId;
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
/**
* @file pages/sitemap/PKPSitemapHandler.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 PKPSitemapHandler
*
* @ingroup pages_sitemap
*
* @brief Produce a sitemap in XML format for submitting to search engines.
*/
namespace PKP\pages\sitemap;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\handler\Handler;
use DOMDocument;
use DOMNode;
use PKP\db\DAORegistry;
use PKP\navigationMenu\NavigationMenuItem;
use PKP\navigationMenu\NavigationMenuItemDAO;
define('SITEMAP_XSD_URL', 'https://www.sitemaps.org/schemas/sitemap/0.9');
class PKPSitemapHandler extends Handler
{
/**
* Generate an XML sitemap for webcrawlers
* Creates a sitemap index if in site context, else creates a sitemap
*
* @param array $args
* @param Request $request
*/
public function index($args, $request)
{
$context = $request->getContext();
if (!$context) {
$doc = $this->_createSitemapIndex($request);
header('Content-Type: application/xml');
header('Cache-Control: private');
header('Content-Disposition: inline; filename="sitemap_index.xml"');
echo $doc->saveXml();
} else {
$doc = $this->_createContextSitemap($request);
header('Content-Type: application/xml');
header('Cache-Control: private');
header('Content-Disposition: inline; filename="sitemap.xml"');
echo $doc->saveXml();
}
}
/**
* Construct a sitemap index listing each context's individual sitemap
*
* @param Request $request
*
* @return DOMDocument
*/
public function _createSitemapIndex($request)
{
$contextDao = Application::getContextDAO();
$doc = new DOMDocument('1.0', 'utf-8');
$root = $doc->createElement('sitemapindex');
$root->setAttribute('xmlns', SITEMAP_XSD_URL);
$contexts = $contextDao->getAll(true);
while ($context = $contexts->next()) {
$sitemapUrl = $request->url($context->getPath(), 'sitemap');
$sitemap = $doc->createElement('sitemap');
$sitemap->appendChild($doc->createElement('loc', htmlspecialchars($sitemapUrl, ENT_COMPAT, 'UTF-8')));
$root->appendChild($sitemap);
}
$doc->appendChild($root);
return $doc;
}
/**
* Construct the sitemap
*
* @param Request $request
*
* @return DOMDocument
*/
public function _createContextSitemap($request)
{
$context = $request->getContext();
$doc = new DOMDocument('1.0', 'utf-8');
$root = $doc->createElement('urlset');
$root->setAttribute('xmlns', SITEMAP_XSD_URL);
// Context home
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath())));
// User register
if ($context->getData('disableUserReg') != 1) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'user', 'register')));
}
// User login
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'login')));
// Announcements
if ($context->getData('enableAnnouncements') == 1) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'announcement')));
$announcementIds = Repo::announcement()
->getCollector()
->filterByContextIds([$context->getId()])
->getIds();
foreach ($announcementIds as $announcementId) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'announcement', 'view', $announcementId)));
}
}
// About: context
if (!empty($context->getData('about'))) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'about')));
}
// About: submissions
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'about', 'submissions')));
// About: editorial team
if (!empty($context->getData('editorialTeam'))) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'about', 'editorialTeam')));
}
// About: contact
if (!empty($context->getData('mailingAddress')) || !empty($context->getData('contactName'))) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), 'about', 'contact')));
}
// Custom pages (navigation menu items)
$navigationMenuItemDao = DAORegistry::getDAO('NavigationMenuItemDAO'); /** @var NavigationMenuItemDAO $navigationMenuItemDao */
$menuItemsResult = $navigationMenuItemDao->getByType(NavigationMenuItem::NMI_TYPE_CUSTOM, $context->getId());
while ($menuItem = $menuItemsResult->next()) {
$root->appendChild($this->_createUrlTree($doc, $request->url($context->getPath(), $menuItem->getPath())));
}
$doc->appendChild($root);
return $doc;
}
/**
* Create a url entry with children
*
* @param DOMDocument $doc Reference to the XML document object
* @param string $loc URL of page (required)
* @param string $lastmod Last modification date of page (optional)
* @param string $changefreq Frequency of page modifications (optional)
* @param string $priority Subjective priority assessment of page (optional)
*
* @return DOMNode
*/
protected function _createUrlTree($doc, $loc, $lastmod = null, $changefreq = null, $priority = null)
{
$url = $doc->createElement('url');
$url->appendChild($doc->createElement('loc', htmlspecialchars($loc, ENT_COMPAT, 'UTF-8')));
if ($lastmod) {
$url->appendChild($doc->createElement('lastmod', $lastmod));
}
if ($changefreq) {
$url->appendChild($doc->createElement('changefreq', $changefreq));
}
if ($priority) {
$url->appendChild($doc->createElement('priority', $priority));
}
return $url;
}
}
+610
View File
@@ -0,0 +1,610 @@
<?php
/**
* @file pages/stats/PKPStatsHandler.php
*
* Copyright (c) 2013-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 PKPStatsHandler
*
* @ingroup pages_stats
*
* @brief Handle requests for statistics pages.
*/
namespace PKP\pages\stats;
use APP\core\Application;
use APP\core\Request;
use APP\core\Services;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\plugins\PluginRegistry;
use PKP\security\authorization\ContextAccessPolicy;
use PKP\security\Role;
use PKP\statistics\PKPStatisticsHelper;
use PKP\sushi\CounterR5Report;
class PKPStatsHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR],
['editorial', 'publications', 'context', 'users', 'reports', 'counterR5']
);
}
/**
* @see PKPHandler::authorize()
*
* @param PKPRequest $request
* @param array $args
* @param array $roleAssignments
*/
public function authorize($request, &$args, $roleAssignments)
{
$this->addPolicy(new ContextAccessPolicy($request, $roleAssignments));
return parent::authorize($request, $args, $roleAssignments);
}
//
// Public handler methods.
//
/**
* Display editorial stats about the submission workflow process
*
* @param array $args
* @param Request $request
*/
public function editorial($args, $request)
{
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
if (!$context) {
$dispatcher->handle404();
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$dateStart = date('Y-m-d', strtotime('-91 days'));
$dateEnd = date('Y-m-d', strtotime('yesterday'));
$args = [
'contextIds' => [$context->getId()],
];
$totals = Services::get('editorialStats')->getOverview($args);
$averages = Services::get('editorialStats')->getAverages($args);
$dateRangeTotals = Services::get('editorialStats')->getOverview(
array_merge(
$args,
[
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
]
)
);
// Stats that should be converted to percentages
$percentageStats = [
'acceptanceRate',
'declineRate',
'declinedDeskRate',
'declinedReviewRate',
];
// Stats that should be indented in the table
$indentStats = [
'submissionsDeclinedDeskReject',
'submissionsDeclinedPostReview',
'daysToAccept',
'daysToReject',
'declinedDeskRate',
'declinedReviewRate',
'submissionsInProgress',
'submissionsImported',
];
// Compile table rows
$tableRows = [];
foreach ($totals as $i => $stat) {
$row = [
'key' => $stat['key'],
'name' => __($stat['name']),
'total' => $stat['value'],
'dateRange' => $dateRangeTotals[$i]['value'],
];
if (in_array($stat['key'], $indentStats)) {
$row['name'] = '' . $row['name'];
}
if (in_array($stat['key'], $percentageStats)) {
$row['total'] = ($stat['value'] * 100) . '%';
$row['dateRange'] = ($dateRangeTotals[$i]['value'] * 100) . '%';
}
$description = $this->_getStatDescription($stat['key']);
if ($description) {
$row['description'] = $description;
}
if (array_key_exists($stat['key'], $averages)
&& $averages[$stat['key']] !== -1
&& $row['total'] > 0) {
$row['total'] = __('stats.countWithYearlyAverage', [
'count' => $stat['value'],
'average' => $averages[$stat['key']],
]);
}
$tableRows[] = $row;
}
// Get the workflow stage counts
$activeByStage = [];
foreach (Application::getApplicationStages() as $stageId) {
$activeByStage[] = [
'name' => __(Application::getWorkflowStageName($stageId)),
'count' => Services::get('editorialStats')->countActiveByStages($stageId, $args),
'color' => Application::getWorkflowStageColor($stageId),
];
}
$statsComponent = new \PKP\components\PKPStatsEditorialPage(
$dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'stats/editorial'),
[
'activeByStage' => $activeByStage,
'averagesApiUrl' => $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'stats/editorial/averages'),
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'dateRangeOptions' => [
[
'dateStart' => date('Y-m-d', strtotime('-91 days')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last90Days'),
],
[
'dateStart' => date('Y-m-d', strtotime(date('Y') . '-01-01')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.thisYear'),
],
[
'dateStart' => date('Y-m-d', strtotime((date('Y') - 1) . '-01-01')),
'dateEnd' => date('Y-m-d', strtotime((date('Y') - 1) . '-12-31')),
'label' => __('stats.dateRange.lastYear'),
],
[
'dateStart' => date('Y-m-d', strtotime((date('Y') - 2) . '-01-01')),
'dateEnd' => date('Y-m-d', strtotime((date('Y') - 1) . '-12-31')),
'label' => __('stats.dateRange.lastTwoYears'),
],
],
'percentageStats' => $percentageStats,
'tableColumns' => [
[
'name' => 'name',
'label' => __('common.name'),
'value' => 'name',
],
[
'name' => 'dateRange',
'label' => $dateStart . ' — ' . $dateEnd,
'value' => 'dateRange',
],
[
'name' => 'total',
'label' => __('stats.total'),
'value' => 'total',
],
],
'tableRows' => $tableRows,
]
);
$templateMgr->setLocaleKeys([
'stats.descriptionForStat',
'stats.countWithYearlyAverage',
]);
$templateMgr->setState($statsComponent->getConfig());
$templateMgr->assign([
'pageComponent' => 'StatsEditorialPage',
'pageTitle' => __('stats.editorialActivity'),
]);
$templateMgr->display('stats/editorial.tpl');
}
/**
* Display published submissions statistics page
*
* @param PKPRequest $request
* @param array $args
*/
public function publications($args, $request)
{
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
if (!$context) {
$dispatcher->handle404();
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$dateStart = date('Y-m-d', strtotime('-31 days'));
$dateEnd = date('Y-m-d', strtotime('yesterday'));
$count = 30;
$timeline = Services::get('publicationStats')->getTimeline(PKPStatisticsHelper::STATISTICS_DIMENSION_DAY, [
'assocTypes' => [Application::ASSOC_TYPE_SUBMISSION],
'contextIds' => [$context->getId()],
'count' => $count,
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
]);
$statsComponent = new \PKP\components\PKPStatsPublicationPage(
$dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'stats/publications'),
[
'timeline' => $timeline,
'timelineInterval' => PKPStatisticsHelper::STATISTICS_DIMENSION_DAY,
'timelineType' => 'abstract',
'tableColumns' => [
[
'name' => 'title',
'label' => __('common.title'),
],
[
'name' => 'abstractViews',
'label' => __('submission.abstractViews'),
'value' => 'abstractViews',
],
[
'name' => 'galleyViews',
'label' => __('stats.fileViews'),
'value' => 'galleyViews',
],
[
'name' => 'pdf',
'label' => __('stats.pdf'),
'value' => 'pdfViews',
],
[
'name' => 'html',
'label' => __('stats.html'),
'value' => 'htmlViews',
],
[
'name' => 'other',
'label' => __('common.other'),
'value' => 'otherViews',
],
[
'name' => 'total',
'label' => __('stats.total'),
'value' => 'total',
'orderBy' => 'total',
'initialOrderDirection' => true,
],
],
'count' => $count,
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'dateRangeOptions' => [
[
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last30Days'),
],
[
'dateStart' => date('Y-m-d', strtotime('-91 days')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last90Days'),
],
[
'dateStart' => date('Y-m-d', strtotime('-12 months')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last12Months'),
],
[
'dateStart' => '',
'dateEnd' => '',
'label' => __('stats.dateRange.allDates'),
],
],
'orderBy' => 'total',
'orderDirection' => true,
]
);
$geoAPIEndPoint = null;
$geoStatsSetting = $context->getEnableGeoUsageStats($request->getSite());
switch ($geoStatsSetting) {
case PKPStatisticsHelper::STATISTICS_SETTING_COUNTRY:
$geoAPIEndPoint = 'countries';
break;
case PKPStatisticsHelper::STATISTICS_SETTING_REGION:
$geoAPIEndPoint = 'regions';
break;
case PKPStatisticsHelper::STATISTICS_SETTING_CITY:
$geoAPIEndPoint = 'cities';
break;
}
$templateMgr->setState($statsComponent->getConfig());
$templateMgr->assign([
'pageComponent' => 'StatsPublicationsPage',
'pageTitle' => __('stats.publicationStats'),
'pageWidth' => TemplateManager::PAGE_WIDTH_WIDE,
'geoReportType' => $geoAPIEndPoint
]);
$templateMgr->display('stats/publications.tpl');
}
/**
* Display published submissions statistics page
*
* @param PKPRequest $request
* @param array $args
*/
public function context($args, $request)
{
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
if (!$context) {
$dispatcher->handle404();
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$dateStart = date('Y-m-d', strtotime('-31 days'));
$dateEnd = date('Y-m-d', strtotime('yesterday'));
$timeline = Services::get('contextStats')->getTimeline(PKPStatisticsHelper::STATISTICS_DIMENSION_DAY, [
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'contextIds' => [$context->getId()]
]);
$statsComponent = new \PKP\components\PKPStatsContextPage(
$dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'stats/contexts/' . $context->getId()),
[
'timeline' => $timeline,
'timelineInterval' => PKPStatisticsHelper::STATISTICS_DIMENSION_DAY,
'tableColumns' => [
[
'name' => 'title',
'label' => __('common.title'),
],
[
'name' => 'total',
'label' => __('stats.total'),
'value' => 'total',
],
],
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'dateRangeOptions' => [
[
'dateStart' => $dateStart,
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last30Days'),
],
[
'dateStart' => date('Y-m-d', strtotime('-91 days')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last90Days'),
],
[
'dateStart' => date('Y-m-d', strtotime('-12 months')),
'dateEnd' => $dateEnd,
'label' => __('stats.dateRange.last12Months'),
],
[
'dateStart' => '',
'dateEnd' => '',
'label' => __('stats.dateRange.allDates'),
],
]
]
);
$templateMgr->setState($statsComponent->getConfig());
$templateMgr->assign([
'pageComponent' => 'StatsContextPage',
'pageTitle' => __('stats.contextStats'),
'pageWidth' => TemplateManager::PAGE_WIDTH_WIDE,
]);
$templateMgr->display('stats/context.tpl');
}
/**
* Display list of available COUNTER R5 reports
*/
public function counterR5(array $args, Request $request): void
{
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$apiUrl = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $request->getContext()->getPath(), 'stats/sushi');
$context = $request->getContext();
$locales = $context->getSupportedFormLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
$counterReportForm = new \APP\components\forms\counter\CounterReportForm($apiUrl, $locales);
$counterReportsListPanel = new \PKP\components\listPanels\PKPCounterReportsListPanel(
'counterReportsListPanel',
__('manager.statistics.counterR5Reports'),
[
'apiUrl' => $apiUrl,
'form' => $counterReportForm,
]
);
$earliestDate = CounterR5Report::getEarliestDate();
$lastDate = CounterR5Report::getLastDate();
$templateMgr->setState([
'components' => [
$counterReportsListPanel->id => $counterReportsListPanel->getConfig(),
],
]);
$templateMgr->assign([
'pageComponent' => 'CounterReportsPage',
'usagePossible' => $lastDate > $earliestDate,
]);
$templateMgr->display('stats/counterReports.tpl');
}
/**
* Display users stats
*
*/
public function users(array $args, Request $request): void
{
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
if (!$context) {
$dispatcher->handle404();
}
// The POST handler is here merely to serve a redirection URL to the Vue component
if ($request->isPost()) {
echo $dispatcher->url($request, PKPApplication::ROUTE_API, $context->getPath(), 'users/report', null, null, $request->getUserVars());
exit;
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$context = $request->getContext();
$selfUrl = $dispatcher->url($request, PKPApplication::ROUTE_PAGE, $context->getPath(), 'stats', 'users');
$reportForm = new \PKP\components\forms\statistics\users\ReportForm($selfUrl, $context);
$templateMgr->setState([
'components' => [
'usersReportForm' => $reportForm->getConfig()
]
]);
$templateMgr->assign([
'pageTitle' => __('stats.userStatistics'),
'pageComponent' => 'StatsUsersPage',
'userStats' => array_map(
function ($item) {
$item['name'] = __($item['name']);
return $item;
},
Repo::user()->getRolesOverview(Repo::user()->getCollector()->filterByContextIds(['contextId' => $context->getId()]))
),
]);
$templateMgr->display('stats/users.tpl');
}
/**
* Route to other Reports operations
*
* @param array $args
* @param PKPRequest $request
*/
public function reports($args, $request)
{
$path = array_shift($args);
switch ($path) {
case '':
case 'reports':
$this->displayReports($args, $request);
break;
case 'report':
$this->report($args, $request);
break;
default: assert(false);
}
}
/**
* Display report possibilities (report plugins)
*
* @param array $args
* @param PKPRequest $request
*/
public function displayReports($args, $request)
{
$dispatcher = $request->getDispatcher();
$context = $request->getContext();
if (!$context) {
$dispatcher->handle404();
}
$templateMgr = TemplateManager::getManager($request);
$this->setupTemplate($request);
$reportPlugins = PluginRegistry::loadCategory('reports');
$templateMgr->assign('reportPlugins', $reportPlugins);
$templateMgr->assign([
'pageTitle' => __('manager.statistics.reports'),
]);
$templateMgr->display('stats/reports.tpl');
}
/**
* Delegates to plugins operations
* related to report generation.
*
* @param array $args
* @param Request $request
*/
public function report($args, $request)
{
$this->setupTemplate($request);
$pluginName = $request->getUserVar('pluginName');
$reportPlugins = PluginRegistry::loadCategory('reports');
if ($pluginName == '' || !isset($reportPlugins[$pluginName])) {
$request->redirect(null, null, 'stats', 'reports');
}
$plugin = $reportPlugins[$pluginName];
$plugin->display($args, $request);
}
//
// Protected methods.
//
/**
* Get a description for stats that require one
*
* @param string $key
*/
protected function _getStatDescription($key)
{
switch ($key) {
case 'daysToDecision': return __('stats.description.daysToDecision');
case 'acceptanceRate': return __('stats.description.acceptRejectRate');
case 'declineRate': return __('stats.description.acceptRejectRate');
case 'submissionsSkipped': return __('stats.description.submissionsSkipped');
}
return '';
}
}
@@ -0,0 +1,890 @@
<?php
/**
* @file pages/submission/PKPSubmissionHandler.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 PKPSubmissionHandler
*
* @ingroup pages_submission
*
* @brief Handles page requests to the submission wizard
*/
namespace PKP\pages\submission;
use APP\components\forms\submission\ReconfigureSubmission;
use APP\core\Application;
use APP\core\Request;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\publication\Publication;
use APP\section\Section;
use APP\submission\Submission;
use APP\template\TemplateManager;
use Illuminate\Support\LazyCollection;
use PKP\components\forms\FormComponent;
use PKP\components\forms\publication\PKPCitationsForm;
use PKP\components\forms\publication\TitleAbstractForm;
use PKP\components\forms\submission\CommentsForTheEditors;
use PKP\components\forms\submission\ConfirmSubmission;
use PKP\components\forms\submission\ForTheEditors;
use PKP\components\forms\submission\PKPSubmissionFileForm;
use PKP\components\listPanels\ContributorsListPanel;
use PKP\context\Context;
use PKP\db\DAORegistry;
use PKP\security\authorization\SubmissionAccessPolicy;
use PKP\security\authorization\UserRequiredPolicy;
use PKP\security\Role;
use PKP\stageAssignment\StageAssignmentDAO;
use PKP\submission\GenreDAO;
use PKP\submissionFile\SubmissionFile;
use PKP\user\User;
abstract class PKPSubmissionHandler extends Handler
{
public const SECTION_TYPE_CONFIRM = 'confirm';
public const SECTION_TYPE_CONTRIBUTORS = 'contributors';
public const SECTION_TYPE_FILES = 'files';
public const SECTION_TYPE_FORM = 'form';
public const SECTION_TYPE_TEMPLATE = 'template';
public const SECTION_TYPE_REVIEW = 'review';
public $_isBackendPage = true;
public function __construct()
{
parent::__construct();
$this->addRoleAssignment(
[
Role::ROLE_ID_AUTHOR,
Role::ROLE_ID_SUB_EDITOR,
Role::ROLE_ID_MANAGER,
Role::ROLE_ID_SITE_ADMIN,
],
[
'index',
'saved',
'wizard', // @deprecated 3.4
]
);
}
/**
* @param Request $request
*/
public function authorize($request, &$args, $roleAssignments): bool
{
$submissionId = (int) $request->getUserVar('id');
// Creating a new submission
if ($submissionId === 0) {
$this->addPolicy(new UserRequiredPolicy($request));
$this->markRoleAssignmentsChecked();
} else {
$this->addPolicy(new SubmissionAccessPolicy($request, $args, $roleAssignments, 'id'));
}
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Route the request to the correct page based
* on whether they are starting a new submission,
* working on a submission in progress, or viewing
* a submission that has been submitted.
*
* @param array $args
* @param Request $request
*/
public function index($args, $request): void
{
$this->setupTemplate($request);
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission) {
$this->start($args, $request);
return;
}
if ($submission->getData('submissionProgress')) {
$this->showWizard($args, $request, $submission);
return;
}
$this->complete($args, $request, $submission);
}
/**
* Display the screen to start a new submission
*/
protected function start(array $args, Request $request): void
{
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageComponent' => 'StartSubmissionPage',
'pageTitle' => __('submission.wizard.title'),
'pageWidth' => TemplateManager::PAGE_WIDTH_NARROW,
]);
$templateMgr->display('submission/start.tpl');
}
/**
* Backwards compatibility for old links to the submission wizard
*
* @deprecated 3.4
*/
public function wizard(array $args, Request $request): void
{
$submissionId = $request->getUserVar('submissionId')
? (int) $request->getUserVar('submissionId')
: null;
$request->redirectUrl(
Repo::submission()->getUrlSubmissionWizard($request->getContext(), $submissionId)
);
}
/**
* Display the submission wizard
*/
protected function showWizard(array $args, Request $request, Submission $submission): void
{
$context = $request->getContext();
/** @var Publication $publication */
$publication = $submission->getCurrentPublication();
/** @var int $sectionId */
$sectionId = $publication->getData(Application::getSectionIdPropName());
if ($sectionId) {
$section = Repo::section()->get($sectionId, $context->getId());
}
if (isset($section) &&
(
$section->getIsInactive() ||
($section->getEditorRestricted() && !$this->isEditor())
)
) {
$this->showErrorPage(
'submission.wizard.sectionClosed',
__('submission.wizard.sectionClosed.message', [
'contextName' => $context->getLocalizedData('name'),
'section' => $section->getLocalizedTitle(),
'email' => $context->getData('contactEmail'),
'name' => $context->getData('contactName'),
])
);
return;
}
$supportedSubmissionLocales = $context->getSupportedSubmissionLocaleNames();
$formLocales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($supportedSubmissionLocales), $supportedSubmissionLocales);
// Order locales with submission locale first
$orderedLocales = $supportedSubmissionLocales;
uksort($orderedLocales, fn ($a, $b) => $a === $submission->getData('locale') ? $a : $b);
$userGroups = Repo::userGroup()
->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
/** @var GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genres = $genreDao->getEnabledByContextId($context->getId())->toArray();
$sections = $this->getSubmitSections($context);
$categories = Repo::category()->getCollector()
->filterByContextIds([$context->getId()])
->getMany();
$submissionFilesListPanel = $this->getSubmissionFilesListPanel($request, $submission, $genres);
$contributorsListPanel = $this->getContributorsListPanel($request, $submission, $publication, $formLocales);
$reconfigureSubmissionForm = $this->getReconfigureForm($context, $submission, $publication, $sections, $categories);
$steps = $this->getSteps($request, $submission, $publication, $formLocales, $sections, $categories);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'categories' => Repo::category()->getBreadcrumbs($categories),
'components' => [
$submissionFilesListPanel['id'] => $submissionFilesListPanel,
$contributorsListPanel->id => $contributorsListPanel->getConfig(),
$reconfigureSubmissionForm->id => $reconfigureSubmissionForm->getConfig(),
],
'i18nConfirmSubmit' => $this->getConfirmSubmitMessage($submission, $context),
'i18nDiscardChanges' => __('common.discardChanges'),
'i18nDisconnected' => __('common.disconnected'),
'i18nLastAutosaved' => __('common.lastSaved'),
'i18nPageTitle' => __('submission.wizard.titleWithStep'),
'i18nSubmit' => __('form.submit'),
'i18nTitleSeparator' => __('common.titleSeparator'),
'i18nUnableToSave' => __('submission.wizard.unableToSave'),
'i18nUnsavedChanges' => __('common.unsavedChanges'),
'i18nUnsavedChangesMessage' => __('common.unsavedChangesMessage'),
'publication' => Repo::publication()->getSchemaMap($submission, $userGroups, $genres)->map($publication),
'publicationApiUrl' => $this->getPublicationApiUrl($request, $submission->getId(), $publication->getId()),
'reconfigurePublicationProps' => $this->getReconfigurePublicationProps(),
'reconfigureSubmissionProps' => $this->getReconfigureSubmissionProps(),
'submission' => Repo::submission()->getSchemaMap()->map($submission, $userGroups, $genres),
'submissionApiUrl' => Repo::submission()->getUrlApi($request->getContext(), $submission->getId()),
'submissionSavedUrl' => $this->getSubmissionSavedUrl($request, $submission->getId()),
'submissionWizardUrl' => Repo::submission()->getUrlSubmissionWizard($context, $submission->getId()),
'submitApiUrl' => $this->getSubmitApiUrl($request, $submission->getId()),
'steps' => $steps,
]);
$templateMgr->assign([
'isCategoriesEnabled' => $context->getData('submitWithCategories') && $categories->count(),
'locales' => $orderedLocales,
'pageComponent' => 'SubmissionWizardPage',
'pageTitle' => __('submission.wizard.title'),
'submission' => $submission,
'submittingTo' => $this->getSubmittingTo($context, $submission, $sections, $categories),
'reviewSteps' => $this->getReviewStepsForSmarty($steps),
]);
$templateMgr->display('submission/wizard.tpl');
}
/**
* Display the submission completed screen
*/
protected function complete(array $args, Request $request, Submission $submission): void
{
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('submission.submit.submissionComplete'),
'pageWidth' => TemplateManager::PAGE_WIDTH_NARROW,
'submission' => $submission,
'workflowUrl' => $this->getWorkflowUrl($submission, $request->getUser()),
]);
$templateMgr->display('submission/complete.tpl');
}
/**
* Display the saved for later screen
*/
public function saved(array $args, Request $request): void
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
if (!$submission) {
$request->getDispatcher()->handle404();
}
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'email' => $request->getUser()->getEmail(),
'pageTitle' => __('submission.wizard.saved'),
'pageWidth' => TemplateManager::PAGE_WIDTH_NARROW,
'submission' => $submission,
'submissionWizardUrl' => Repo::submission()->getUrlSubmissionWizard($request->getContext(), $submission->getId()),
]);
$templateMgr->display('submission/saved.tpl');
}
/**
* Get all steps of the submission wizard
*/
protected function getSteps(Request $request, Submission $submission, Publication $publication, array $locales, array $sections, LazyCollection $categories): array
{
$publicationApiUrl = $this->getPublicationApiUrl($request, $submission->getId(), $publication->getId());
$controlledVocabUrl = $this->getControlledVocabBaseUrl($request);
$steps = [];
$steps[] = $this->getDetailsStep($request, $submission, $publication, $locales, $publicationApiUrl, $sections, $controlledVocabUrl);
$steps[] = $this->getFilesStep($request, $submission, $publication, $locales, $publicationApiUrl);
$steps[] = $this->getContributorsStep($request, $submission, $publication, $locales, $publicationApiUrl);
$steps[] = $this->getEditorsStep($request, $submission, $publication, $locales, $publicationApiUrl, $categories);
$steps[] = $this->getConfirmStep($request, $submission, $publication, $locales, $publicationApiUrl);
return $steps;
}
/**
* Get the url to the API endpoint to submit this submission
*/
protected function getSubmitApiUrl(Request $request, int $submissionId): string
{
return $request
->getDispatcher()
->url(
$request,
Application::ROUTE_API,
$request->getContext()->getPath(),
'submissions/' . $submissionId . '/submit'
);
}
/**
* Get the url to the publication's API endpoint
*/
protected function getPublicationApiUrl(Request $request, int $submissionId, int $publicationId): string
{
return $request
->getDispatcher()
->url(
$request,
Application::ROUTE_API,
$request->getContext()->getPath(),
'submissions/' . $submissionId . '/publications/' . $publicationId
);
}
/**
* Get the URL to the page that shows the submission
* has been saved
*/
protected function getSubmissionSavedUrl(Request $request, int $submissionId): string
{
return $request
->getDispatcher()
->url(
$request,
Application::ROUTE_PAGE,
$request->getContext()->getPath(),
'submission',
'saved',
null,
[
'id' => $submissionId,
]
);
}
/**
* Get the url to the submission's files API endpoint
*/
protected function getSubmissionFilesApiUrl(Request $request, int $submissionId): string
{
return $request
->getDispatcher()
->url(
$request,
Application::ROUTE_API,
$request->getContext()->getPath(),
'submissions/' . $submissionId . '/files'
);
}
/**
* Get the base url to the controlled vocab suggestions API endpoint
*
* The entry `__vocab__` will be replaced with the user's search phrase.
*/
protected function getControlledVocabBaseUrl(Request $request): string
{
return $request->getDispatcher()->url(
$request,
Application::ROUTE_API,
$request->getContext()->getData('urlPath'),
'vocabs',
null,
null,
['vocab' => '__vocab__']
);
}
/**
* Get the state needed for the SubmissionFilesListPanel component
*/
protected function getSubmissionFilesListPanel(Request $request, Submission $submission, array $genres): array
{
$submissionFiles = Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_SUBMISSION])
->getMany();
// Don't allow dependent files to be uploaded with the submission
$genres = array_values(
array_filter($genres, fn ($genre) => !$genre->getDependent())
);
$form = new PKPSubmissionFileForm(
$this->getSubmissionFilesApiUrl($request, $submission->getId()),
$genres
);
return [
'addFileLabel' => __('common.addFile'),
'apiUrl' => $this->getSubmissionFilesApiUrl($request, $submission->getId()),
'cancelUploadLabel' => __('form.dropzone.dictCancelUpload'),
'genrePromptLabel' => __('submission.submit.genre.label'),
'emptyLabel' => __('submission.upload.instructions'),
'emptyAddLabel' => __('common.upload.addFile'),
'fileStage' => SubmissionFile::SUBMISSION_FILE_SUBMISSION,
'form' => $form->getConfig(),
'genres' => array_map(
fn ($genre) => [
'id' => (int) $genre->getId(),
'name' => $genre->getLocalizedName(),
'isPrimary' => !$genre->getSupplementary() && !$genre->getDependent(),
],
$genres
),
'id' => 'submissionFiles',
'items' => Repo::submissionFile()
->getSchemaMap()
->summarizeMany($submissionFiles, $genres)
->values(),
'options' => [
'maxFilesize' => Application::getIntMaxFileMBs(),
'timeout' => ini_get('max_execution_time') ? ini_get('max_execution_time') * 1000 : 0,
'dropzoneDictDefaultMessage' => __('form.dropzone.dictDefaultMessage'),
'dropzoneDictFallbackMessage' => __('form.dropzone.dictFallbackMessage'),
'dropzoneDictFallbackText' => __('form.dropzone.dictFallbackText'),
'dropzoneDictFileTooBig' => __('form.dropzone.dictFileTooBig'),
'dropzoneDictInvalidFileType' => __('form.dropzone.dictInvalidFileType'),
'dropzoneDictResponseError' => __('form.dropzone.dictResponseError'),
'dropzoneDictCancelUpload' => __('form.dropzone.dictCancelUpload'),
'dropzoneDictUploadCanceled' => __('form.dropzone.dictUploadCanceled'),
'dropzoneDictCancelUploadConfirmation' => __('form.dropzone.dictCancelUploadConfirmation'),
'dropzoneDictRemoveFile' => __('form.dropzone.dictRemoveFile'),
'dropzoneDictMaxFilesExceeded' => __('form.dropzone.dictMaxFilesExceeded'),
],
'otherLabel' => __('about.other'),
'primaryLocale' => $request->getContext()->getPrimaryLocale(),
'removeConfirmLabel' => __('submission.submit.removeConfirm'),
'stageId' => WORKFLOW_STAGE_ID_SUBMISSION,
'title' => __('submission.files'),
'uploadProgressLabel' => __('submission.upload.percentComplete'),
];
}
/**
* Get an instance of the ContributorsListPanel component
*/
protected function getContributorsListPanel(Request $request, Submission $submission, Publication $publication, array $locales): ContributorsListPanel
{
return new ContributorsListPanel(
'contributors',
__('publication.contributors'),
$submission,
$request->getContext(),
$locales,
[], // Populated by publication state
true
);
}
/**
* Get the user groups that a user can submit in
*/
protected function getSubmitUserGroups(Context $context, User $user): LazyCollection
{
$userGroups = Repo::userGroup()
->getCollector()
->filterByContextIds([$context->getId()])
->filterByUserIds([$user->getId()])
->filterByRoleIds([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_AUTHOR])
->getMany();
// Users without a submitting role can submit as an
// author role that allows self registration
if (!$userGroups->count()) {
$defaultUserGroup = Repo::userGroup()->getFirstSubmitAsAuthorUserGroup($context->getId());
return LazyCollection::make(function () use ($defaultUserGroup) {
if ($defaultUserGroup) {
yield $defaultUserGroup->getId() => $defaultUserGroup;
}
});
}
return $userGroups;
}
/**
* Get the state for the files step
*/
protected function getFilesStep(Request $request, Submission $submission, Publication $publication, array $locales, string $publicationApiUrl): array
{
return [
'id' => 'files',
'name' => __('submission.upload.uploadFiles'),
'reviewName' => __('submission.files'),
'sections' => [
[
'id' => 'files',
'name' => __('submission.upload.uploadFiles'),
'type' => self::SECTION_TYPE_FILES,
'description' => $request->getContext()->getLocalizedData('uploadFilesHelp'),
],
],
'reviewTemplate' => '/submission/review-files.tpl',
];
}
/**
* Get the state for the contributors step
*/
protected function getContributorsStep(Request $request, Submission $submission, Publication $publication, array $locales, string $publicationApiUrl): array
{
return [
'id' => 'contributors',
'name' => __('publication.contributors'),
'reviewName' => __('publication.contributors'),
'sections' => [
[
'id' => 'contributors',
'name' => __('publication.contributors'),
'type' => self::SECTION_TYPE_CONTRIBUTORS,
'description' => $request->getContext()->getLocalizedData('contributorsHelp'),
],
],
'reviewTemplate' => '/submission/review-contributors.tpl',
];
}
/**
* Get the state for the details step
*/
protected function getDetailsStep(Request $request, Submission $submission, Publication $publication, array $locales, string $publicationApiUrl, array $sections, string $controlledVocabUrl): array
{
$titleAbstractForm = $this->getDetailsForm(
$publicationApiUrl,
$locales,
$publication,
$request->getContext(),
$sections,
$controlledVocabUrl
);
$this->removeButtonFromForm($titleAbstractForm);
$sections = [
[
'id' => $titleAbstractForm->id,
'name' => __('submission.details'),
'type' => self::SECTION_TYPE_FORM,
'description' => $request->getContext()->getLocalizedData('detailsHelp'),
'form' => $this->getLocalizedForm($titleAbstractForm, $submission, $request->getContext()),
],
];
if (in_array($request->getContext()->getData('citations'), [Context::METADATA_REQUEST, Context::METADATA_REQUIRE])) {
$citationsForm = new PKPCitationsForm(
$publicationApiUrl,
$publication,
$request->getContext()->getData('citations') === Context::METADATA_REQUIRE
);
$this->removeButtonFromForm($citationsForm);
$sections[] = [
'id' => $citationsForm->id,
'name' => '',
'type' => self::SECTION_TYPE_FORM,
'description' => '',
'form' => $citationsForm->getConfig(),
];
}
return [
'id' => 'details',
'name' => __('common.details'),
'reviewName' => __('common.details'),
'sections' => $sections,
'reviewTemplate' => '/submission/review-details.tpl',
];
}
/**
* Get the state for the For the Editors step
*
* If no metadata is enabled during submission, the metadata
* form is not shown.
*/
protected function getEditorsStep(Request $request, Submission $submission, Publication $publication, array $locales, string $publicationApiUrl, LazyCollection $categories): array
{
$metadataForm = $this->getForTheEditorsForm(
$publicationApiUrl,
$locales,
$publication,
$submission,
$request->getContext(),
$request->getDispatcher()->url(
$request,
Application::ROUTE_API,
$request->getContext()->getData('urlPath'),
'vocabs',
null,
null,
['vocab' => '__vocab__']
),
$categories
);
$this->removeButtonFromForm($metadataForm);
$commentsForm = new CommentsForTheEditors(
Repo::submission()->getUrlApi($request->getContext(), $submission->getId()),
$submission
);
$this->removeButtonFromForm($commentsForm);
$hasMetadataForm = count($metadataForm->fields);
$metadataFormData = $this->getLocalizedForm($metadataForm, $submission, $request->getContext());
$commentsFormData = $this->getLocalizedForm($commentsForm, $submission, $request->getContext());
$sections = [
[
'id' => $hasMetadataForm ? $metadataForm->id : $commentsForm->id,
'name' => __('submission.forTheEditors'),
'type' => self::SECTION_TYPE_FORM,
'description' => $request->getContext()->getLocalizedData('forTheEditorsHelp'),
'form' => $hasMetadataForm ? $metadataFormData : $commentsFormData,
],
];
if ($hasMetadataForm) {
$sections[] = [
'id' => $commentsForm->id,
'name' => '',
'type' => self::SECTION_TYPE_FORM,
'description' => '',
'form' => $commentsFormData,
];
}
return [
'id' => 'editors',
'name' => __('submission.forTheEditors'),
'reviewName' => __('submission.forTheEditors'),
'sections' => $sections,
'reviewTemplate' => '/submission/review-editors.tpl',
];
}
/**
* Get the state for the Confirm step
*/
protected function getConfirmStep(Request $request, Submission $submission, Publication $publication, array $locales, string $publicationApiUrl): array
{
$sections = [
[
'id' => 'review',
'name' => __('submission.reviewAndSubmit'),
'type' => self::SECTION_TYPE_REVIEW,
'description' => $request->getContext()->getLocalizedData('reviewHelp'),
]
];
$confirmForm = new ConfirmSubmission(
FormComponent::ACTION_EMIT,
$request->getContext()
);
if (!empty($confirmForm->fields)) {
$this->removeButtonFromForm($confirmForm);
$sections[] = [
'id' => $confirmForm->id,
'name' => __('author.submit.confirmation'),
'type' => self::SECTION_TYPE_CONFIRM,
'description' => '<p>' . __('submission.wizard.confirm') . '</p>',
'form' => $confirmForm->getConfig(),
];
}
return [
'id' => 'review',
'name' => __('submission.review'),
'sections' => $sections,
];
}
/**
* A helper function to remove the save button forms in the wizard
*
* This creates a default group/page for each form and assigns each #
* field and group to that page.
*/
protected function removeButtonFromForm(FormComponent $form): void
{
$form->addPage([
'id' => 'default',
])
->addGroup([
'id' => 'default',
'pageId' => 'default'
]);
foreach ($form->fields as $field) {
$field->groupId = 'default';
}
}
/**
* Get details about the steps that are required by the smarty template
*/
protected function getReviewStepsForSmarty(array $steps): array
{
$reviewSteps = [];
foreach ($steps as $step) {
if ($step['id'] === 'review') {
continue;
}
$reviewSteps[] = [
'id' => $step['id'],
'reviewTemplate' => $step['reviewTemplate'],
'reviewName' => $step['reviewName'],
];
}
return $reviewSteps;
}
/**
* Show an error page
*/
protected function showErrorPage(string $titleLocaleKey, string $message): void
{
$this->_isBackendPage = false;
$templateMgr = TemplateManager::getManager(Application::get()->getRequest());
$templateMgr->assign([
'pageTitle' => $titleLocaleKey,
'messageTranslated' => $message,
]);
$templateMgr->display('frontend/pages/message.tpl');
}
/**
* Get the appropriate workflow URL for the current user
*
* Returns the author dashboard if the user has an author assignment
* and the editorial workflow if not.
*/
protected function getWorkflowUrl(Submission $submission, User $user): string
{
/** @var StageAssignmentDAO $stageAssignmentDao */
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO');
$results = $stageAssignmentDao->getBySubmissionAndRoleIds($submission->getId(), [Role::ROLE_ID_AUTHOR], WORKFLOW_STAGE_ID_SUBMISSION, $user->getId());
$request = Application::get()->getRequest();
if (count($results->toArray())) {
return Repo::submission()->getUrlAuthorWorkflow($request->getContext(), $submission->getId());
}
return Repo::submission()->getUrlEditorialWorkflow($request->getContext(), $submission->getId());
}
/**
* Get the sections that this user can submit to
*/
protected function getSubmitSections(Context $context): array
{
$allSections = Repo::section()
->getCollector()
->filterByContextIds([$context->getId()])
->excludeInactive()
->getMany();
$submitSections = [];
/** @var Section $section */
foreach ($allSections as $section) {
if ($section->getEditorRestricted() && !$this->isEditor()) {
continue;
}
$submitSections[] = $section;
}
return $submitSections;
}
/**
* Get the "are you sure?" message shown to the user
* before they complete their submission
*/
protected function getConfirmSubmitMessage(Submission $submission, Context $context): string
{
return __('submission.wizard.confirmSubmit', ['context' => htmlspecialchars($context->getLocalizedName())]);
}
/**
* Is the current user an editor
*/
protected function isEditor(): bool
{
return !empty(
array_intersect(
Section::getEditorRestrictedRoles(),
$this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES)
)
);
}
/**
* Get the form configuration data with the correct
* locale settings based on the submission's locale
*
* Uses the submission locale as the primary and
* visible locale, and puts that locale first in the
* list of supported locales.
*
* Call this instead of $form->getConfig() to display
* a form with the correct submission locales
*/
protected function getLocalizedForm(FormComponent $form, Submission $submission, Context $context): array
{
$config = $form->getConfig();
$config['primaryLocale'] = $submission->getLocale();
$config['visibleLocales'] = [$submission->getLocale()];
$supportedFormLocales = [];
foreach ($context->getSupportedSubmissionLocaleNames() as $localeKey => $name) {
$supportedFormLocales[] = [
'key' => $localeKey,
'label' => $name,
];
}
usort($supportedFormLocales, fn ($a, $b) => $a['key'] === $submission->getLocale() ? -1 : 1);
$config['supportedFormLocales'] = $supportedFormLocales;
return $config;
}
/**
* Get a string describing the sections, languages, etc
* that the submission is in
*/
abstract protected function getSubmittingTo(Context $context, Submission $submission, array $sections, LazyCollection $categories): string;
/**
* Get the form to reconfigure a submission that has already been started
*/
abstract protected function getReconfigureForm(Context $context, Submission $submission, Publication $publication, array $sections, LazyCollection $categories): ReconfigureSubmission;
/**
* Get the form for entering the title/abstract details
*/
abstract protected function getDetailsForm(string $publicationApiUrl, array $locales, Publication $publication, Context $context, array $sections, string $suggestionUrlBase): TitleAbstractForm;
/**
* Get the form for entering information for the editors
*/
abstract protected function getForTheEditorsForm(string $publicationApiUrl, array $locales, Publication $publication, Submission $submission, Context $context, string $suggestionUrlBase, LazyCollection $categories): ForTheEditors;
/**
* Get the properties that should be saved to the Submission
* from the ReconfigureSubmission form
*/
abstract protected function getReconfigurePublicationProps(): array;
/**
* Get the properties that should be saved to the Submission
* from the ReconfigureSubmission form
*/
abstract protected function getReconfigureSubmissionProps(): array;
}
+24
View File
@@ -0,0 +1,24 @@
<?php
/**
* @defgroup pages_submissions Submissions editorial page
*/
/**
* @file lib/pkp/pages/submissions/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_submissions
*
* @brief Handle requests for submissions functions.
*
*/
switch ($op) {
case 'index':
case 'tasks':
define('HANDLER_CLASS', 'PKP\pages\dashboard\DashboardHandler');
break;
}
+109
View File
@@ -0,0 +1,109 @@
<?php
/**
* @file pages/user/PKPUserHandler.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 PKPUserHandler
*
* @ingroup pages_user
*
* @brief Handle requests for user functions.
*/
namespace PKP\pages\user;
use APP\core\Request;
use APP\handler\Handler;
use APP\template\TemplateManager;
use PKP\core\JSONMessage;
use PKP\core\PKPRequest;
use PKP\facades\Locale;
use PKP\security\Validation;
use PKP\user\InterestManager;
class PKPUserHandler extends Handler
{
/**
* Index page; redirect to profile
*/
public function index($args, $request)
{
$request->redirect(null, null, 'profile');
}
/**
* Change the locale for the current user.
*
* @param array $args first parameter is the new locale
*/
public function setLocale($args, $request)
{
$setLocale = array_shift($args);
$site = $request->getSite();
$context = $request->getContext();
if ($context != null) {
$contextSupportedLocales = (array) $context->getSupportedLocales();
}
if (Locale::isLocaleValid($setLocale) && (!isset($contextSupportedLocales) || in_array($setLocale, $contextSupportedLocales)) && in_array($setLocale, $site->getSupportedLocales())) {
$session = $request->getSession();
$session->setSessionVar('currentLocale', $setLocale);
}
$source = $request->getUserVar('source');
if (preg_match('#^/\w#', $source) === 1) {
$request->redirectUrl($source);
}
if (isset($_SERVER['HTTP_REFERER'])) {
$request->redirectUrl($_SERVER['HTTP_REFERER']);
}
$request->redirect(null, 'index');
}
/**
* Get interests for reviewer interests autocomplete.
*
* @param array $args
* @param PKPRequest $request
*
* @return JSONMessage JSON object
*/
public function getInterests($args, $request)
{
return new JSONMessage(
true,
(new InterestManager())->getAllInterests($request->getUserVar('term'))
);
}
/**
* Display an authorization denied message.
*
* @param array $args
* @param Request $request
*/
public function authorizationDenied($args, $request)
{
if (!Validation::isLoggedIn()) {
Validation::redirectLogin();
}
// Get message with sanity check (for XSS or phishing)
$authorizationMessage = $request->getUserVar('message');
if (!preg_match('/^[a-zA-Z0-9.]+$/', $authorizationMessage)) {
fatalError('Invalid locale key for auth message.');
}
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('message', $authorizationMessage);
return $templateMgr->display('frontend/pages/message.tpl');
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
/**
* @file pages/user/ProfileHandler.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 ProfileHandler
*
* @ingroup pages_user
*
* @brief Handle requests for modifying user profiles.
*/
namespace PKP\pages\user;
use APP\core\Application;
use APP\pages\user\UserHandler;
use APP\template\TemplateManager;
use PKP\core\PKPRequest;
use PKP\security\authorization\PKPSiteAccessPolicy;
use PKP\security\authorization\UserRequiredPolicy;
class ProfileHandler extends UserHandler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
//
// Implement template methods from PKPHandler
//
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
$operations = [
'profile',
];
// Site access policy
$this->addPolicy(new PKPSiteAccessPolicy($request, $operations, PKPSiteAccessPolicy::SITE_ACCESS_ALL_ROLES));
// User must be logged in
$this->addPolicy(new UserRequiredPolicy($request));
return parent::authorize($request, $args, $roleAssignments);
}
/**
* Display user profile tabset.
*
* @param array $args
* @param PKPRequest $request
*/
public function profile($args, $request)
{
$context = $request->getContext();
if (!$context) {
$user = $request->getUser();
$contextDao = Application::getContextDAO();
$workingContexts = $contextDao->getAvailable($user ? $user->getId() : null);
[$firstContext, $secondContext] = [$workingContexts->next(), $workingContexts->next()];
if ($firstContext && !$secondContext) {
$request->redirect($firstContext->getPath(), 'user', 'profile', null, $args);
}
}
if ($anchor = array_shift($args)) {
// Some requests will try to specify a tab name in the args. Redirect
// to use this as an anchor name instead.
$request->redirect(null, null, null, null, null, $anchor);
}
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => __('user.profile'),
]);
$templateMgr->display('user/profile.tpl');
}
}
+224
View File
@@ -0,0 +1,224 @@
<?php
/**
* @file pages/user/RegistrationHandler.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 RegistrationHandler
*
* @ingroup pages_user
*
* @brief Handle requests for user registration.
*/
namespace PKP\pages\user;
use APP\core\Application;
use APP\facades\Repo;
use APP\pages\user\UserHandler;
use APP\template\TemplateManager;
use PKP\config\Config;
use PKP\core\Core;
use PKP\core\PKPRequest;
use PKP\notification\PKPNotification;
use PKP\notification\PKPNotificationManager;
use PKP\observers\events\UserRegisteredContext;
use PKP\observers\events\UserRegisteredSite;
use PKP\security\AccessKeyManager;
use PKP\security\Validation;
use PKP\user\form\RegistrationForm;
use Symfony\Component\Mailer\Exception\TransportException;
class RegistrationHandler extends UserHandler
{
/**
* Display registration form for new users, validate and execute that form,
* or display a registration success page if the user is logged in.
*
* @param array $args
* @param PKPRequest $request
*/
public function register($args, $request)
{
if (Config::getVar('security', 'force_login_ssl') && $request->getProtocol() != 'https') {
// Force SSL connections for registration
$request->redirectSSL();
}
// If the user is logged in, show them the registration success page
if (Validation::isLoggedIn()) {
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('pageTitle', 'user.login.registrationComplete');
return $templateMgr->display('frontend/pages/userRegisterComplete.tpl');
}
$this->validate(null, $request);
$this->setupTemplate($request);
$regForm = new RegistrationForm($request->getSite());
// Initial GET request to register page
if (!$request->isPost()) {
$regForm->initData();
return $regForm->display($request);
}
// Form submitted
$regForm->readInputData();
if (!$regForm->validate()) {
return $regForm->display($request);
}
$userId = $regForm->execute();
$user = Repo::user()->get($userId, true);
try {
if ($context = $request->getContext()) {
event(new UserRegisteredContext($user, $context));
} else {
event(new UserRegisteredSite($user, $request->getSite()));
}
} catch (TransportException $e) {
$notificationMgr = new PKPNotificationManager();
$notificationMgr->createTrivialNotification(
$userId,
PKPNotification::NOTIFICATION_TYPE_ERROR,
['contents' => __('email.compose.error')]
);
trigger_error($e->getMessage(), E_USER_WARNING);
}
// Inform the user of the email validation process. This must be run
// before the disabled account check to ensure new users don't see the
// disabled account message.
if (Config::getVar('email', 'require_validation')) {
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'requireValidation' => true,
'pageTitle' => 'user.login.registrationPendingValidation',
'messageTranslated' => __('user.login.accountNotValidated', ['email' => $regForm->getData('email')]),
]);
return $templateMgr->display('frontend/pages/message.tpl');
}
$reason = null;
Validation::login($regForm->getData('username'), $regForm->getData('password'), $reason);
if ($reason !== null) {
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => 'user.login',
'errorMsg' => $reason == '' ? 'user.login.accountDisabled' : 'user.login.accountDisabledWithReason',
'errorParams' => ['reason' => $reason],
'backLink' => $request->url(null, 'login'),
'backLinkLabel' => 'user.login',
]);
return $templateMgr->display('frontend/pages/error.tpl');
}
$source = $request->getUserVar('source');
if (preg_match('#^/\w#', $source) === 1) {
return $request->redirectUrl($source);
} else {
// Make a new request to update cookie details after login
$request->redirect(null, 'user', 'register');
}
}
/**
* Re-route request to the register method.
* Backwards-compatible with third-party themes that submit the registration
* form to the registerUser method.
*
* @see RegistrationHandler::register
*/
public function registerUser($args, $request)
{
$this->register($args, $request);
}
/**
* Check credentials and activate a new user
*
* @param array $args
* @param PKPRequest $request
*/
public function activateUser($args, $request)
{
$username = array_shift($args);
$accessKeyCode = array_shift($args);
$user = Repo::user()->getByUsername($username, true);
if (!$user) {
$request->redirect(null, 'login');
}
// Checks user and token
$accessKeyManager = new AccessKeyManager();
$accessKeyHash = $accessKeyManager->generateKeyHash($accessKeyCode);
$accessKey = $accessKeyManager->validateKey(
'RegisterContext',
$user->getId(),
$accessKeyHash
);
if ($accessKey != null && $user->getDateValidated() === null) {
// Activate user
$user->setDisabled(false);
$user->setDisabledReason('');
$user->setDateValidated(Core::getCurrentDate());
Repo::user()->edit($user);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign('message', 'user.login.activated');
return $templateMgr->display('frontend/pages/message.tpl');
}
$request->redirect(null, 'login');
}
/**
* @copydoc PKPHandler::validate
*
* @param null|mixed $requiredContexts
* @param null|mixed $request
*/
public function validate($requiredContexts = null, $request = null)
{
$context = $request->getContext();
$disableUserReg = false;
if (!$context) {
$contextDao = Application::getContextDAO();
$contexts = $contextDao->getAll(true)->toArray();
$contextsForRegistration = [];
foreach ($contexts as $context) {
if (!$context->getData('disableUserReg')) {
$contextsForRegistration[] = $context;
}
}
if (empty($contextsForRegistration)) {
$disableUserReg = true;
}
} elseif ($context->getData('disableUserReg')) {
$disableUserReg = true;
}
if ($disableUserReg) {
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageTitle' => 'user.register',
'errorMsg' => 'user.register.registrationDisabled',
'backLink' => $request->url(null, 'login'),
'backLinkLabel' => 'user.login',
]);
$templateMgr->display('frontend/pages/error.tpl');
exit;
}
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/**
* @defgroup pages_user User Pages
*/
/**
* @file lib/pkp/pages/user/index.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2003-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @ingroup pages_user
*
* @brief Handle requests for user functions.
*
*/
switch ($op) {
//
// Profiles
//
case 'profile':
define('HANDLER_CLASS', 'PKP\pages\user\ProfileHandler');
break;
//
// Registration
//
case 'register':
case 'registerUser':
case 'activateUser':
define('HANDLER_CLASS', 'PKP\pages\user\RegistrationHandler');
break;
}
@@ -0,0 +1,912 @@
<?php
/**
* @file pages/workflow/PKPWorkflowHandler.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 PKPWorkflowHandler
*
* @ingroup pages_reviewer
*
* @brief Handle requests for the submssion workflow.
*/
namespace PKP\pages\workflow;
use APP\core\Application;
use APP\core\PageRouter;
use APP\core\Request;
use APP\core\Services;
use APP\facades\Repo;
use APP\handler\Handler;
use APP\publication\Publication;
use APP\submission\Submission;
use APP\template\TemplateManager;
use Exception;
use Illuminate\Support\Enumerable;
use PKP\components\forms\publication\PKPCitationsForm;
use PKP\components\forms\publication\PKPMetadataForm;
use PKP\components\forms\publication\PKPPublicationLicenseForm;
use PKP\components\forms\publication\TitleAbstractForm;
use PKP\components\listPanels\ContributorsListPanel;
use PKP\context\Context;
use PKP\core\JSONMessage;
use PKP\core\PKPApplication;
use PKP\core\PKPRequest;
use PKP\db\DAORegistry;
use PKP\notification\NotificationDAO;
use PKP\notification\PKPNotification;
use PKP\plugins\PluginRegistry;
use PKP\security\authorization\internal\SubmissionRequiredPolicy;
use PKP\security\authorization\internal\UserAccessibleWorkflowStageRequiredPolicy;
use PKP\security\authorization\WorkflowStageAccessPolicy;
use PKP\security\Role;
use PKP\stageAssignment\StageAssignmentDAO;
use PKP\submission\GenreDAO;
use PKP\submission\PKPSubmission;
use PKP\submission\reviewRound\ReviewRoundDAO;
use PKP\user\User;
use PKP\workflow\WorkflowStageDAO;
abstract class PKPWorkflowHandler extends Handler
{
/** @copydoc PKPHandler::_isBackendPage */
public $_isBackendPage = true;
//
// Implement template methods from PKPHandler
//
/**
* @copydoc PKPHandler::authorize()
*/
public function authorize($request, &$args, $roleAssignments)
{
/** @var PageRouter */
$router = $request->getRouter();
$operation = $router->getRequestedOp($request);
if ($operation == 'access') {
// Authorize requested submission.
$this->addPolicy(new SubmissionRequiredPolicy($request, $args, 'submissionId'));
// This policy will deny access if user has no accessible workflow stage.
// Otherwise it will build an authorized object with all accessible
// workflow stages and authorize user operation access.
$this->addPolicy(new UserAccessibleWorkflowStageRequiredPolicy($request, PKPApplication::WORKFLOW_TYPE_EDITORIAL));
$this->markRoleAssignmentsChecked();
} else {
$this->addPolicy(new WorkflowStageAccessPolicy($request, $args, $roleAssignments, 'submissionId', $this->identifyStageId($request, $args), PKPApplication::WORKFLOW_TYPE_EDITORIAL));
}
return parent::authorize($request, $args, $roleAssignments);
}
//
// Public handler methods
//
/**
* Redirect users to their most appropriate
* submission workflow stage.
*
* @param array $args
* @param PKPRequest $request
*/
public function access($args, $request)
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$currentStageId = $submission->getStageId();
$accessibleWorkflowStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
$workflowRoles = Application::getWorkflowTypeRoles();
$editorialWorkflowRoles = $workflowRoles[PKPApplication::WORKFLOW_TYPE_EDITORIAL];
// Get the closest workflow stage that user has an assignment.
$workingStageId = null;
for ($workingStageId = $currentStageId; $workingStageId >= WORKFLOW_STAGE_ID_SUBMISSION; $workingStageId--) {
if (isset($accessibleWorkflowStages[$workingStageId]) && array_intersect($editorialWorkflowRoles, $accessibleWorkflowStages[$workingStageId] ?? [])) {
break;
}
}
// If no stage was found, user still have access to future stages of the
// submission. Try to get the closest future workflow stage.
if ($workingStageId == null) {
for ($workingStageId = $currentStageId; $workingStageId <= WORKFLOW_STAGE_ID_PRODUCTION; $workingStageId++) {
if (isset($accessibleWorkflowStages[$workingStageId]) && array_intersect($editorialWorkflowRoles, $accessibleWorkflowStages[$workingStageId] ?? [])) {
break;
}
}
}
assert(isset($workingStageId));
$router = $request->getRouter();
$request->redirectUrl($router->url($request, null, 'workflow', 'index', [$submission->getId(), $workingStageId]));
}
/**
* Show the workflow stage, with the stage path as an #anchor.
*
* @param array $args
* @param PKPRequest $request
*/
public function index($args, $request)
{
$this->setupTemplate($request);
$templateMgr = TemplateManager::getManager($request);
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$requestedStageId = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE);
$submissionContext = $request->getContext();
if ($submission->getContextId() !== $submissionContext->getId()) {
$submissionContext = Services::get('context')->get($submission->getContextId());
}
$workflowStages = WorkflowStageDAO::getWorkflowStageKeysAndPaths();
$accessibleWorkflowStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
$workflowRoles = Application::getWorkflowTypeRoles();
$editorialWorkflowRoles = $workflowRoles[PKPApplication::WORKFLOW_TYPE_EDITORIAL];
$result = Repo::userGroup()->getCollector()
->filterByContextIds([$submission->getData('contextId')])
->getMany();
$authorUserGroups = Repo::userGroup()->getByRoleIds([Role::ROLE_ID_AUTHOR], $submission->getData('contextId'));
$workflowUserGroups = Repo::userGroup()->getByRoleIds($editorialWorkflowRoles, $submission->getData('contextId'));
// Publication tab
// Users have access to the publication tab if they are assigned to
// the active stage id or if they are assigned as an editor or if
// they are not assigned in any role and have a manager role in the
// context.
$currentStageId = $submission->getStageId();
$accessibleWorkflowStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES);
$canAccessPublication = false; // View title, metadata, etc.
$canEditPublication = Repo::submission()->canEditPublication($submission->getId(), $request->getUser()->getId());
$canAccessProduction = false; // Access to galleys and issue entry
$canPublish = false; // Ability to publish, unpublish and create versions
$canAccessEditorialHistory = false; // Access to activity log
// unassigned managers
if (!$accessibleWorkflowStages && array_intersect($this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN] ?? [])) {
$canAccessProduction = true;
$canPublish = true;
$canAccessPublication = true;
$canAccessEditorialHistory = true;
} elseif (!empty($accessibleWorkflowStages[$currentStageId]) && array_intersect($editorialWorkflowRoles, $accessibleWorkflowStages[$currentStageId] ?? [])) {
$canAccessProduction = (bool) array_intersect($editorialWorkflowRoles, $accessibleWorkflowStages[WORKFLOW_STAGE_ID_PRODUCTION] ?? []);
$canAccessPublication = true;
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$result = $stageAssignmentDao->getBySubmissionAndUserIdAndStageId(
$submission->getId(),
$request->getUser()->getId(),
WORKFLOW_STAGE_ID_PRODUCTION
)->toArray();
// If they have no stage assignments, check the role they have been granted
// for the production workflow stage. An unassigned admin or manager may
// have been granted access and should be allowed to publish.
if (empty($result) && is_array($accessibleWorkflowStages[WORKFLOW_STAGE_ID_PRODUCTION])) {
$canPublish = (bool) array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $accessibleWorkflowStages[WORKFLOW_STAGE_ID_PRODUCTION] ?? []);
// Otherwise, check stage assignments
// "Recommend only" stage assignments can not publish
} else {
foreach ($result as $stageAssignment) {
foreach ($workflowUserGroups as $workflowUserGroup) {
if ($stageAssignment->getUserGroupId() == $workflowUserGroup->getId() &&
!$stageAssignment->getRecommendOnly()) {
$canPublish = true;
break;
}
}
}
}
}
if (!empty($accessibleWorkflowStages[$currentStageId]) && array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR], $accessibleWorkflowStages[$currentStageId] ?? [])) {
$canAccessEditorialHistory = true;
}
/** @var GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genres = $genreDao->getByContextId($submission->getData('contextId'))->toArray();
$locales = $submissionContext->getSupportedSubmissionLocaleNames();
$locales = array_map(fn (string $locale, string $name) => ['key' => $locale, 'label' => $name], array_keys($locales), $locales);
$latestPublication = $submission->getLatestPublication();
$submissionApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getData('urlPath'), 'submissions/' . $submission->getId());
$submissionFileApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getData('urlPath'), 'submissions/' . $submission->getId() . '/files');
$latestPublicationApiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $submissionContext->getData('urlPath'), 'submissions/' . $submission->getId() . '/publications/' . $latestPublication->getId());
$decisionUrl = $request->url(
$submissionContext->getData('urlPath'),
'decision',
'record',
$submission->getId(),
[
'decision' => '__decision__',
'reviewRoundId' => '__reviewRoundId__',
]
);
$editorialHistoryUrl = $request->getDispatcher()->url(
$request,
Application::ROUTE_COMPONENT,
null,
'informationCenter.SubmissionInformationCenterHandler',
'viewInformationCenter',
null,
['submissionId' => $submission->getId()]
);
$submissionLibraryUrl = $request->getDispatcher()->url(
$request,
Application::ROUTE_COMPONENT,
null,
'modals.documentLibrary.DocumentLibraryHandler',
'documentLibrary',
null,
['submissionId' => $submission->getId()]
);
$publishUrl = $request->getDispatcher()->url(
$request,
Application::ROUTE_COMPONENT,
null,
'modals.publish.PublishHandler',
'publish',
null,
[
'submissionId' => $submission->getId(),
'publicationId' => '__publicationId__',
]
);
$citationsForm = new PKPCitationsForm($latestPublicationApiUrl, $latestPublication);
$publicationLicenseForm = new PKPPublicationLicenseForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext, $authorUserGroups);
$titleAbstractForm = $this->getTitleAbstractForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext);
$authorItems = [];
foreach ($latestPublication->getData('authors') as $contributor) {
$authorItems[] = Repo::author()->getSchemaMap()->map($contributor);
}
$contributorsListPanel = $this->getContributorsListPanel(
$submission,
$submissionContext,
$locales,
$authorItems,
$canEditPublication
);
// Import constants
import('classes.components.forms.publication.PublishForm');
$templateMgr->setConstants([
'STATUS_QUEUED' => PKPSubmission::STATUS_QUEUED,
'STATUS_PUBLISHED' => PKPSubmission::STATUS_PUBLISHED,
'STATUS_DECLINED' => PKPSubmission::STATUS_DECLINED,
'STATUS_SCHEDULED' => PKPSubmission::STATUS_SCHEDULED,
'FORM_CITATIONS' => FORM_CITATIONS,
'FORM_PUBLICATION_LICENSE' => FORM_PUBLICATION_LICENSE,
'FORM_PUBLISH' => FORM_PUBLISH,
'FORM_TITLE_ABSTRACT' => FORM_TITLE_ABSTRACT,
]);
// Get the submission props without the full publication details. We'll
// retrieve just the publication information that we need separately to
// reduce the amount of data passed to the browser
$submissionProps = Repo::submission()->getSchemaMap()->summarizeWithoutPublication($submission);
// Get an array of publications
$publications = $submission->getData('publications'); /** @var Enumerable $publications */
$publicationList = $publications->map(function ($publication) {
return [
'id' => $publication->getId(),
'datePublished' => $publication->getData('datePublished'),
'status' => $publication->getData('status'),
'version' => $publication->getData('version')
];
})->values();
// Get full details of the working publication and the current publication
$mapper = Repo::publication()->getSchemaMap($submission, $authorUserGroups, $genres);
$workingPublicationProps = $mapper->map($submission->getLatestPublication());
$currentPublicationProps = $submission->getLatestPublication()->getId() === $submission->getCurrentPublication()->getId()
? $workingPublicationProps
: $mapper->map($submission->getCurrentPublication());
$state = [
'activityLogLabel' => __('submission.list.infoCenter'),
'canAccessPublication' => $canAccessPublication,
'canEditPublication' => $canEditPublication,
'components' => [
$contributorsListPanel->id => $contributorsListPanel->getConfig(),
$citationsForm->id => $citationsForm->getConfig(),
$publicationLicenseForm->id => $publicationLicenseForm->getConfig(),
$titleAbstractForm->id => $titleAbstractForm->getConfig(),
],
'currentPublication' => $currentPublicationProps,
'decisionUrl' => $decisionUrl,
'editorialHistoryUrl' => $editorialHistoryUrl,
'publicationFormIds' => [
FORM_CITATIONS,
FORM_PUBLICATION_LICENSE,
FORM_PUBLISH,
FORM_TITLE_ABSTRACT,
],
'publicationList' => $publicationList,
'publicationTabsLabel' => __('publication.version.details'),
'publishLabel' => __('publication.publish'),
'publishUrl' => $publishUrl,
'representationsGridUrl' => $this->_getRepresentationsGridUrl($request, $submission),
'schedulePublicationLabel' => __('editor.submission.schedulePublication'),
'statusLabel' => __('semicolon', ['label' => __('common.status')]),
'submission' => $submissionProps,
'submissionFileApiUrl' => $submissionFileApiUrl,
'submissionApiUrl' => $submissionApiUrl,
'submissionLibraryLabel' => __('grid.libraryFiles.submission.title'),
'submissionLibraryUrl' => $submissionLibraryUrl,
'supportsReferences' => !!$submissionContext->getData('citations'),
'unpublishConfirmLabel' => __('publication.unpublish.confirm'),
'unpublishLabel' => __('publication.unpublish'),
'unscheduleConfirmLabel' => __('publication.unschedule.confirm'),
'unscheduleLabel' => __('publication.unschedule'),
'versionLabel' => __('semicolon', ['label' => __('admin.version')]),
'versionConfirmTitle' => __('publication.createVersion'),
'versionConfirmMessage' => __('publication.version.confirm'),
'workingPublication' => $workingPublicationProps,
];
// Add the metadata form if one or more metadata fields are enabled
$vocabSuggestionUrlBase = $request->getDispatcher()->url($request, PKPApplication::ROUTE_API, $submissionContext->getData('urlPath'), 'vocabs', null, null, ['vocab' => '__vocab__']);
$metadataForm = new PKPMetadataForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext, $vocabSuggestionUrlBase, true);
$metadataFormConfig = $metadataForm->getConfig();
$metadataEnabled = count($metadataForm->fields);
if ($metadataEnabled) {
$templateMgr->setConstants([
'FORM_METADATA' => FORM_METADATA,
]);
$state['components'][FORM_METADATA] = $metadataFormConfig;
$state['publicationFormIds'][] = FORM_METADATA;
}
// Add the identifiers form if one or more identifier is enabled
$identifiersEnabled = false;
$pubIdPlugins = PluginRegistry::getPlugins('pubIds');
foreach ($pubIdPlugins as $pubIdPlugin) {
if ($pubIdPlugin->isObjectTypeEnabled('Publication', $request->getContext()->getId())) {
$identifiersEnabled = true;
break;
}
}
if ($identifiersEnabled) {
$identifiersForm = new \PKP\components\forms\publication\PKPPublicationIdentifiersForm($latestPublicationApiUrl, $locales, $latestPublication, $submissionContext);
$templateMgr->setConstants([
'FORM_PUBLICATION_IDENTIFIERS' => FORM_PUBLICATION_IDENTIFIERS,
]);
$state['components'][FORM_PUBLICATION_IDENTIFIERS] = $identifiersForm->getConfig();
$state['publicationFormIds'][] = FORM_PUBLICATION_IDENTIFIERS;
}
// Add the revision decision/recommendation forms if this app supports a review stage
if (count(array_intersect([WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW], Application::getApplicationStages() ?? []))) {
$selectRevisionDecisionForm = new \PKP\components\forms\decision\SelectRevisionDecisionForm();
$selectRevisionRecommendationForm = new \PKP\components\forms\decision\SelectRevisionRecommendationForm();
$state['components'][$selectRevisionDecisionForm->id] = $selectRevisionDecisionForm->getConfig();
$state['components'][$selectRevisionRecommendationForm->id] = $selectRevisionRecommendationForm->getConfig();
$templateMgr->setConstants([
'FORM_SELECT_REVISION_DECISION' => FORM_SELECT_REVISION_DECISION,
'FORM_SELECT_REVISION_RECOMMENDATION' => FORM_SELECT_REVISION_RECOMMENDATION,
]);
}
$templateMgr->setState($state);
$templateMgr->assign([
'canAccessEditorialHistory' => $canAccessEditorialHistory,
'canAccessPublication' => $canAccessPublication,
'canEditPublication' => $canEditPublication,
'canAccessProduction' => $canAccessProduction,
'canPublish' => $canPublish,
'identifiersEnabled' => $identifiersEnabled,
'metadataEnabled' => $metadataEnabled,
'pageComponent' => 'WorkflowPage',
'pageTitle' => implode(__('common.titleSeparator'), array_filter([
$submission->getLatestPublication()->getShortAuthorString(),
$submission->getLocalizedTitle()
])),
'pageWidth' => TemplateManager::PAGE_WIDTH_WIDE,
'requestedStageId' => $requestedStageId,
'submission' => $submission,
'workflowStages' => $workflowStages,
]);
$this->setupIndex($request);
$templateMgr->display('workflow/workflow.tpl');
}
/**
* Show the submission stage.
*
* @param array $args
* @param PKPRequest $request
*/
public function submission($args, $request)
{
$this->_redirectToIndex($args, $request);
}
/**
* Show the external review stage.
*
* @param array $args
* @param PKPRequest $request
*/
public function externalReview($args, $request)
{
$this->_redirectToIndex($args, $request);
}
/**
* Show the editorial stage
*
* @param PKPRequest $request
* @param array $args
*/
public function editorial($args, $request)
{
$this->_redirectToIndex($args, $request);
}
/**
* Show the production stage
*
* @param PKPRequest $request
* @param array $args
*/
public function production($args, $request)
{
$this->_redirectToIndex($args, $request);
}
/**
* Redirect all old stage paths to index
*
* @param array $args
* @param PKPRequest $request
*/
protected function _redirectToIndex($args, $request)
{
// Translate the operation to a workflow stage identifier.
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$router = $request->getRouter();
$workflowPath = $router->getRequestedOp($request);
$stageId = WorkflowStageDAO::getIdFromPath($workflowPath);
$request->redirectUrl($router->url($request, null, 'workflow', 'index', [$submission->getId(), $stageId]));
}
/**
* Fetch JSON-encoded editor decision options.
*
* @param array $args
* @param Request $request
*
* @return JSONMessage JSON object
*/
public function editorDecisionActions($args, $request)
{
$this->setupTemplate($request);
$reviewRoundId = (int) $request->getUserVar('reviewRoundId');
// Prepare the action arguments.
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$stageId = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_WORKFLOW_STAGE);
$actionArgs = [
'submissionId' => $submission->getId(),
'stageId' => (int) $stageId,
];
// If a review round was specified, include it in the args;
// must also check that this is the last round or decisions
// cannot be recorded.
$reviewRound = null;
if ($reviewRoundId) {
$actionArgs['reviewRoundId'] = $reviewRoundId;
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /** @var ReviewRoundDAO $reviewRoundDao */
$lastReviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $stageId);
$reviewRound = $reviewRoundDao->getById($reviewRoundId);
} else {
$lastReviewRound = null;
}
// If there is an editor assigned, retrieve stage decisions.
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /** @var StageAssignmentDAO $stageAssignmentDao */
$editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $stageId);
$user = $request->getUser();
$makeRecommendation = $makeDecision = false;
// if the user is assigned several times in an editorial role, check his/her assignments permissions i.e.
// if the user is assigned with both possibilities: to only recommend as well as make decision
foreach ($editorsStageAssignments as $editorsStageAssignment) {
if ($editorsStageAssignment->getUserId() == $user->getId()) {
if (!$editorsStageAssignment->getRecommendOnly()) {
$makeDecision = true;
} else {
$makeRecommendation = true;
}
}
}
// If user is not assigned to the submission,
// see if the user is manager, and
// if the group is recommendOnly
if (!$makeRecommendation && !$makeDecision) {
$userGroups = Repo::userGroup()->userUserGroups($user->getId(), $request->getContext()->getId());
foreach ($userGroups as $userGroup) {
if (in_array($userGroup->getRoleId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN])) {
if (!$userGroup->getRecommendOnly()) {
$makeDecision = true;
} else {
$makeRecommendation = true;
}
}
}
}
// if the user can make recommendations, check whether there are any decisions that can be made given
// the stage that we are operating into.
$isOnlyRecommending = $makeRecommendation && !$makeDecision;
if ($isOnlyRecommending) {
$recommendatorsAvailableDecisions = Repo::decision()
->getDecisionTypesMadeByRecommendingUsers($stageId);
if (!empty($recommendatorsAvailableDecisions)) {
// If there are any, then the user can be considered a decision user.
$makeDecision = true;
}
}
$lastRecommendation = null;
$allRecommendations = null;
$hasDecidingEditors = false;
if (!empty($editorsStageAssignments) && (!$reviewRoundId || ($lastReviewRound && $reviewRoundId == $lastReviewRound->getId()))) {
// If this is a review stage and the user has "recommend only role"
if (($stageId == WORKFLOW_STAGE_ID_EXTERNAL_REVIEW || $stageId == WORKFLOW_STAGE_ID_INTERNAL_REVIEW)) {
if ($makeRecommendation) {
// Get the made editorial decisions from the current user
$editorDecisions = Repo::decision()->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByStageIds([$stageId])
->filterByReviewRoundIds([$reviewRound->getId()])
->filterByEditorIds([$user->getId()])
->getMany();
// Get the last recommendation
foreach ($editorDecisions as $editorDecision) {
if (Repo::decision()->isRecommendation($editorDecision->getData('decision'))) {
if ($lastRecommendation) {
if ($editorDecision->getData('dateDecided') >= $lastRecommendation->getData('dateDecided')) {
$lastRecommendation = $editorDecision;
}
} else {
$lastRecommendation = $editorDecision;
}
}
}
if ($lastRecommendation) {
$lastRecommendation = $this->getRecommendationLabel($lastRecommendation->getData('decision'));
}
// At least one deciding editor must be assigned before a recommendation can be made
/** @var StageAssignmentDAO $stageAssignmentDao */
$stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO');
$decidingEditorIds = $stageAssignmentDao->getDecidingEditorIds($submission->getId(), $stageId);
$hasDecidingEditors = count($decidingEditorIds) > 0;
} elseif ($makeDecision) {
// Get the made editorial decisions from all users
$editorDecisions = Repo::decision()
->getCollector()
->filterBySubmissionIds([$submission->getId()])
->filterByStageIds([$stageId])
->filterByReviewRoundIds([$reviewRound->getId()])
->getMany();
// Get all recommendations
$recommendations = [];
foreach ($editorDecisions as $editorDecision) {
if (Repo::decision()->isRecommendation($editorDecision->getData('decision'))) {
if (array_key_exists($editorDecision->getData('editorId'), $recommendations)) {
if ($editorDecision->getData('dateDecided') >= $recommendations[$editorDecision->getData('editorId')]['dateDecided']) {
$recommendations[$editorDecision->getData('editorId')] = ['dateDecided' => $editorDecision->getData('dateDecided'), 'decision' => $editorDecision->getData('decision')];
}
} else {
$recommendations[$editorDecision->getData('editorId')] = ['dateDecided' => $editorDecision->getData('dateDecided'), 'decision' => $editorDecision->getData('decision')];
}
}
}
$allRecommendations = [];
foreach ($recommendations as $recommendation) {
$allRecommendations[] = $this->getRecommendationLabel($recommendation['decision']);
}
$allRecommendations = join(__('common.commaListSeparator'), $allRecommendations);
}
}
}
$hasSubmissionPassedThisStage = $submission->getStageId() > $stageId;
$lastDecision = null;
switch ($submission->getStatus()) {
case PKPSubmission::STATUS_QUEUED:
switch ($stageId) {
case WORKFLOW_STAGE_ID_SUBMISSION:
if ($hasSubmissionPassedThisStage) {
$lastDecision = 'editor.submission.workflowDecision.submission.underReview';
}
break;
case WORKFLOW_STAGE_ID_INTERNAL_REVIEW:
case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
if ($reviewRoundId < $lastReviewRound->getId()) {
$lastDecision = 'editor.submission.workflowDecision.submission.reviewRound';
} elseif ($hasSubmissionPassedThisStage) {
$lastDecision = 'editor.submission.workflowDecision.submission.accepted';
}
break;
case WORKFLOW_STAGE_ID_EDITING:
if ($hasSubmissionPassedThisStage) {
$lastDecision = 'editor.submission.workflowDecision.submission.production';
}
break;
}
break;
case PKPSubmission::STATUS_PUBLISHED:
$lastDecision = 'editor.submission.workflowDecision.submission.published';
break;
case PKPSubmission::STATUS_DECLINED:
$lastDecision = 'editor.submission.workflowDecision.submission.declined';
break;
}
$canRecordDecision =
// Only allow decisions to be recorded on the submission's current stage
$submission->getData('stageId') == $stageId
// Only allow decisions on the latest review round
&& (!$lastReviewRound || $lastReviewRound->getId() == $reviewRoundId)
// At least one deciding editor must be assigned to make a recommendation
&& ($makeDecision || $hasDecidingEditors);
$decisions = $this->getStageDecisionTypes($stageId);
if ($isOnlyRecommending) {
$decisions = Repo::decision()
->getDecisionTypesMadeByRecommendingUsers($stageId);
}
// Assign the actions to the template.
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'canRecordDecision' => $canRecordDecision,
'decisions' => $decisions,
'recommendations' => $this->getStageRecommendationTypes($stageId),
'primaryDecisions' => $this->getPrimaryDecisionTypes(),
'warnableDecisions' => $this->getWarnableDecisionTypes(),
'editorsAssigned' => count($editorsStageAssignments) > 0,
'stageId' => $stageId,
'reviewRoundId' => $reviewRound
? $reviewRound->getId()
: null,
'lastDecision' => $lastDecision,
'lastReviewRound' => $lastReviewRound,
'submission' => $submission,
'makeRecommendation' => $makeRecommendation,
'makeDecision' => $makeDecision,
'lastRecommendation' => $lastRecommendation,
'allRecommendations' => $allRecommendations,
]);
return $templateMgr->fetchJson('workflow/editorialLinkActions.tpl');
}
/**
* Fetch the JSON-encoded submission progress bar.
*
* @param array $args
* @param Request $request
*
* @return JSONMessage JSON object
*/
public function submissionProgressBar($args, $request)
{
$this->setupTemplate($request);
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$workflowStages = WorkflowStageDAO::getWorkflowStageKeysAndPaths();
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'submission' => $submission,
'currentStageId' => $this->identifyStageId($request, $args),
'workflowStages' => $workflowStages,
]);
return $templateMgr->fetchJson('workflow/submissionProgressBar.tpl');
}
/**
* Placeholder method to be overridden by apps in order to add
* app-specific data to the template
*
* @param Request $request
*/
public function setupIndex($request)
{
}
//
// Protected helper methods
//
/**
* Translate the requested operation to a stage id.
*
* @param Request $request
* @param array $args
*
* @return int One of the WORKFLOW_STAGE_* constants.
*/
protected function identifyStageId($request, $args)
{
if ($stageId = $request->getUserVar('stageId')) {
return (int) $stageId;
}
// Maintain the old check for previous path urls
$router = $request->getRouter();
$workflowPath = $router->getRequestedOp($request);
$stageId = WorkflowStageDAO::getIdFromPath($workflowPath);
if ($stageId) {
return $stageId;
}
// Finally, retrieve the requested operation, if the stage id is
// passed in via an argument in the URL, like index/submissionId/stageId
$stageId = $args[1];
// Translate the operation to a workflow stage identifier.
assert(WorkflowStageDAO::getPathFromId($stageId) !== null);
return $stageId;
}
/**
* Determine if a particular stage has a notification pending. If so, return true.
* This is used to set the CSS class of the submission progress bar.
*
* @param User $user
* @param int $stageId
* @param int $contextId
*
* @return bool
*/
protected function notificationOptionsByStage($user, $stageId, $contextId)
{
$submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION);
$notificationDao = DAORegistry::getDAO('NotificationDAO'); /** @var NotificationDAO $notificationDao */
$editorAssignmentNotificationType = $this->getEditorAssignmentNotificationTypeByStageId($stageId);
$editorAssignments = $notificationDao->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), null, $editorAssignmentNotificationType, $contextId);
// if the User has assigned TASKs in this stage check, return true
if ($editorAssignments->next()) {
return true;
}
// check for more specific notifications on those stages that have them.
if ($stageId == WORKFLOW_STAGE_ID_PRODUCTION) {
$submissionApprovalNotification = $notificationDao->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), null, PKPNotification::NOTIFICATION_TYPE_APPROVE_SUBMISSION, $contextId);
if ($submissionApprovalNotification->next()) {
return true;
}
}
return false;
}
/**
* Get a label for a recommendation decision type
*/
protected function getRecommendationLabel(int $decision): string
{
$decisionType = Repo::decision()->getDecisionType($decision);
if (!$decisionType || !method_exists($decisionType, 'getRecommendationLabel')) {
throw new Exception('Could not find label for unknown recommendation type.');
}
return $decisionType->getRecommendationLabel();
}
/**
* Get the contributor list panel
*/
protected function getContributorsListPanel(Submission $submission, Context $context, array $locales, array $authorItems, bool $canEditPublication): ContributorsListPanel
{
return new ContributorsListPanel(
'contributors',
__('publication.contributors'),
$submission,
$context,
$locales,
$authorItems,
$canEditPublication
);
}
//
// Abstract protected methods.
//
/**
* Return the editor assignment notification type based on stage id.
*
* @param int $stageId
*
* @return int
*/
abstract protected function getEditorAssignmentNotificationTypeByStageId($stageId);
/**
* Get the URL for the galley/publication formats grid with a placeholder for
* the publicationId value
*
* @param Request $request
* @param Submission $submission
*
* @return string
*/
abstract protected function _getRepresentationsGridUrl($request, $submission);
/**
* A helper method to get a list of editor decisions to
* show on the right panel of each stage
*
* @return string[]
*/
abstract protected function getStageDecisionTypes(int $stageId): array;
/**
* A helper method to get a list of editor recommendations to
* show on the right panel of the review stage
*
*/
abstract protected function getStageRecommendationTypes(int $stageId): array;
/**
* Get the editor decision types that should be shown
* as primary buttons (eg - Accept)
*
* @return string[]
*/
abstract protected function getPrimaryDecisionTypes(): array;
/**
* Get the editor decision types that should be shown
* as warnable buttons (eg - Decline)
*
* @return string[]
*/
abstract protected function getWarnableDecisionTypes(): array;
/**
* Get the form for entering the title/abstract details
*/
abstract protected function getTitleAbstractForm(string $latestPublicationApiUrl, array $locales, Publication $latestPublication, Context $context): TitleAbstractForm;
}