From 4fd34f71c7cad244be734c651188ac7748db3589 Mon Sep 17 00:00:00 2001 From: Alec Smecher Date: Tue, 7 Jan 2025 16:51:57 -0800 Subject: [PATCH] pkp/pkp-lib#10671 Fix filters for user import/export plugin and classloading modernization (#10780) --- classes/filter/ClassTypeDescription.php | 10 +-- classes/filter/Filter.php | 15 ++--- classes/filter/FilterGroupDAO.php | 4 +- classes/filter/PrimitiveTypeDescription.php | 5 +- classes/filter/TypeDescription.php | 12 +--- classes/filter/TypeDescriptionFactory.php | 34 +++++------ classes/metadata/MetadataTypeDescription.php | 5 +- .../upgrade/v3_5_0/FilterClassNames.php | 61 +++++++++++++++++++ .../validation/ValidatorTypeDescription.php | 4 +- classes/xslt/XMLTypeDescription.php | 4 +- .../native/filter/NativeExportFilter.php | 18 +++--- .../users/filter/PKPUserUserXmlFilter.php | 8 +-- .../users/filter/UserGroupNativeXmlFilter.php | 5 +- .../users/filter/UserXmlPKPUserFilter.php | 4 +- .../users/filter/filterConfig.xml | 8 +-- 15 files changed, 127 insertions(+), 70 deletions(-) create mode 100644 classes/migration/upgrade/v3_5_0/FilterClassNames.php diff --git a/classes/filter/ClassTypeDescription.php b/classes/filter/ClassTypeDescription.php index de81feb76e4..9988f4aab9d 100644 --- a/classes/filter/ClassTypeDescription.php +++ b/classes/filter/ClassTypeDescription.php @@ -1,4 +1,5 @@ splitClassName($typeName); if ($splitName === false) { @@ -61,7 +62,7 @@ public function parseTypeName($typeName) /** * @see TypeDescription::checkType() */ - public function checkType($object) + public function checkType($object): bool { // We expect an object if (!is_object($object)) { @@ -93,6 +94,7 @@ public function checkType($object) public function splitClassName($typeName) { // This should be a class - identify package and class name + // This behaviour is DEPRECATED with pkp/pkp-lib#8186 $typeNameParts = explode('.', $typeName); $className = array_pop($typeNameParts); diff --git a/classes/filter/Filter.php b/classes/filter/Filter.php index 4d6676ae38d..eb8cff2f493 100644 --- a/classes/filter/Filter.php +++ b/classes/filter/Filter.php @@ -1,4 +1,5 @@ _inputType; } @@ -195,7 +196,7 @@ public function &getInputType(): TypeDescription /** * Get the output type */ - public function &getOutputType(): TypeDescription + public function getOutputType(): TypeDescription { return $this->_outputType; } @@ -278,9 +279,9 @@ public function clearErrors(): void * Set the required runtime environment * */ - public function setRuntimeEnvironment(RuntimeEnvironment &$runtimeEnvironment): void + public function setRuntimeEnvironment(RuntimeEnvironment $runtimeEnvironment): void { - $this->_runtimeEnvironment = &$runtimeEnvironment; + $this->_runtimeEnvironment = $runtimeEnvironment; // Inject the runtime settings into the data object // for persistence. @@ -294,7 +295,7 @@ public function setRuntimeEnvironment(RuntimeEnvironment &$runtimeEnvironment): /** * Get the required runtime environment */ - public function &getRuntimeEnvironment(): RuntimeEnvironment + public function getRuntimeEnvironment(): RuntimeEnvironment { return $this->_runtimeEnvironment; } @@ -339,7 +340,7 @@ abstract public function &process(&$input); public function supports(&$input, &$output): bool { // Validate input - $inputType = &$this->getInputType(); + $inputType = $this->getInputType(); $validInput = $inputType->isCompatible($input); // If output is null then we're done @@ -348,7 +349,7 @@ public function supports(&$input, &$output): bool } // Validate output - $outputType = &$this->getOutputType(); + $outputType = $this->getOutputType(); $validOutput = $outputType->isCompatible($output); return $validInput && $validOutput; diff --git a/classes/filter/FilterGroupDAO.php b/classes/filter/FilterGroupDAO.php index 894b94f0cb1..f2b26bf91e0 100644 --- a/classes/filter/FilterGroupDAO.php +++ b/classes/filter/FilterGroupDAO.php @@ -29,7 +29,7 @@ class FilterGroupDAO extends \PKP\db\DAO * * @return int the new filter group id */ - public function insertObject(FilterGroup &$filterGroup): int + public function insertObject(FilterGroup $filterGroup): int { $this->update( sprintf('INSERT INTO filter_groups @@ -87,7 +87,7 @@ public function getObjectBySymbolic(string $filterGroupSymbolic): ?FilterGroup /** * Update an existing filter group. */ - public function updateObject(FilterGroup $filterGroup) + public function updateObject(FilterGroup $filterGroup): void { $this->update( 'UPDATE filter_groups diff --git a/classes/filter/PrimitiveTypeDescription.php b/classes/filter/PrimitiveTypeDescription.php index 5b6ae59effb..706baed21bd 100644 --- a/classes/filter/PrimitiveTypeDescription.php +++ b/classes/filter/PrimitiveTypeDescription.php @@ -1,4 +1,5 @@ _supportedPrimitiveTypes())) { @@ -53,7 +54,7 @@ public function parseTypeName($typeName) /** * @see TypeDescription::checkType() */ - public function checkType($object) + public function checkType($object): bool { // We expect a primitive type if (!is_scalar($object)) { diff --git a/classes/filter/TypeDescription.php b/classes/filter/TypeDescription.php index 3147c4e4ee8..e9784fa97c5 100644 --- a/classes/filter/TypeDescription.php +++ b/classes/filter/TypeDescription.php @@ -1,4 +1,5 @@ _namespaceMap($typeDescriptionParts[0]); if (is_null($typeDescriptionClass)) { - return $nullVar; + return null; } // Instantiate and return the type description object - $typeDescriptionObject = & instantiate($typeDescriptionClass, 'TypeDescription', null, null, $typeDescriptionParts[1]); - if (!is_object($typeDescriptionObject)) { - return $nullVar; + if (strpos($typeDescriptionClass, '.')) { + // DEPRECATED: Use old instantiate function + return instantiate($typeDescriptionClass, 'TypeDescription', null, null, $typeDescriptionParts[1]); + } else { + // New behaviour: Use fully qualified class name + return new $typeDescriptionClass(); } - - return $typeDescriptionObject; } @@ -114,21 +114,17 @@ public function &instantiateTypeDescription($typeDescription) * class name. * * FIXME: Move this map to the Application object. - * - * @param string $namespace - * - * @return string */ - public function _namespaceMap($namespace) + public function _namespaceMap(string $namespace): ?string { - static $namespaceMap = [ + return match($namespace) { self::TYPE_DESCRIPTION_NAMESPACE_PRIMITIVE => 'lib.pkp.classes.filter.PrimitiveTypeDescription', self::TYPE_DESCRIPTION_NAMESPACE_CLASS => 'lib.pkp.classes.filter.ClassTypeDescription', self::TYPE_DESCRIPTION_NAMESPACE_METADATA => 'lib.pkp.classes.metadata.MetadataTypeDescription', self::TYPE_DESCRIPTION_NAMESPACE_XML => 'lib.pkp.classes.xslt.XMLTypeDescription', - self::TYPE_DESCRIPTION_NAMESPACE_VALIDATOR => 'lib.pkp.classes.validation.ValidatorTypeDescription' - ]; - return $namespaceMap[$namespace] ?? null; + self::TYPE_DESCRIPTION_NAMESPACE_VALIDATOR => 'lib.pkp.classes.validation.ValidatorTypeDescription', + default => null + }; } } diff --git a/classes/metadata/MetadataTypeDescription.php b/classes/metadata/MetadataTypeDescription.php index c2f22fe7345..32cdafea694 100644 --- a/classes/metadata/MetadataTypeDescription.php +++ b/classes/metadata/MetadataTypeDescription.php @@ -1,4 +1,5 @@ where('input_type', 'class::lib.pkp.classes.security.UserGroup[]') + ->update(['input_type' => 'class::PKP\\userGroup\\UserGroup[]']); + DB::table('filter_groups') + ->where('output_type', 'class::lib.pkp.classes.security.UserGroup[]') + ->update(['output_type' => 'class::PKP\\userGroup\\UserGroup[]']); + + // User class didn't move, but use namespacing + DB::table('filter_groups') + ->where('input_type', 'class::lib.pkp.classes.user.User[]') + ->update(['input_type' => 'class::PKP\\user\\User[]']); + DB::table('filter_groups') + ->where('output_type', 'class::classes.users.User[]') + ->update(['output_type' => 'class::PKP\\user\\User[]']); + } + + public function down(): void + { + // UserGroup + DB::table('filter_groups') + ->where('input_type', 'class::PKP\\userGroup\\UserGroup[]') + ->update(['input_type' => 'class::lib.pkp.classes.security.UserGroup[]']); + DB::table('filter_groups') + ->where('output_type', 'class::PKP\\userGroup\\UserGroup[]') + ->update(['output_type' => 'class::lib.pkp.classes.security.UserGroup[]']); + + // User + DB::table('filter_groups') + ->where('input_type', 'class::PKP\\user\\User[]') + ->update(['input_type' => 'class::lib.pkp.classes.user.User[]']); + DB::table('filter_groups') + ->where('output_type', 'class::PKP\\user\\User[]') + ->update(['output_type' => 'class::classes.users.User[]']); + } +} diff --git a/classes/validation/ValidatorTypeDescription.php b/classes/validation/ValidatorTypeDescription.php index 3e208d8970b..0c7cc3c032a 100644 --- a/classes/validation/ValidatorTypeDescription.php +++ b/classes/validation/ValidatorTypeDescription.php @@ -47,7 +47,7 @@ public function getNamespace() /** * @see TypeDescription::parseTypeName() */ - public function parseTypeName($typeName) + public function parseTypeName(string $typeName): bool { // Standard validators are based on string input. parent::parseTypeName('string'); @@ -85,7 +85,7 @@ public function parseTypeName($typeName) /** * @see TypeDescription::checkType() */ - public function checkType($object) + public function checkType($object): bool { // Check primitive type. if (!parent::checkType($object)) { diff --git a/classes/xslt/XMLTypeDescription.php b/classes/xslt/XMLTypeDescription.php index 1ef6330ace0..5fe48b46da8 100644 --- a/classes/xslt/XMLTypeDescription.php +++ b/classes/xslt/XMLTypeDescription.php @@ -69,7 +69,7 @@ public function setValidationStrategy($validationStrategy) /** * @copydoc TypeDescription::parseTypeName() */ - public function parseTypeName($typeName) + public function parseTypeName(string $typeName): bool { // We expect a validation strategy and an optional validation argument $typeNameParts = explode('(', $typeName); @@ -107,7 +107,7 @@ public function parseTypeName($typeName) /** * @copydoc TypeDescription::checkType() */ - public function checkType($object) + public function checkType($object): bool { // We only accept DOMDocument objects and source strings. if (!$object instanceof DOMDocument && !is_string($object)) { diff --git a/plugins/importexport/native/filter/NativeExportFilter.php b/plugins/importexport/native/filter/NativeExportFilter.php index 7b922bafb97..2ed216c717e 100644 --- a/plugins/importexport/native/filter/NativeExportFilter.php +++ b/plugins/importexport/native/filter/NativeExportFilter.php @@ -50,23 +50,27 @@ public function getNoValidation(): ?bool public function supports(&$input, &$output): bool { // Validate input - $inputType = & $this->getInputType(); - $validInput = $inputType->isCompatible($input); + $inputType = $this->getInputType(); + if (!$inputType || !$inputType->isCompatible($input)) { + return false; + } // If output is null then we're done if (is_null($output)) { - return $validInput; + return true; } // Validate output - $outputType = & $this->getOutputType(); + $outputType = $this->getOutputType(); + if (!$outputType) { + return false; + } - if ($outputType instanceof XMLTypeDescription && $this->getNoValidation()) { + if ($this->getNoValidation()) { $outputType->setValidationStrategy(XMLTypeDescription::XML_TYPE_DESCRIPTION_VALIDATE_NONE); } - $validOutput = $outputType->isCompatible($output); - return $validInput && $validOutput; + return $outputType->isCompatible($output); } // diff --git a/plugins/importexport/users/filter/PKPUserUserXmlFilter.php b/plugins/importexport/users/filter/PKPUserUserXmlFilter.php index 2445df1c330..6ed3c357152 100644 --- a/plugins/importexport/users/filter/PKPUserUserXmlFilter.php +++ b/plugins/importexport/users/filter/PKPUserUserXmlFilter.php @@ -136,7 +136,7 @@ public function createPKPUserNode($doc, $user) ->get(); foreach ($userGroups as $userGroup) { - $userNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'user_group_ref', htmlspecialchars($userGroup->getName($context->getPrimaryLocale()), ENT_COMPAT, 'UTF-8'))); + $userNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'user_group_ref', htmlspecialchars($userGroup->name[$context->getPrimaryLocale()], ENT_COMPAT, 'UTF-8'))); } // Add Reviewing Interests, if any. @@ -152,8 +152,7 @@ public function addUserGroups($doc, $rootNode) $context = $deployment->getContext(); $userGroupsNode = $doc->createElementNS($deployment->getNamespace(), 'user_groups'); - $userGroups = UserGroup::withContextIds([$context->getId()]) - ->get(); + $userGroups = UserGroup::withContextIds([$context->getId()])->get(); $filterDao = DAORegistry::getDAO('FilterDAO'); /** @var FilterDAO $filterDao */ $userGroupExportFilters = $filterDao->getObjectsByGroup('usergroup=>user-xml'); @@ -161,8 +160,7 @@ public function addUserGroups($doc, $rootNode) $exportFilter = array_shift($userGroupExportFilters); $exportFilter->setDeployment($this->getDeployment()); - $userGroupsArray = $userGroups->toArray(); - $userGroupsDoc = $exportFilter->execute($userGroupsArray); + $userGroupsDoc = $exportFilter->execute($userGroups->all()); if ($userGroupsDoc->documentElement instanceof \DOMElement) { $clone = $doc->importNode($userGroupsDoc->documentElement, true); $rootNode->appendChild($clone); diff --git a/plugins/importexport/users/filter/UserGroupNativeXmlFilter.php b/plugins/importexport/users/filter/UserGroupNativeXmlFilter.php index ec35792d241..73858170a65 100644 --- a/plugins/importexport/users/filter/UserGroupNativeXmlFilter.php +++ b/plugins/importexport/users/filter/UserGroupNativeXmlFilter.php @@ -16,7 +16,6 @@ namespace PKP\plugins\importexport\users\filter; -use APP\facades\Repo; use DOMDocument; use PKP\filter\FilterGroup; use PKP\userGroup\UserGroup; @@ -86,8 +85,8 @@ public function createUserGroupNode($doc, $userGroup) $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'context_id', $userGroup->contextId)); $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'is_default', $userGroup->isDefault ? 'true' : 'false')); $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'show_title', $userGroup->showTitle ? 'true' : 'false')); - $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'permit_selfRegistration', $userGroup->permitSelfRegistration ? 'true' : 'false')); - $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'permit_metadataEdit', $userGroup->permitMetadataEdit ? 'true' : 'false')); + $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'permit_self_registration', $userGroup->permitSelfRegistration ? 'true' : 'false')); + $userGroupNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'permit_metadata_edit', $userGroup->permitMetadataEdit ? 'true' : 'false')); $this->createLocalizedNodes($doc, $userGroupNode, 'name', $userGroup->name); $this->createLocalizedNodes($doc, $userGroupNode, 'abbrev', $userGroup->abbrev); diff --git a/plugins/importexport/users/filter/UserXmlPKPUserFilter.php b/plugins/importexport/users/filter/UserXmlPKPUserFilter.php index f7092a47312..df451d2d46a 100644 --- a/plugins/importexport/users/filter/UserXmlPKPUserFilter.php +++ b/plugins/importexport/users/filter/UserXmlPKPUserFilter.php @@ -306,9 +306,9 @@ public function parseUser($node) // if the given user associated group name in within tag 'user_group_ref' is in the list of $userGroup name local list // and the user is not already assigned to that group if (in_array($n->textContent, $userGroup->name) && - !UserGroup::userInGroup($userId, $userGroup->id)) { + !Repo::userGroup()->userInGroup($userId, $userGroup->id)) { // Found a candidate; assign user to it. - UserGroup::assignUserToGroup($userId, $userGroup->id); + Repo::userGroup()->assignUserToGroup($userId, $userGroup->id); } } } diff --git a/plugins/importexport/users/filter/filterConfig.xml b/plugins/importexport/users/filter/filterConfig.xml index 79c218a75c1..57e1281a987 100644 --- a/plugins/importexport/users/filter/filterConfig.xml +++ b/plugins/importexport/users/filter/filterConfig.xml @@ -16,7 +16,7 @@ symbolic="user=>user-xml" displayName="plugins.importexport.users.displayName" description="plugins.importexport.users.description" - inputType="class::lib.pkp.classes.user.User[]" + inputType="class::PKP\user\User[]" outputType="xml::schema(lib/pkp/plugins/importexport/users/pkp-users.xsd)" /> + outputType="class::PKP\user\User[]" /> + outputType="class::PKP\userGroup\UserGroup[]" />