* element. * @endverbatim * @param $filterGroupsNode XMLNode */ function installFilterGroups($filterGroupsNode) { // Install filter groups. $filterGroupDao = DAORegistry::getDAO('FilterGroupDAO'); /* @var $filterGroupDao FilterGroupDAO */ import('lib.pkp.classes.filter.FilterGroup'); foreach ($filterGroupsNode->getChildren() as $filterGroupNode) { /* @var $filterGroupNode XMLNode */ $filterGroupSymbolic = $filterGroupNode->getAttribute('symbolic'); // Make sure that the filter group has not been // installed before to guarantee idempotence. $existingFilterGroup =& $filterGroupDao->getObjectBySymbolic($filterGroupSymbolic); if (!is_null($existingFilterGroup)) continue; // Instantiate and configure the filter group. $filterGroup = new FilterGroup(); $filterGroup->setSymbolic($filterGroupSymbolic); $filterGroup->setDisplayName($filterGroupNode->getAttribute('displayName')); $filterGroup->setDescription($filterGroupNode->getAttribute('description')); $filterGroup->setInputType($filterGroupNode->getAttribute('inputType')); $filterGroup->setOutputType($filterGroupNode->getAttribute('outputType')); // Install the filter group. $installedGroupId = $filterGroupDao->insertObject($filterGroup); assert(is_integer($installedGroupId)); unset($filterGroup); } } /** * @verbatim * Helper method that configures and optionally * installs a filter based on the given XML node * which represents a element. * @endverbatim * @param $filterNode XMLNode * @param $persist boolean whether to install the filter * @return PersistableFilter the installed filter. */ function &configureFilter($filterNode, $persist = true) { // Install filters. $filterDao = DAORegistry::getDAO('FilterDAO'); /* @var $filterDao FilterDAO */ $filterGroupSymbolic = $filterNode->getAttribute('inGroup'); $filterClassName = $filterNode->getAttribute('class'); $isTemplate = $filterNode->getAttribute('isTemplate'); // We have to include the filter class before going on // so that all required constants are defined before they // might be used in settings. if (PKPString::regexp_match('/^[a-zA-Z0-9.]+$/', $filterClassName)) { import($filterClassName); } // Go through the filter sub-nodes. This can be nested // filters or filter settings. $subNodes = $filterNode->getChildren(); $settings = array(); $subFilters = array(); foreach($subNodes as $subNode) { /* @var $subNode XMLNode */ switch($subNode->getName()) { case 'setting': // Get the filter setting. list($name, $value) = $this->getFilterSetting($subNode); $settings[$name] = $value; unset($name, $value); break; case 'filter': // Recursively configure sub-filters. $subFilter =& $this->configureFilter($subNode, false); $subFilters[] =& $subFilter; unset($subFilter); break; } } // We ensure idempotence of plug-in installation by checking // for existing identical filters. $similarFilterFactory = $filterDao->getObjectsByGroupAndClass($filterGroupSymbolic, $filterClassName, 0, $isTemplate); if ($similarFilterFactory->getCount() > 0) { // 1) Find similar filters. $similarFilters =& $similarFilterFactory->toArray(); // 2) Go through similar filters and eliminate them // if they don't have the exact same settings. foreach($similarFilters as $index => $similarFilter) { /* @var $similarFilter PersistableFilter */ if (!$this->compareFilters($similarFilter, $settings, $subFilters)) unset($similarFilters[$index]); } // There can be a maximum of exactly one identical transformation // in the database otherwise we've somehow installed a duplicate filter. $identicalFilters = count($similarFilters); assert($identicalFilters <= 1); // 3) If the filter has been installed before then return the existing filter. if ($identicalFilters) { $existingFilter = array_pop($similarFilters); return $existingFilter; } } // Configure (and optionally install) the filter. $installedFilter = $filterDao->configureObject($filterClassName, $filterGroupSymbolic, $settings, $isTemplate, 0, $subFilters, $persist); assert(is_a($installedFilter, 'PersistableFilter')); return $installedFilter; } /** * Recursively compares two filters (filter A and filter B) * based on their settings and sub-filters. * @param $filterA PersistableFilter * @param $filterBSettings array an array of key/value pairs * @param $filterBSubfilters array an array of filters * @return boolean true if the two transformations are identical, false otherwise */ function compareFilters(&$filterA, $filterBSettings, &$filterBSubfilters) { // Compare settings. foreach($filterBSettings as $name => $value) { if (!($filterA->hasSetting($name) || in_array($name, $filterA->getInternalSettings())) || $filterA->getData($name) != $value) { return false; } } // Compare sub-filters. if (is_a($filterA, 'CompositeFilter')) { // Compare sub-filters of composite filters. foreach($filterBSubfilters as $filterBSubfilter) { /* @var $filterBSubfilter PersistableFilter */ $seq = $filterBSubfilter->getSequence(); $filterASubfilter =& $filterA->getFilter($seq); if (!$filterASubfilter || !$filterBSubfilter || get_class($filterASubfilter) != get_class($filterBSubfilter)) { return false; } // Extract sub-filter settings. $filterBSubfilterSettings = array(); foreach($filterBSubfilter->getSettingNames() as $filterBSubfilterSettingName) { if ($filterBSubfilter->hasData($filterBSubfilterSettingName)) { $filterBSubfilterSettings[$filterBSubfilterSettingName] = $filterBSubfilter->getData($filterBSubfilterSettingName); } } // Extract sub-filter sub-filters. if (is_a($filterBSubfilter, 'CompositeFilter')) { $filterBSubfilterSubfilters =& $filterBSubfilter->getFilters(); } else { $filterBSubfilterSubfilters = array(); } // Recurse. if (!$this->compareFilters($filterASubfilter, $filterBSubfilterSettings, $filterBSubfilterSubfilters)) return false; } } return true; } /** * @verbatim * Helper method that extracts filter settings * from the children of a element. * @endverbatim * @param $settingNode XMLNode * @return $setting array a key-value pair. */ function getFilterSetting($settingNode) { // Retrieve the setting name. $nameNode =& $settingNode->getChildByName('name'); assert(is_a($nameNode, 'XMLNode')); $name = $nameNode->getValue(); // Retrieve the setting value. $type = $settingNode->getAttribute('type'); $valueNode =& $settingNode->getChildByName('value'); assert(is_a($valueNode, 'XMLNode')); switch($type) { case 'string': $value = (string)$valueNode->getValue(); break; case 'bool': $value = (boolean)$valueNode->getValue(); break; case 'int': $value = (integer)$valueNode->getValue(); break; case 'const': $constName = $valueNode->getValue(); assert(defined($constName)); $value = constant($constName); break; case 'object': $value = array(); $arrayNode =& $valueNode->getChildByName('array'); $value = $this->readArraySetting($arrayNode); break; default: // Unknown type. assert(false); $value = null; } // Add the setting to the list. $setting = array($name, $value); return $setting; } /** * Recursively read an array from an XML element list. * @param $arrayNode XMLNode * @return array */ function readArraySetting($arrayNode) { $resultArray = array(); foreach($arrayNode->getChildren() as $elementNode) { /* @var $elementNode XMLNode */ $key = $elementNode->getAttribute('key'); $subArrayNode = $elementNode->getChildByName('array'); if (is_a($subArrayNode, 'XMLNode')) { // Recurse into sub-array. $value = $this->readArraySetting($subArrayNode); } else { // Read the element value. $value = $elementNode->getValue(); } // Add the element to the result set. if (isset($key)) { $resultArray[$key] = $value; } else { $resultArray[] = $value; } } return $resultArray; } } ?>