| Server IP : 127.0.1.1 / Your IP : 216.73.216.83 Web Server : Apache/2.4.58 (Ubuntu) System : Linux nepub 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64 User : root ( 0) PHP Version : 8.2.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/public_html/lib/pkp/classes/services/ |
Upload File : |
<?php
/**
* @file classes/services/PKPContextService.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 PKPContextService
* @ingroup services
*
* @brief Helper class that encapsulates business logic for contexts (journals
* and presses)
*/
namespace PKP\Services;
use \DBResultRange;
use \Application;
use \DAOResultFactory;
use \DAORegistry;
use \Services;
use \PKP\Services\interfaces\EntityPropertyInterface;
use \PKP\Services\interfaces\EntityReadInterface;
use \PKP\Services\interfaces\EntityWriteInterface;
use \APP\Services\QueryBuilders\ContextQueryBuilder;
import('lib.pkp.classes.db.DBResultRange');
abstract class PKPContextService implements EntityPropertyInterface, EntityReadInterface, EntityWriteInterface {
/**
* @var array List of file directories to create on installation. Use %d to
* use the context ID in a file path.
*/
var $installFileDirs;
/**
* @var array The file directory where context files are stored. Expects
* `journals` or `presses`.
*/
var $contextsFileDirName;
/**
* @copydoc \PKP\Services\interfaces\EntityReadInterface::get()
*/
public function get($contextId) {
return Application::getContextDAO()->getById($contextId);
}
/**
* @copydoc \PKP\Services\interfaces\EntityReadInterface::getCount()
*/
public function getCount($args = []) {
return $this->getQueryBuilder($args)->getCount();
}
/**
* @copydoc \PKP\Services\interfaces\EntityReadInterface::getIds()
*/
public function getIds($args = []) {
return $this->getQueryBuilder($args)->getIds();
}
/**
* Get a summary of context information limited, filtered
* and sorted by $args.
*
* This is faster than getMany if you don't need to
* retrieve all the context settings. It returns the
* data from the main table and the name of the context
* in its primary locale.
*
* @see self::getMany()
* @return array
*/
public function getManySummary($args = []) {
return $this->getQueryBuilder($args)->getManySummary();
}
/**
* Get a collection of Context objects limited, filtered
* and sorted by $args
*
* @param array $args {
* @option bool isEnabled
* @option int userId
* @option string searchPhrase
* @option int count
* @option int offset
* }
* @return Iterator
*/
public function getMany($args = array()) {
$range = null;
if (isset($args['count'])) {
import('lib.pkp.classes.db.DBResultRange');
$range = new \DBResultRange($args['count'], null, isset($args['offset']) ? $args['offset'] : 0);
}
// Pagination is handled by the DAO, so don't pass count and offset
// arguments to the QueryBuilder.
if (isset($args['count'])) unset($args['count']);
if (isset($args['offset'])) unset($args['offset']);
$contextListQO = $this->getQueryBuilder($args)->getQuery();
$contextDao = Application::getContextDAO();
$result = $contextDao->retrieveRange($contextListQO->toSql(), $contextListQO->getBindings(), $range);
$queryResults = new DAOResultFactory($result, $contextDao, '_fromRow');
return $queryResults->toIterator();
}
/**
* @copydoc \PKP\Services\interfaces\EntityReadInterface::getMax()
*/
public function getMax($args = array()) {
// Don't accept args to limit the results
if (isset($args['count'])) unset($args['count']);
if (isset($args['offset'])) unset($args['offset']);
return $this->getQueryBuilder($args)->getCount();
}
/**
* @copydoc \PKP\Services\interfaces\EntityReadInterface::getQueryBuilder()
* @return ContextQueryBuilder
*/
public function getQueryBuilder($args = array()) {
$defaultArgs = array(
'isEnabled' => null,
'userId' => null,
'searchPhrase' => null,
);
$args = array_merge($defaultArgs, $args);
$contextListQB = new ContextQueryBuilder();
$contextListQB
->filterByIsEnabled($args['isEnabled'])
->filterByUserId($args['userId'])
->searchPhrase($args['searchPhrase']);
\HookRegistry::call('Context::getMany::queryBuilder', array(&$contextListQB, $args));
return $contextListQB;
}
/**
* @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getProperties()
*/
public function getProperties($context, $props, $args = null) {
$slimRequest = $args['slimRequest'];
$request = $args['request'];
$dispatcher = $request->getDispatcher();
$values = array();
foreach ($props as $prop) {
switch ($prop) {
case 'url':
$values[$prop] = $dispatcher->url(
$request,
ROUTE_PAGE,
$context->getPath()
);
break;
case '_href':
$values[$prop] = null;
if (!empty($slimRequest)) {
$route = $slimRequest->getAttribute('route');
$values[$prop] = $dispatcher->url(
$args['request'],
ROUTE_API,
$context->getData('urlPath'),
'contexts/' . $context->getId()
);
}
break;
default:
$values[$prop] = $context->getData($prop);
break;
}
}
$supportedLocales = empty($args['supportedLocales']) ? $context->getSupportedFormLocales() : $args['supportedLocales'];
$values = Services::get('schema')->addMissingMultilingualValues(SCHEMA_CONTEXT, $values, $supportedLocales);
\HookRegistry::call('Context::getProperties', array(&$values, $context, $props, $args));
ksort($values);
return $values;
}
/**
* @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getSummaryProperties()
*/
public function getSummaryProperties($context, $args = null) {
$props = Services::get('schema')->getSummaryProps(SCHEMA_CONTEXT);
return $this->getProperties($context, $props, $args);
}
/**
* @copydoc \PKP\Services\interfaces\EntityPropertyInterface::getFullProperties()
*/
public function getFullProperties($context, $args = null) {
$props = Services::get('schema')->getFullProps(SCHEMA_CONTEXT);
return $this->getProperties($context, $props, $args);
}
/**
* @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::validate()
*/
public function validate($action, $props, $allowedLocales, $primaryLocale) {
\AppLocale::requireComponents(
LOCALE_COMPONENT_PKP_ADMIN,
LOCALE_COMPONENT_APP_ADMIN,
LOCALE_COMPONENT_PKP_MANAGER,
LOCALE_COMPONENT_APP_MANAGER
);
$schemaService = Services::get('schema');
import('lib.pkp.classes.validation.ValidatorFactory');
$validator = \ValidatorFactory::make(
$props,
$schemaService->getValidationRules(SCHEMA_CONTEXT, $allowedLocales),
[
'urlPath.regex' => __('admin.contexts.form.pathAlphaNumeric'),
'primaryLocale.regex' => __('validator.localeKey'),
'supportedFormLocales.regex' => __('validator.localeKey'),
'supportedLocales.regex' => __('validator.localeKey'),
'supportedSubmissionLocales.*.regex' => __('validator.localeKey'),
]
);
// Check required fields
\ValidatorFactory::required(
$validator,
$action,
$schemaService->getRequiredProps(SCHEMA_CONTEXT),
$schemaService->getMultilingualProps(SCHEMA_CONTEXT),
$allowedLocales,
$primaryLocale
);
// Check for input from disallowed locales
\ValidatorFactory::allowedLocales($validator, $schemaService->getMultilingualProps(SCHEMA_CONTEXT), $allowedLocales);
// Ensure that a urlPath, if provided, does not already exist
$validator->after(function($validator) use ($action, $props) {
if (isset($props['urlPath']) && !$validator->errors()->get('urlPath')) {
$contextDao = Application::getContextDAO();
$contextWithPath = $contextDao->getByPath($props['urlPath']);
if ($contextWithPath) {
if (!($action === VALIDATE_ACTION_EDIT
&& isset($props['id'])
&& (int) $contextWithPath->getId() === $props['id'])) {
$validator->errors()->add('urlPath', __('admin.contexts.form.pathExists'));
}
}
}
});
// Ensure that a urlPath is not 0, because this will cause router problems
$validator->after(function($validator) use ($props) {
if (isset($props['urlPath']) && !$validator->errors()->get('urlPath') && $props['urlPath'] == '0') {
$validator->errors()->add('urlPath', __('admin.contexts.form.pathRequired'));
}
});
// Ensure that the primary locale is one of the supported locales
$validator->after(function($validator) use ($action, $props, $allowedLocales) {
if (isset($props['primaryLocale']) && !$validator->errors()->get('primaryLocale')) {
// Check against a new supported locales prop
if (isset($props['supportedLocales'])) {
$newSupportedLocales = (array) $props['supportedLocales'];
if (!in_array($props['primaryLocale'], $newSupportedLocales)) {
$validator->errors()->add('primaryLocale', __('admin.contexts.form.primaryLocaleNotSupported'));
}
// Or check against the $allowedLocales
} elseif (!in_array($props['primaryLocale'], $allowedLocales)) {
$validator->errors()->add('primaryLocale', __('admin.contexts.form.primaryLocaleNotSupported'));
}
}
});
// Ensure that the supported locales are supported by the site
$validator->after(function($validator) use ($action, $props) {
$siteSupportedLocales = Application::get()->getRequest()->getSite()->getData('supportedLocales');
$localeProps = ['supportedLocales', 'supportedFormLocales', 'supportedSubmissionLocales'];
foreach ($localeProps as $localeProp) {
if (isset($props[$localeProp]) && !$validator->errors()->get($localeProp)) {
$unsupportedLocales = array_diff($props[$localeProp], $siteSupportedLocales);
if (!empty($unsupportedLocales)) {
$validator->errors()->add($localeProp, __('api.contexts.400.localesNotSupported', ['locales' => join(__('common.commaListSeparator'), $unsupportedLocales)]));
}
}
}
});
// If a new file has been uploaded, check that the temporary file exists and
// the current user owns it
$user = Application::get()->getRequest()->getUser();
\ValidatorFactory::temporaryFilesExist(
$validator,
['favicon', 'homepageImage', 'pageHeaderLogoImage', 'styleSheet'],
['favicon', 'homepageImage', 'pageHeaderLogoImage'],
$props,
$allowedLocales,
$user ? $user->getId() : null
);
// If sidebar blocks are passed, ensure the block plugin exists and is
// enabled
$validator->after(function($validator) use ($props) {
if (!empty($props['sidebar']) && !$validator->errors()->get('sidebar')) {
$plugins = \PluginRegistry::loadCategory('blocks', true);
foreach ($props['sidebar'] as $pluginName) {
if (empty($plugins[$pluginName])) {
$validator->errors()->add('sidebar', __('manager.setup.layout.sidebar.invalidBlock', ['name' => $pluginName]));
}
}
}
});
// Ensure the theme plugin is installed and enabled
$validator->after(function($validator) use ($props) {
if (!empty($props['themePluginPath']) && !$validator->errors()->get('themePluginPath')) {
$plugins = \PluginRegistry::loadCategory('themes', true);
$found = false;
foreach ($plugins as $plugin) {
if ($props['themePluginPath'] === $plugin->getDirName()) {
$found = true;
break;
}
}
if (!$found) {
$validator->errors()->add('themePluginPath', __('manager.setup.theme.notFound'));
}
}
});
// Only allow admins to modify which user groups are disabled for bulk emails
if (!empty($props['disableBulkEmailUserGroups'])) {
$user = Application::get()->getRequest()->getUser();
$validator->after(function($validator) use ($user) {
$roleDao = DAORegistry::getDAO('RoleDAO'); /* @var $roleDao RoleDAO */
if (!$roleDao->userHasRole(CONTEXT_ID_NONE, $user->getId(), ROLE_ID_SITE_ADMIN)) {
$validator->errors()->add('disableBulkEmailUserGroups', __('admin.settings.disableBulkEmailRoles.adminOnly'));
}
});
}
if ($validator->fails()) {
$errors = $schemaService->formatValidationErrors($validator->errors(), $schemaService->get(SCHEMA_CONTEXT), $allowedLocales);
}
\HookRegistry::call('Context::validate', array(&$errors, $action, $props, $allowedLocales, $primaryLocale));
return $errors;
}
/**
* @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::add()
*/
public function add($context, $request) {
$site = $request->getSite();
$currentUser = $request->getUser();
$contextDao = Application::getContextDAO();
if (!$context->getData('primaryLocale')) {
$context->setData('primaryLocale', $site->getPrimaryLocale());
}
if (!$context->getData('supportedLocales')) {
$context->setData('supportedLocales', $site->getSupportedLocales());
}
// Specify values needed to render default locale strings
$localeParams = array(
'indexUrl' => $request->getIndexUrl(),
'primaryLocale' => $context->getData('primaryLocale'),
'contextName' => $context->getData('name', $context->getPrimaryLocale()),
'contextPath' => $context->getData('urlPath'),
'contextUrl' => $request->getDispatcher()->url(
$request,
ROUTE_PAGE,
$context->getPath()
),
);
// Allow plugins to extend the $localeParams for new property defaults
\HookRegistry::call('Context::defaults::localeParams', array(&$localeParams, $context, $request));
$context = Services::get('schema')->setDefaults(
SCHEMA_CONTEXT,
$context,
$context->getData('supportedLocales'),
$context->getData('primaryLocale'),
$localeParams
);
if (!$context->getData('supportedFormLocales')) {
$context->setData('supportedFormLocales', [$context->getData('primaryLocale')]);
}
if (!$context->getData('supportedSubmissionLocales')) {
$context->setData('supportedSubmissionLocales', [$context->getData('primaryLocale')]);
}
$context->setSequence(REALLY_BIG_NUMBER);
$contextDao->insertObject($context);
$contextDao->resequence();
$context = $this->get($context->getId());
// Move uploaded files into place and update the settings
$supportedLocales = $context->getSupportedFormLocales();
$fileUploadProps = ['favicon', 'homepageImage', 'pageHeaderLogoImage'];
$params = [];
foreach ($fileUploadProps as $fileUploadProp) {
$value = $context->getData($fileUploadProp);
if (empty($value)) {
continue;
}
foreach ($supportedLocales as $localeKey) {
if (!array_key_exists($localeKey, $value)) {
continue;
}
$value[$localeKey] = $this->_saveFileParam($context, $value[$localeKey], $fileUploadProp, $currentUser->getId(), $localeKey, true);
}
$params[$fileUploadProp] = $value;
}
if (!empty($params['styleSheet'])) {
$params['styleSheet'] = $this->_saveFileParam($context, $params['styleSheet'], 'styleSheet', $userId);
}
$context = $this->edit($context, $params, $request);
$genreDao = \DAORegistry::getDAO('GenreDAO');
$genreDao->installDefaults($context->getId(), $context->getData('supportedLocales'));
$userGroupDao = \DAORegistry::getDAO('UserGroupDAO');
$userGroupDao->installSettings($context->getId(), 'registry/userGroups.xml');
$managerUserGroup = $userGroupDao->getDefaultByRoleId($context->getId(), ROLE_ID_MANAGER);
$userGroupDao->assignUserToGroup($currentUser->getId(), $managerUserGroup->getId());
import('lib.pkp.classes.file.FileManager');
$fileManager = new \FileManager();
foreach ($this->installFileDirs as $dir) {
$fileManager->mkdir(sprintf($dir, $this->contextsFileDirName, $context->getId()));
}
$navigationMenuDao = \DAORegistry::getDAO('NavigationMenuDAO');
$navigationMenuDao->installSettings($context->getId(), 'registry/navigationMenus.xml');
// Load all plugins so they can hook in and add their installation settings
\PluginRegistry::loadAllPlugins();
\HookRegistry::call('Context::add', array(&$context, $request));
return $context;
}
/**
* @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::edit()
*/
public function edit($context, $params, $request) {
$contextDao = Application::getContextDao();
// Move uploaded files into place and update the params
$userId = $request->getUser() ? $request->getUser()->getId() : null;
$supportedLocales = $context->getSupportedFormLocales();
$fileUploadParams = ['favicon', 'homepageImage', 'pageHeaderLogoImage'];
foreach ($fileUploadParams as $fileUploadParam) {
if (!array_key_exists($fileUploadParam, $params)) {
continue;
}
foreach ($supportedLocales as $localeKey) {
if (!array_key_exists($localeKey, $params[$fileUploadParam])) {
continue;
}
$params[$fileUploadParam][$localeKey] = $this->_saveFileParam($context, $params[$fileUploadParam][$localeKey], $fileUploadParam, $userId, $localeKey, true);
}
}
if (array_key_exists('styleSheet', $params)) {
$params['styleSheet'] = $this->_saveFileParam($context, $params['styleSheet'], 'styleSheet', $userId);
}
$newContext = $contextDao->newDataObject();
$newContext->_data = array_merge($context->_data, $params);
\HookRegistry::call('Context::edit', array(&$newContext, $context, $params, $request));
$contextDao->updateObject($newContext);
$newContext = $this->get($newContext->getId());
return $newContext;
}
/**
* @copydoc \PKP\Services\EntityProperties\EntityWriteInterface::delete()
*/
public function delete($context) {
\HookRegistry::call('Context::delete::before', array(&$context));
$contextDao = Application::getContextDao();
$contextDao->deleteObject($context);
$userGroupDao = \DAORegistry::getDAO('UserGroupDAO');
$userGroupDao->deleteAssignmentsByContextId($context->getId());
$userGroupDao->deleteByContextId($context->getId());
$genreDao = \DAORegistry::getDAO('GenreDAO');
$genreDao->deleteByContextId($context->getId());
$announcementDao = \DAORegistry::getDAO('AnnouncementDAO');
$announcementDao->deleteByAssoc($context->getAssocType(), $context->getId());
$announcementTypeDao = \DAORegistry::getDAO('AnnouncementTypeDAO');
$announcementTypeDao->deleteByAssoc($context->getAssocType(), $context->getId());
Services::get('emailTemplate')->restoreDefaults($context->getId());
$pluginSettingsDao = \DAORegistry::getDAO('PluginSettingsDAO');
$pluginSettingsDao->deleteByContextId($context->getId());
$reviewFormDao = \DAORegistry::getDAO('ReviewFormDAO');
$reviewFormDao->deleteByAssoc($context->getAssocType(), $context->getId());
$navigationMenuDao = \DAORegistry::getDAO('NavigationMenuDAO');
$navigationMenuDao->deleteByContextId($context->getId());
$navigationMenuItemDao = \DAORegistry::getDAO('NavigationMenuItemDAO');
$navigationMenuItemDao->deleteByContextId($context->getId());
import('lib.pkp.classes.file.FileManager');
$fileManager = new \FileManager($context->getId());
$contextPath = \Config::getVar('files', 'files_dir') . '/' . $this->contextsFileDirName . '/' . $context->getId();
$fileManager->rmtree($contextPath);
\HookRegistry::call('Context::delete', array(&$context));
}
/**
* Restore default values for context settings in a specific local
*
* Updates multilingual values of a context, restoring default values in a
* specific context. This may be useful when a new language has been added
* after a context has been created, or when translations change and a journal
* wants to take advantage of the new values.
*
* @param $context Context The context to restore default values for
* @param $request Request
* @param $locale string Locale key to restore defaults for. Example: `en_US`
*/
public function restoreLocaleDefaults($context, $request, $locale) {
\AppLocale::reloadLocale($locale);
\AppLocale::requireComponents(LOCALE_COMPONENT_PKP_DEFAULT, LOCALE_COMPONENT_APP_DEFAULT, $locale);
// Specify values needed to render default locale strings
$localeParams = array(
'indexUrl' => $request->getIndexUrl(),
'contextPath' => $context->getData('urlPath'),
'journalPath' => $context->getData('urlPath'), // DEPRECATED
'primaryLocale' => $context->getData('primaryLocale'),
'journalName' => $context->getData('name', $locale), // DEPRECATED
'contextName' => $context->getData('name', $locale),
'contextUrl' => $request->getDispatcher()->url(
$request,
ROUTE_PAGE,
$context->getPath()
),
);
// Allow plugins to extend the $localeParams for new property defaults
\HookRegistry::call('Context::restoreLocaleDefaults::localeParams', array(&$localeParams, $context, $request, $locale));
$localeDefaults = Services::get('schema')->getLocaleDefaults(SCHEMA_CONTEXT, $locale, $localeParams);
$params = [];
foreach ($localeDefaults as $paramName => $value) {
$params[$paramName] = array_merge(
(array) $context->getData($paramName),
[$locale => $localeDefaults[$paramName]]
);
}
return $this->edit($context, $params, $request);
}
/**
* Move a temporary file to the context's public directory
*
* @param $context Context
* @param $temporaryFile TemporaryFile
* @param $fileNameBase string Unique identifier to use for the filename. The
* Extension and locale will be appended.
* @param $userId int ID of the user who uploaded the temporary file
* @param $localeKey string Example: en_US. Leave empty for a file that is
* not localized.
* @return string|boolean The new filename or false on failure
*/
public function moveTemporaryFile($context, $temporaryFile, $fileNameBase, $userId, $localeKey = '') {
import('classes.file.PublicFileManager');
$publicFileManager = new \PublicFileManager();
import('lib.pkp.classes.file.TemporaryFileManager');
$temporaryFileManager = new \TemporaryFileManager();
$fileName = $fileNameBase;
if ($localeKey) {
$fileName .= '_' . $localeKey;
}
$extension = $publicFileManager->getDocumentExtension($temporaryFile->getFileType());
if (!$extension) {
$extension = $publicFileManager->getImageExtension($temporaryFile->getFileType());
}
$fileName .= $extension;
$result = $publicFileManager->copyContextFile(
$context->getId(),
$temporaryFile->getFilePath(),
$fileName
);
if (!$result) {
return false;
}
$temporaryFileManager->deleteById($temporaryFile->getId(), $userId);
return $fileName;
}
/**
* Handle a context setting for an uploaded file
*
* - Moves the temporary file to the public directory
* - Resets the param value to what is expected to be stored in the db
* - If a null value is passed, deletes any existing file
*
* This method is protected because all operations which edit contexts should
* go through the add and edit methods in order to ensure that
* the appropriate hooks are fired.
*
* @param $context Context The context being edited
* @param $value mixed The param value to be saved. Contains the temporary
* file ID if a new file has been uploaded.
* @param $settingName string The name of the setting to save, typically used
* in the filename.
* @param $userId integer ID of the user who owns the temporary file
* @param $localeKey string Optional. Used in the filename for multilingual
* properties.
* @param $isImage boolean Optional. For image files which return alt text,
* width, height, etc in the param value.
* @return string|array|null New param value or null on failure
*/
protected function _saveFileParam($context, $value, $settingName, $userId, $localeKey = '', $isImage = false) {
import('lib.pkp.classes.file.TemporaryFileManager');
$temporaryFileManager = new \TemporaryFileManager();
// If the value is null, clean up any existing file in the system
if (is_null($value)) {
$setting = $context->getData($settingName, $localeKey);
if ($setting) {
$fileName = $isImage ? $setting['uploadName'] : $setting;
import('classes.file.PublicFileManager');
$publicFileManager = new \PublicFileManager();
$publicFileManager->removeContextFile($context->getId(), $fileName);
}
return null;
}
// Check if there is something to upload
if (empty($value['temporaryFileId'])) {
return $value;
}
$temporaryFile = $temporaryFileManager->getFile((int) $value['temporaryFileId'], $userId);
$fileName = $this->moveTemporaryFile($context, $temporaryFile, $settingName, $userId, $localeKey);
if ($fileName) {
// Get the details for image uploads
if ($isImage) {
import('classes.file.PublicFileManager');
$publicFileManager = new \PublicFileManager();
$filePath = $publicFileManager->getContextFilesPath($context->getId());
list($width, $height) = getimagesize($filePath . '/' . $fileName);
$altText = !empty($value['altText']) ? $value['altText'] : '';
return [
'name' => $temporaryFile->getOriginalFileName(),
'uploadName' => $fileName,
'width' => $width,
'height' => $height,
'dateUploaded' => \Core::getCurrentDate(),
'altText' => $altText,
];
} else {
return [
'name' => $temporaryFile->getOriginalFileName(),
'uploadName' => $fileName,
'dateUploaded' => \Core::getCurrentDate(),
];
}
}
return false;
}
}