[ Mini Kiebo ]
Server: Windows NT DESKTOP-5B8S0D4 6.2 build 9200 (Windows 8 Professional Edition) i586
Path:
D:
/
Backup
/
14082024
/
Data
/
htdocs
/
htdocs
/
jurnal-kesmas
/
lib
/
pkp
/
api
/
v1
/
contexts
/
[
Home
]
File: PKPContextHandler.php
<?php /** * @file api/v1/contexts/PKPContextHandler.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 PKPContextHandler * * @ingroup api_v1_context * * @brief Base class to handle API requests for contexts (journals/presses). */ namespace PKP\API\v1\contexts; use APP\core\Application; use APP\core\Services; use APP\plugins\IDoiRegistrationAgency; use APP\services\ContextService; use APP\template\TemplateManager; use PKP\context\Context; use PKP\core\APIResponse; use PKP\db\DAORegistry; use PKP\handler\APIHandler; use PKP\plugins\Hook; use PKP\plugins\Plugin; use PKP\plugins\PluginRegistry; use PKP\plugins\ThemePlugin; use PKP\security\authorization\PolicySet; use PKP\security\authorization\RoleBasedHandlerOperationPolicy; use PKP\security\authorization\UserRolesRequiredPolicy; use PKP\security\Role; use PKP\security\RoleDAO; use PKP\services\interfaces\EntityWriteInterface; use PKP\services\PKPSchemaService; use Slim\Http\Request as SlimRequest; use Slim\Http\Response as SlimResponse; class PKPContextHandler extends APIHandler { /** @var string One of the SCHEMA_... constants */ public $schemaName = PKPSchemaService::SCHEMA_CONTEXT; /** * @copydoc APIHandler::__construct() */ public function __construct() { $this->_handlerPath = 'contexts'; $roles = [Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER]; $this->_endpoints = [ 'GET' => [ [ 'pattern' => $this->getEndpointPattern(), 'handler' => [$this, 'getMany'], 'roles' => $roles, ], [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}', 'handler' => [$this, 'get'], 'roles' => $roles, ], [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}/theme', 'handler' => [$this, 'getTheme'], 'roles' => $roles, ], ], 'POST' => [ [ 'pattern' => $this->getEndpointPattern(), 'handler' => [$this, 'add'], 'roles' => [Role::ROLE_ID_SITE_ADMIN], ], ], 'PUT' => [ [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}', 'handler' => [$this, 'edit'], 'roles' => $roles, ], [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}/theme', 'handler' => [$this, 'editTheme'], 'roles' => $roles, ], [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}/registrationAgency', 'handler' => [$this, 'editDoiRegistrationAgencyPlugin'], 'roles' => $roles, ] ], 'DELETE' => [ [ 'pattern' => $this->getEndpointPattern() . '/{contextId:\d+}', 'handler' => [$this, 'delete'], 'roles' => [Role::ROLE_ID_SITE_ADMIN], ], ], ]; parent::__construct(); } /** * @copydoc PKPHandler::authorize */ public function authorize($request, &$args, $roleAssignments) { $this->addPolicy(new UserRolesRequiredPolicy($request), true); $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); } /** * Get a collection of contexts * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function getMany($slimRequest, $response, $args) { $request = $this->getRequest(); $defaultParams = [ 'count' => 20, 'offset' => 0, ]; $requestParams = array_merge($defaultParams, $slimRequest->getQueryParams()); $allowedParams = []; // Process query params to format incoming data as needed foreach ($requestParams as $param => $val) { switch ($param) { case 'isEnabled': $allowedParams[$param] = (bool) $val; break; case 'searchPhrase': $allowedParams[$param] = trim($val); break; case 'count': $allowedParams[$param] = min(100, (int) $val); break; case 'offset': $allowedParams[$param] = (int) $val; break; } } Hook::call('API::contexts::params', [&$allowedParams, $slimRequest]); // Anyone not a site admin should not be able to access contexts that are // not enabled if (empty($allowedParams['isEnabled'])) { $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); $canAccessDisabledContexts = !empty(array_intersect([Role::ROLE_ID_SITE_ADMIN], $userRoles)); if (!$canAccessDisabledContexts) { return $response->withStatus(403)->withJsonError('api.contexts.403.requestedDisabledContexts'); } } $items = []; $contextsIterator = Services::get('context')->getMany($allowedParams); $propertyArgs = [ 'request' => $request, 'slimRequest' => $slimRequest, ]; foreach ($contextsIterator as $context) { $items[] = Services::get('context')->getSummaryProperties($context, $propertyArgs); } $data = [ 'itemsMax' => Services::get('context')->getMax($allowedParams), 'items' => $items, ]; return $response->withJson($data, 200); } /** * Get a single context * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function get($slimRequest, $response, $args) { $request = $this->getRequest(); $user = $request->getUser(); $contextService = Services::get('context'); $context = $contextService->get((int) $args['contextId']); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } // Don't allow to get one context from a different context's endpoint if ($request->getContext() && $request->getContext()->getId() !== $context->getId()) { return $response->withStatus(403)->withJsonError('api.contexts.403.contextsDidNotMatch'); } // A disabled journal can only be access by site admins and users with a // manager role in that journal if (!$context->getEnabled()) { $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); if (!in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles)) { $roleDao = DAORegistry::getDao('RoleDAO'); /** @var RoleDAO $roleDao */ if (!$roleDao->userHasRole($context->getId(), $user->getId(), Role::ROLE_ID_MANAGER)) { return $response->withStatus(403)->withJsonError('api.contexts.403.notAllowed'); } } } $data = $contextService->getFullProperties($context, [ 'request' => $request, 'slimRequest' => $slimRequest ]); return $response->withJson($data, 200); } /** * Get the theme and any theme options for a context * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function getTheme($slimRequest, $response, $args) { $request = $this->getRequest(); $user = $request->getUser(); $contextService = Services::get('context'); $context = $contextService->get((int) $args['contextId']); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } // Don't allow to get one context from a different context's endpoint if ($request->getContext() && $request->getContext()->getId() !== $context->getId()) { return $response->withStatus(403)->withJsonError('api.contexts.403.contextsDidNotMatch'); } // A disabled journal can only be access by site admins and users with a // manager role in that journal if (!$context->getEnabled()) { $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); if (!in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles)) { $roleDao = DAORegistry::getDao('RoleDAO'); /** @var RoleDAO $roleDao */ if (!$roleDao->userHasRole($context->getId(), $user->getId(), Role::ROLE_ID_MANAGER)) { return $response->withStatus(403)->withJsonError('api.contexts.403.notAllowed'); } } } $allThemes = PluginRegistry::loadCategory('themes', true); $activeTheme = null; foreach ($allThemes as $theme) { if ($context->getData('themePluginPath') === $theme->getDirName()) { $activeTheme = $theme; break; } } if (!$activeTheme) { return $response->withStatus(404)->withJsonError('api.themes.404.themeUnavailable'); } $data = array_merge( $activeTheme->getOptionValues($context->getId()), ['themePluginPath' => $theme->getDirName()] ); ksort($data); return $response->withJson($data, 200); } /** * Add a context * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function add($slimRequest, $response, $args) { $request = $this->getRequest(); // This endpoint is only available at the site-wide level if ($request->getContext()) { return $response->withStatus(404)->withJsonError('api.submissions.404.siteWideEndpoint'); } $site = $request->getSite(); $params = $this->convertStringsToSchema(PKPSchemaService::SCHEMA_CONTEXT, $slimRequest->getParsedBody()); $primaryLocale = $site->getPrimaryLocale(); $allowedLocales = $site->getSupportedLocales(); // If the site only supports a single locale, set the context's locales if (count($allowedLocales) === 1) { if (!isset($params['primaryLocale'])) { $params['primaryLocale'] = $primaryLocale; } if (!isset($params['supportedLocales'])) { $params['supportedLocales'] = $allowedLocales; } } $contextService = Services::get('context'); /** @var ContextService $contextService */ $errors = $contextService->validate(EntityWriteInterface::VALIDATE_ACTION_ADD, $params, $allowedLocales, $primaryLocale); if (!empty($errors)) { return $response->withStatus(400)->withJson($errors); } $context = Application::getContextDAO()->newDataObject(); $context->setAllData($params); $context = $contextService->add($context, $request); $contextProps = $contextService->getFullProperties($context, [ 'request' => $request, 'slimRequest' => $slimRequest ]); return $response->withJson($contextProps, 200); } /** * Edit a context * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function edit($slimRequest, $response, $args) { $request = $this->getRequest(); $requestContext = $request->getContext(); $contextId = (int) $args['contextId']; // Don't allow to get one context from a different context's endpoint if ($request->getContext() && $request->getContext()->getId() !== $contextId) { return $response->withStatus(403)->withJsonError('api.contexts.403.contextsDidNotMatch'); } // Don't allow to edit the context from the site-wide API, because the // context's plugins will not be enabled if (!$request->getContext()) { return $response->withStatus(403)->withJsonError('api.contexts.403.requiresContext'); } $contextService = Services::get('context'); $context = $contextService->get($contextId); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); if (!$requestContext && !in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles)) { return $response->withStatus(403)->withJsonError('api.contexts.403.notAllowedEdit'); } $params = $this->convertStringsToSchema(PKPSchemaService::SCHEMA_CONTEXT, $slimRequest->getParsedBody()); $params['id'] = $contextId; $site = $request->getSite(); $primaryLocale = $context->getPrimaryLocale(); $allowedLocales = $context->getSupportedFormLocales(); $errors = $contextService->validate(EntityWriteInterface::VALIDATE_ACTION_EDIT, $params, $allowedLocales, $primaryLocale); if (!empty($errors)) { return $response->withStatus(400)->withJson($errors); } $context = $contextService->edit($context, $params, $request); $contextProps = $contextService->getFullProperties($context, [ 'request' => $request, 'slimRequest' => $slimRequest ]); return $response->withJson($contextProps, 200); } /** * Edit a context's theme and theme options * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function editTheme($slimRequest, $response, $args) { $request = $this->getRequest(); $requestContext = $request->getContext(); $contextId = (int) $args['contextId']; // Don't allow to get one context from a different context's endpoint if ($request->getContext() && $request->getContext()->getId() !== $contextId) { return $response->withStatus(403)->withJsonError('api.contexts.403.contextsDidNotMatch'); } // Don't allow to edit the context from the site-wide API, because the // context's plugins will not be enabled if (!$requestContext) { return $response->withStatus(403)->withJsonError('api.contexts.403.requiresContext'); } $contextService = Services::get('context'); $context = $contextService->get($contextId); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); $allowedRoles = [Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER]; if (!array_intersect($allowedRoles, $userRoles)) { return $response->withStatus(403)->withJsonError('api.contexts.403.notAllowedEdit'); } $params = $slimRequest->getParsedBody(); // Validate the themePluginPath and allow themes to perform their own validation $themePluginPath = empty($params['themePluginPath']) ? null : $params['themePluginPath']; if ($themePluginPath !== $context->getData('themePluginPath')) { $errors = $contextService->validate( EntityWriteInterface::VALIDATE_ACTION_EDIT, ['themePluginPath' => $themePluginPath], $context->getSupportedFormLocales(), $context->getPrimaryLocale() ); if (!empty($errors)) { return $response->withJson($errors, 400); } $newContext = $contextService->edit($context, ['themePluginPath' => $themePluginPath], $request); } // Get the appropriate theme plugin /** @var iterable<ThemePlugin> */ $allThemes = PluginRegistry::loadCategory('themes', true); /** @var ?ThemePlugin */ $selectedTheme = null; foreach ($allThemes as $theme) { if ($themePluginPath === $theme->getDirName()) { $selectedTheme = $theme; break; } } // Run the theme's init() method if a new theme has been selected if (isset($newContext)) { $selectedTheme->init(); } $errors = $selectedTheme->validateOptions($params, $themePluginPath, $context->getId(), $request); if (!empty($errors)) { return $response->withJson($errors, 400); } // Only accept params that are defined in the theme options $options = $selectedTheme->getOptionsConfig(); foreach ($options as $optionName => $optionConfig) { if (!array_key_exists($optionName, $params)) { continue; } $selectedTheme->saveOption($optionName, $params[$optionName], $context->getId()); } // Clear the template cache so that new settings can take effect $templateMgr = TemplateManager::getManager(Application::get()->getRequest()); $templateMgr->clearTemplateCache(); $templateMgr->clearCssCache(); $data = array_merge( $selectedTheme->getOptionValues($context->getId()), ['themePluginPath' => $themePluginPath] ); ksort($data); return $response->withJson($data, 200); } /** @param APIResponse $response */ public function editDoiRegistrationAgencyPlugin(SlimRequest $slimRequest, SlimResponse $response, array $args): SlimResponse { $request = $this->getRequest(); $requestContext = $request->getContext(); $contextId = (int) $args['contextId']; // Don't allow to get one context from a different context's endpoint if ($request->getContext() && $request->getContext()->getId() !== $contextId) { return $response->withStatus(403)->withJsonError('api.contexts.403.contextsDidNotMatch'); } // Don't allow to edit the context from the site-wide API, because the // context's plugins will not be enabled if (!$requestContext) { return $response->withStatus(403)->withJsonError('api.contexts.403.requiresContext'); } /** @var ContextService $contextService */ $contextService = Services::get('context'); $context = $contextService->get($contextId); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); if (!array_intersect([Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_MANAGER], $userRoles)) { return $response->withStatus(403)->withJsonError('api.contexts.403.notAllowedEdit'); } /** @var PKPSchemaService $schemaService */ $schemaService = Services::get('schema'); $params = $this->convertStringsToSchema(PKPSchemaService::SCHEMA_CONTEXT, $slimRequest->getParsedBody()); $contextFullProps = array_flip($schemaService->getFullProps(PKPSchemaService::SCHEMA_CONTEXT)); $contextParams = array_intersect_key( $params, $contextFullProps, ); // Validate the registrationAgency and automatic deposit fields // and allow agencies to perform their own validation. if (!empty($contextParams)) { $errors = $contextService->validate( ContextService::VALIDATE_ACTION_EDIT, $contextParams, $context->getSupportedFormLocales(), $context->getPrimaryLocale(), ); if (!empty($errors)) { return $response->withJson($errors, 400); } $contextService->edit( $context, $contextParams, $request ); } // Return if no registration agency enabled; if ($contextParams[Context::SETTING_CONFIGURED_REGISTRATION_AGENCY] === null) { return $response->withJson($contextParams, 200); } // Get the appropriate agency plugin $plugins = PluginRegistry::loadCategory('generic', true); $selectedPlugin = null; foreach ($plugins as $plugin) { if ( $contextParams[Context::SETTING_CONFIGURED_REGISTRATION_AGENCY] === $plugin->getName() ) { $selectedPlugin = $plugin; break; } } // Check if it's a registration agency plugin if (!$selectedPlugin instanceof IDoiRegistrationAgency) { return $response->withStatus(400)->withJsonError('api.dois.400.invalidPluginType'); } // If it's a new/different registration agency plugin, update the enabled DOI types based on // allowed types per the registration agency plugin if ( $context->getData(Context::SETTING_CONFIGURED_REGISTRATION_AGENCY) !== $contextParams[Context::SETTING_CONFIGURED_REGISTRATION_AGENCY] && $contextParams[Context::SETTING_CONFIGURED_REGISTRATION_AGENCY] !== null ) { /** @var Context $newContext */ $newContext = $contextService->get($contextId); $enabledPubObjectTypes = $newContext->getEnabledDoiTypes(); $allowedPubObjectTypes = $selectedPlugin->getAllowedDoiTypes(); $filteredPubObjectTypes = array_intersect($enabledPubObjectTypes, $allowedPubObjectTypes); if ($filteredPubObjectTypes != $enabledPubObjectTypes) { $contextService->edit( $newContext, [Context::SETTING_ENABLED_DOI_TYPES => $filteredPubObjectTypes], $request ); } } $settingsObject = $selectedPlugin->getSettingsObject(); $params = $this->convertStringsToSchema($settingsObject::class, $slimRequest->getParsedBody()); $pluginParams = array_intersect_key( $params, (array) $settingsObject->getSchema()->properties, ); // Validate plugin settings $errors = $settingsObject->validate($pluginParams); if (!empty($errors)) { return $response->withStatus(400)->withJson($errors); } $this->updateRegistrationAgencyPluginSettings( $contextId, $selectedPlugin, $settingsObject::class, $pluginParams, ); return $response->withJson( array_merge($contextParams, $pluginParams), 200, ); } /** * Delete a context * * @param SlimRequest $slimRequest Slim request object * @param APIResponse $response object * @param array $args arguments * * @return APIResponse */ public function delete($slimRequest, $response, $args) { // This endpoint is only available at the site-wide level if ($this->getRequest()->getContext()) { return $response->withStatus(404)->withJsonError('api.submissions.404.siteWideEndpoint'); } $userRoles = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_USER_ROLES); if (!in_array(Role::ROLE_ID_SITE_ADMIN, $userRoles)) { $response->withStatus(403)->withJsonError('api.contexts.403.notAllowedDelete'); } $contextId = (int) $args['contextId']; $contextService = Services::get('context'); $context = $contextService->get($contextId); if (!$context) { return $response->withStatus(404)->withJsonError('api.contexts.404.contextNotFound'); } $contextProps = $contextService->getSummaryProperties($context, [ 'request' => $this->getRequest(), 'slimRequest' => $slimRequest ]); $contextService->delete($context); return $response->withJson($contextProps, 200); } /** * Updates a settings plugin according to a given schema. Used in lieu of a generic plugin settings management workflow. * * @param Plugin $plugin Currently configured registration agency plugin. Should also implement IDoiRegistrationAgency * @param string $schemaName Name of RegistrationAgencySettings child class used as schema name * @param array $props Plugin properties to update */ protected function updateRegistrationAgencyPluginSettings(int $contextId, Plugin $plugin, string $schemaName, array $props): void { /** @var PKPSchemaService $schemaService */ $schemaService = Services::get('schema'); $sanitizedProps = $schemaService->sanitize($schemaName, $props); foreach ($sanitizedProps as $fieldName => $value) { $plugin->updateSetting($contextId, $fieldName, $value); } } }