first commit
This commit is contained in:
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user