From 84bbf37daa95d287dec5451ec7e9b2670afa52cc Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Thu, 18 Jun 2015 11:54:02 -0400 Subject: [PATCH] Added formatting classes for hierarchy output. --- module/VuFind/config/module.config.php | 7 + .../TreeDataFormatter/AbstractBase.php | 223 ++++++++++++++++++ .../Hierarchy/TreeDataFormatter/Json.php | 107 +++++++++ .../TreeDataFormatter/PluginManager.php | 51 ++++ .../Hierarchy/TreeDataFormatter/Xml.php | 106 +++++++++ module/VuFind/src/VuFind/Service/Factory.php | 13 + 6 files changed, 507 insertions(+) create mode 100644 module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php create mode 100644 module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php create mode 100644 module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php create mode 100644 module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index 7ac922c378c..113ab49fb91 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -147,6 +147,7 @@ $config = [ 'VuFind\DbTablePluginManager' => 'VuFind\Service\Factory::getDbTablePluginManager', 'VuFind\Export' => 'VuFind\Service\Factory::getExport', 'VuFind\HierarchyDriverPluginManager' => 'VuFind\Service\Factory::getHierarchyDriverPluginManager', + 'VuFind\HierarchyTreeDataFormatterPluginManager' => 'VuFind\Service\Factory::getHierarchyTreeDataFormatterPluginManager', 'VuFind\HierarchyTreeDataSourcePluginManager' => 'VuFind\Service\Factory::getHierarchyTreeDataSourcePluginManager', 'VuFind\HierarchyTreeRendererPluginManager' => 'VuFind\Service\Factory::getHierarchyTreeRendererPluginManager', 'VuFind\Http' => 'VuFind\Service\Factory::getHttp', @@ -350,6 +351,12 @@ $config = [ 'flat' => 'VuFind\Hierarchy\Driver\Factory::getHierarchyFlat', ], ], + 'hierarchy_treedataformatter' => [ + 'invokables' => [ + 'json' => 'VuFind\Hierarchy\TreeDataFormatter\Json', + 'xml' => 'VuFind\Hierarchy\TreeDataFormatter\Xml', + ], + ], 'hierarchy_treedatasource' => [ 'factories' => [ 'solr' => 'VuFind\Hierarchy\TreeDataSource\Factory::getSolr', diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php new file mode 100644 index 00000000000..29702ee8470 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/AbstractBase.php @@ -0,0 +1,223 @@ +<?php +/** + * Hierarchy Tree Data Formatter (abstract base) + * + * PHP version 5 + * + * Copyright (C) Villanova University 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Hierarchy\TreeDataFormatter; + +/** + * Hierarchy Tree Data Formatter (abstract base) + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +abstract class AbstractBase +{ + /** + * Top-level record from index + * + * @var object + */ + protected $topNode; + + /** + * Child data map from index + * + * @var array + */ + protected $childMap; + + /** + * Is sorting enabled? + * + * @var bool + */ + protected $sort; + + /** + * Collection mode + * + * @var string + */ + protected $collectionType; + + /** + * How many nodes have we formatted? + * + * @var int; + */ + protected $count = 0; + + /** + * Set raw data. + * + * @param object $topNode Full record for top node + * @param array $childMap Data map from index + * @param bool $sort Is sorting enabled? + * @param string $cType Collection type + * + * @return void + */ + public function setRawData($topNode, $childMap, $sort = false, $cType = 'All') + { + $this->topNode = $topNode; + $this->childMap = $childMap; + $this->sort = $sort; + $this->collectionType = $cType; + } + + /** + * Get number of nodes formatted. + * + * @return int; + */ + public function getCount() + { + return $this->count; + } + + /** + * Get the formatted metadata. + * + * @return string + */ + abstract public function getData(); + + /** + * Get the positions of this item within parent collections. Returns an array + * of parent ID => sequence number. + * + * @param object $fields Solr fields + * + * @return array + */ + protected function getHierarchyPositionsInParents($fields) + { + $retVal = []; + if (isset($fields->hierarchy_parent_id) + && isset($fields->hierarchy_sequence) + ) { + foreach ($fields->hierarchy_parent_id as $key => $val) { + $retVal[$val] = $fields->hierarchy_sequence[$key]; + } + } + return $retVal; + } + + /** + * Get the titles of this item within parent collections. Returns an array + * of parent ID => sequence number. + * + * @param object $fields Solr fields + * + * @return Array + */ + protected function getTitlesInHierarchy($fields) + { + $retVal = []; + if (isset($fields->title_in_hierarchy) + && is_array($fields->title_in_hierarchy) + ) { + $titles = $fields->title_in_hierarchy; + $parentIDs = $fields->hierarchy_parent_id; + if (count($titles) === count($parentIDs)) { + foreach ($parentIDs as $key => $val) { + $retVal[$val] = $titles[$key]; + } + } + } + return $retVal; + } + + /** + * Identify whether the provided record is a collection. + * + * NOTE: \VuFind\RecordDriver\SolrDefault::isCollection() duplicates some of\ + * this logic. + * + * @param object $fields Solr fields + * + * @return bool + */ + protected function isCollection($fields) + { + // Check config setting for what constitutes a collection + switch ($this->collectionType) { + case 'All': + return (isset($fields->is_hierarchy_id)); + case 'Top': + return isset($fields->is_hierarchy_id) + && in_array($fields->is_hierarchy_id, $fields->hierarchy_top_id); + default: + // Default to not be a collection level record + return false; + } + } + + /** + * Choose a title for the record. + * + * @param object $record Solr record to format + * @param string $parentID The starting point for the current recursion + * (equivalent to Solr field hierarchy_parent_id) + * + * @return string + */ + protected function pickTitle($record, $parentID) + { + $titles = $this->getTitlesInHierarchy($record); + // TODO: handle missing titles more gracefully (title not available?) + $title = isset($record->title) ? $record->title : $record->id; + return null != $parentID && isset($titles[$parentID]) + ? $titles[$parentID] : $title; + } + + /** + * Sort Nodes + * Convert an unsorted array of [ key, value ] pairs into a sorted array + * of values. + * + * @param array $array The array of arrays to sort + * + * @return array + */ + protected function sortNodes($array) + { + // Sort arrays based on first element + $sorter = function ($a, $b) { + return strcmp($a[0], $b[0]); + }; + usort($array, $sorter); + + // Collapse array to remove sort values + $mapper = function ($i) { + return $i[1]; + }; + return array_map($mapper, $array); + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php new file mode 100644 index 00000000000..d0fb8d03c70 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Json.php @@ -0,0 +1,107 @@ +<?php +/** + * Hierarchy Tree Data Formatter (JSON) + * + * PHP version 5 + * + * Copyright (C) Villanova University 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Hierarchy\TreeDataFormatter; + +/** + * Hierarchy Tree Data Formatter (JSON) + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class Json extends AbstractBase +{ + /** + * Get the formatted metadata. + * + * @return string + */ + public function getData() + { + return json_encode($this->formatNode($this->topNode)); + } + + /** + * Get Solr Children for JSON + * + * @param object $record Solr record to format + * @param string $parentID The starting point for the current recursion + * (equivalent to Solr field hierarchy_parent_id) + * + * @return string + */ + protected function formatNode($record, $parentID = null) + { + $raw = [ + 'id' => $record->id, + 'type' => $this->isCollection($record) ? 'collection' : 'record', + 'title' => $this->pickTitle($record, $parentID) + ]; + + if (isset($this->childMap[$record->id])) { + $children = $this->mapChildren($record->id); + if (!empty($children)) { + $raw['children'] = $children; + } + } + + return (object)$raw; + } + + /** + * Get Solr Children for JSON + * + * @param string $parentID The starting point for the current recursion + * (equivalent to Solr field hierarchy_parent_id) + * + * @return string + */ + protected function mapChildren($parentID) + { + $json = []; + foreach ($this->childMap[$parentID] as $current) { + ++$this->count; + + $childNode = $this->formatNode($current, $parentID); + + // If we're in sorting mode, we need to create key-value arrays; + // otherwise, we can just collect flat values. + if ($this->sort) { + $positions = $this->getHierarchyPositionsInParents($current); + $sequence = isset($positions[$parentID]) ? $positions[$parentID] : 0; + $json[] = [$sequence, $childNode]; + } else { + $json[] = $childNode; + } + } + + return $this->sort ? $this->sortNodes($json) : $json; + } +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php new file mode 100644 index 00000000000..143b893d683 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php @@ -0,0 +1,51 @@ +<?php +/** + * Hierarchy tree data formatter plugin manager + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package HierarchyTree_DataSource + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Hierarchy\TreeDataFormatter; + +/** + * Hierarchy tree data formatter plugin manager + * + * @category VuFind2 + * @package HierarchyTree_DataSource + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return 'VuFind\Hierarchy\TreeDataFormatter\AbstractBase'; + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php new file mode 100644 index 00000000000..65a068b1798 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/Xml.php @@ -0,0 +1,106 @@ +<?php +/** + * Hierarchy Tree Data Formatter (XML) + * + * PHP version 5 + * + * Copyright (C) Villanova University 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace VuFind\Hierarchy\TreeDataFormatter; + +/** + * Hierarchy Tree Data Formatter (XML) + * + * @category VuFind2 + * @package HierarchyTree_DataFormatter + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class Xml extends AbstractBase +{ + /** + * Get the formatted metadata. + * + * @return string + */ + public function getData() + { + return '<root>' + . $this->formatNode($this->topNode) + . '</root>'; + } + + /** + * Get Solr Children for JSON + * + * @param object $record Solr record to format + * @param string $parentID The starting point for the current recursion + * (equivalent to Solr field hierarchy_parent_id) + * + * @return string + */ + protected function formatNode($record, $parentID = null) + { + $isCollection = $this->isCollection($record) ? "true" : "false"; + return '<item id="' . htmlspecialchars($record->id) + . '" isCollection="' . $isCollection . '">' + . '<content><name>' + . htmlspecialchars($this->pickTitle($record, $parentID)) + . '</name></content>' + . $this->mapChildren($record->id) + . '</item>'; + } + + /** + * Get Solr Children for JSON + * + * @param string $parentID The starting point for the current recursion + * (equivalent to Solr field hierarchy_parent_id) + * + * @return string + */ + protected function mapChildren($parentID) + { + if (!isset($this->childMap[$parentID])) { + return ''; + } + $parts = []; + foreach ($this->childMap[$parentID] as $current) { + ++$this->count; + + $childNode = $this->formatNode($current, $parentID); + + // If we're in sorting mode, we need to create key-value arrays; + // otherwise, we can just collect flat values. + if ($this->sort) { + $positions = $this->getHierarchyPositionsInParents($current); + $sequence = isset($positions[$parentID]) ? $positions[$parentID] : 0; + $parts[] = [$sequence, $childNode]; + } else { + $parts[] = $childNode; + } + } + + return implode('', $this->sort ? $this->sortNodes($parts) : $parts); + } +} diff --git a/module/VuFind/src/VuFind/Service/Factory.php b/module/VuFind/src/VuFind/Service/Factory.php index d7fdc978897..dd279d35644 100644 --- a/module/VuFind/src/VuFind/Service/Factory.php +++ b/module/VuFind/src/VuFind/Service/Factory.php @@ -299,6 +299,19 @@ class Factory return static::getGenericPluginManager($sm, 'Hierarchy\Driver'); } + /** + * Construct the Hierarchy\TreeDataFormatter Plugin Manager. + * + * @param ServiceManager $sm Service manager. + * + * @return \VuFind\Hierarchy\TreeDataFormatter\PluginManager + */ + public static function getHierarchyTreeDataFormatterPluginManager( + ServiceManager $sm + ) { + return static::getGenericPluginManager($sm, 'Hierarchy\TreeDataFormatter'); + } + /** * Construct the Hierarchy\TreeDataSource Plugin Manager. * -- GitLab