From 64792c1e72030874d29ca932a256b9a914eb8202 Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Tue, 20 Dec 2016 12:56:27 -0500 Subject: [PATCH] Add RecordDataFormatter helper to simplify description template. (#829) - Replaces extremely redundant template logic with a view helper driven by a simple, declarative configuration. - Injects default configurations into the view helper, so that changes (adding/removing/reordering fields) can be made by customizing a factory OR by customizing templates, depending on user preferences. - Includes a class for managing configurations, to simplify common setup tasks. - Keeps all data retrieval logic in the record driver, but allows extremely granular display customization on a field-by-field and driver-by-driver basis using driver-specific templates. --- .../src/VuFind/View/Helper/Root/Record.php | 2 +- .../View/Helper/Root/RecordDataFormatter.php | 275 ++++++++++++++++++ .../Root/RecordDataFormatter/SpecBuilder.php | 136 +++++++++ .../Root/RecordDataFormatterFactory.php | 161 ++++++++++ .../RecordDataFormatter/SpecBuilderTest.php | 86 ++++++ .../Helper/Root/RecordDataFormatterTest.php | 192 ++++++++++++ .../RecordDriver/SolrDefault/core.phtml | 245 +--------------- .../SolrDefault/data-allRecordLinks.phtml | 11 + .../SolrDefault/data-allSubjectHeadings.phtml | 11 + .../SolrDefault/data-authorNotes.phtml | 8 + .../SolrDefault/data-authors.phtml | 15 + .../SolrDefault/data-childRecords.phtml | 1 + .../SolrDefault/data-containerTitle.phtml | 10 + .../SolrDefault/data-onlineAccess.phtml | 14 + .../SolrDefault/data-publicationDetails.phtml | 14 + .../SolrDefault/data-series.phtml | 16 + .../RecordDriver/SolrDefault/data-tags.phtml | 15 + .../templates/RecordTab/description.phtml | 236 +-------------- themes/root/theme.config.php | 1 + 19 files changed, 985 insertions(+), 464 deletions(-) create mode 100644 module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php create mode 100644 module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php create mode 100644 module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php create mode 100644 module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php create mode 100644 module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml create mode 100644 themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Record.php b/module/VuFind/src/VuFind/View/Helper/Root/Record.php index 7dd53c668dd..c9bf3514ca1 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Record.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Record.php @@ -100,7 +100,7 @@ class Record extends AbstractHelper * * @return string */ - protected function renderTemplate($name, $context = null) + public function renderTemplate($name, $context = null) { // Set default context if none provided: if (is_null($context)) { diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php new file mode 100644 index 00000000000..6e627ca7fe2 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter.php @@ -0,0 +1,275 @@ +<?php +/** + * Record driver data formatting view helper + * + * PHP version 5 + * + * Copyright (C) Villanova University 2016. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:architecture:record_data_formatter + * Wiki + */ +namespace VuFind\View\Helper\Root; +use VuFind\RecordDriver\AbstractBase as RecordDriver; +use Zend\View\Helper\AbstractHelper; + +/** + * Record driver data formatting view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:architecture:record_data_formatter + * Wiki + */ +class RecordDataFormatter extends AbstractHelper +{ + /** + * Default settings. + * + * @var array + */ + protected $defaults = []; + + /** + * Sort callback for field specification. + * + * @param array $a First value to compare + * @param array $b Second value to compare + * + * @return int + */ + public function specSortCallback($a, $b) + { + $posA = isset($a['pos']) ? $a['pos'] : 0; + $posB = isset($b['pos']) ? $b['pos'] : 0; + if ($posA === $posB) { + return 0; + } + return $posA < $posB ? -1 : 1; + } + + /** + * Create formatted key/value data based on a record driver and field spec. + * + * @param RecordDriver $driver Record driver object. + * @param array $spec Formatting specification + * + * @return Record + */ + public function getData(RecordDriver $driver, array $spec) + { + $result = []; + + // Sort the spec into order by position: + uasort($spec, [$this, 'specSortCallback']); + + // Apply the spec: + foreach ($spec as $field => $current) { + // Extract the relevant data from the driver. + $data = $this->extractData($driver, $current); + $allowZero = isset($current['allowZero']) ? $current['allowZero'] : true; + if (!empty($data) || ($allowZero && ($data === 0 || $data === '0'))) { + // Determine the rendering method to use with the second element + // of the current spec. + $renderMethod = empty($current['renderType']) + ? 'renderSimple' : 'render' . $current['renderType']; + + // Add the rendered data to the return value if it is non-empty: + if (is_callable([$this, $renderMethod])) { + $text = $this->$renderMethod($driver, $data, $current); + if (!$text && (!$allowZero || ($text !== 0 && $text !== '0'))) { + continue; + } + // Allow dynamic label override: + if (isset($current['labelFunction']) + && is_callable($current['labelFunction']) + ) { + $field = call_user_func($current['labelFunction'], $data); + } + $result[$field] = $text; + } + } + } + return $result; + } + + /** + * Get default configuration. + * + * @param string $key Key for configuration to look up. + * + * @return array + */ + public function getDefaults($key) + { + return isset($this->defaults[$key]) ? $this->defaults[$key] : []; + } + + /** + * Set default configuration. + * + * @param string $key Key for configuration to set. + * @param array $values Defaults to store. + * + * @return void + */ + public function setDefaults($key, array $values) + { + $this->defaults[$key] = $values; + } + + /** + * Extract data (usually from the record driver). + * + * @param RecordDriver $driver Record driver + * @param array $options Incoming options + * + * @return mixed + */ + protected function extractData(RecordDriver $driver, array $options) + { + // Static cache for persisting data. + static $cache = []; + + // If $method is a bool, return it as-is; this allows us to force the + // rendering (or non-rendering) of particular data independent of the + // record driver. + $method = isset($options['dataMethod']) ? $options['dataMethod'] : false; + if ($method === true || $method === false) { + return $method; + } + + $useCache = isset($options['cacheData']) && $options['cacheData']; + + if ($useCache) { + $cacheKey = $driver->getUniqueID() . '|' + . $driver->getSourceIdentifier() . '|' . $method; + if (isset($cache[$cacheKey])) { + return $cache[$cacheKey]; + } + } + + // Default action: try to extract data from the record driver: + $data = $driver->tryMethod($method); + + if ($useCache) { + $cache[$cacheKey] = $data; + } + + return $data; + } + + /** + * Render using the record view helper. + * + * @param RecordDriver $driver Reoord driver object. + * @param mixed $data Data to render + * @param array $options Rendering options. + * + * @return string + */ + protected function renderRecordHelper(RecordDriver $driver, $data, + array $options + ) { + $method = isset($options['helperMethod']) ? $options['helperMethod'] : null; + $plugin = $this->getView()->plugin('record'); + if (empty($method) || !is_callable([$plugin, $method])) { + throw new \Exception('Cannot call "' . $method . '" on helper.'); + } + return $plugin($driver)->$method($data); + } + + /** + * Render a record driver template. + * + * @param RecordDriver $driver Reoord driver object. + * @param mixed $data Data to render + * @param array $options Rendering options. + * + * @return string + */ + protected function renderRecordDriverTemplate(RecordDriver $driver, $data, + array $options + ) { + if (!isset($options['template'])) { + throw new \Exception('Template option missing.'); + } + $helper = $this->getView()->plugin('record'); + $context = isset($options['context']) ? $options['context'] : []; + $context['driver'] = $driver; + $context['data'] = $data; + return trim( + $helper($driver)->renderTemplate($options['template'], $context) + ); + } + + /** + * Get a link associated with a value, or else return false if link does + * not apply. + * + * @param string $value Value associated with link. + * @param array $options Rendering options. + * + * @return string|bool + */ + protected function getLink($value, $options) + { + if (isset($options['recordLink']) && $options['recordLink']) { + $helper = $this->getView()->plugin('record'); + return $helper->getLink($options['recordLink'], $value); + } + return false; + } + + /** + * Simple rendering method. + * + * @param RecordDriver $driver Reoord driver object. + * @param mixed $data Data to render + * @param array $options Rendering options. + * + * @return string + */ + protected function renderSimple(RecordDriver $driver, $data, array $options) + { + $view = $this->getView(); + $escaper = (isset($options['translate']) && $options['translate']) + ? $view->plugin('transEsc') : $view->plugin('escapeHtml'); + $separator = isset($options['separator']) + ? $options['separator'] : '<br />'; + $retVal = ''; + $array = (array)$data; + $remaining = count($data); + foreach ($array as $line) { + $remaining--; + $text = $escaper($line); + $retVal .= ($link = $this->getLink($line, $options)) + ? '<a href="' . $link . '">' . $text . '</a>' : $text; + if ($remaining > 0) { + $retVal .= $separator; + } + } + return (isset($options['prefix']) ? $options['prefix'] : '') + . $retVal + . (isset($options['suffix']) ? $options['suffix'] : ''); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php new file mode 100644 index 00000000000..aef39f81bd9 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatter/SpecBuilder.php @@ -0,0 +1,136 @@ +<?php +/** + * Specification builder for record driver data formatting view helper + * + * PHP version 5 + * + * Copyright (C) Villanova University 2016. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root\RecordDataFormatter; + +/** + * Specification builder for record driver data formatting view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SpecBuilder +{ + /** + * Spec + * + * @var array + */ + protected $spec = []; + + /** + * Highest position value so far. + * + * @var int + */ + protected $maxPos = 0; + + /** + * Constructor + * + * @param array $spec Existing specification lines (optional) + */ + public function __construct($spec = []) + { + $this->spec = $spec; + foreach ($spec as $current) { + if (isset($current['pos']) && $current['pos'] > $this->maxPos) { + $this->maxPos = $current['pos']; + } + } + } + /** + * Set a generic spec line. + * + * @param string $key Label to associate with this spec line + * @param string $dataMethod Method of data retrieval for rendering element + * @param string $renderType Type of rendering to use to generate output + * @param array $options Additional options + * + * @return array + */ + public function setLine($key, $dataMethod, $renderType = null, $options = []) + { + $options['dataMethod'] = $dataMethod; + $options['renderType'] = $renderType; + if (!isset($options['pos'])) { + $this->maxPos += 100; + $options['pos'] = $this->maxPos; + } + $this->spec[$key] = $options; + } + + /** + * Construct a record driver template spec line. + * + * @param string $key Label to associate with this spec line + * @param string $dataMethod Method of data retrieval for rendering element + * @param string $template Record driver template to render with data + * @param array $options Additional options + * + * @return array + */ + public function setTemplateLine($key, $dataMethod, $template, $options = []) + { + $options['template'] = $template; + return $this->setLine($key, $dataMethod, 'RecordDriverTemplate', $options); + } + + /** + * Reorder the specs to match the provided array of keys. + * + * @param array $orderedKeys Keys in the desired order + * @param int $defaultPos Position to use for elements not included in + * $orderedKeys (null to put unrecognized items at end of list). + * + * @return void + */ + public function reorderKeys($orderedKeys, $defaultPos = null) + { + $lookup = array_flip($orderedKeys); + if (null === $defaultPos) { + $defaultPos = (max($lookup) + 2) * 100; + } + foreach ($this->spec as $key => $options) { + $this->spec[$key]['pos'] = isset($lookup[$key]) + ? ($lookup[$key] + 1) * 100 : $defaultPos; + } + } + + /** + * Get the spec. + * + * @return array + */ + public function getArray() + { + return $this->spec; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php new file mode 100644 index 00000000000..1f355073207 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordDataFormatterFactory.php @@ -0,0 +1,161 @@ +<?php +/** + * Factory for record driver data formatting view helper + * + * PHP version 5 + * + * Copyright (C) Villanova University 2016. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:architecture:record_data_formatter + * Wiki + */ +namespace VuFind\View\Helper\Root; + +/** + * Factory for record driver data formatting view helper + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:architecture:record_data_formatter + * Wiki + */ +class RecordDataFormatterFactory +{ + /** + * Create the helper. + * + * @return RecordDataFormatter + */ + public function __invoke() + { + $helper = new RecordDataFormatter(); + $helper->setDefaults('core', $this->getDefaultCoreSpecs()); + $helper->setDefaults('description', $this->getDefaultDescriptionSpecs()); + return $helper; + } + + /** + * Get default specifications for displaying data in core metadata. + * + * @return array + */ + public function getDefaultCoreSpecs() + { + $spec = new RecordDataFormatter\SpecBuilder(); + $spec->setTemplateLine( + 'Published in', 'getContainerTitle', 'data-containerTitle.phtml' + ); + $spec->setLine( + 'New Title', 'getNewerTitles', null, ['recordLink' => 'title'] + ); + $spec->setLine( + 'Previous Title', 'getPreviousTitles', null, ['recordLink' => 'title'] + ); + $spec->setTemplateLine( + 'Main Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', + [ + 'useCache' => true, + 'labelFunction' => function ($data) { + return count($data['main']) > 1 + ? 'Main Authors' : 'Main Author'; + }, + 'context' => ['type' => 'main', 'schemaLabel' => 'author'], + ] + ); + $spec->setTemplateLine( + 'Corporate Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', + [ + 'useCache' => true, + 'labelFunction' => function ($data) { + return count($data['corporate']) > 1 + ? 'Corporate Authors' : 'Corporate Author'; + }, + 'context' => ['type' => 'corporate', 'schemaLabel' => 'creator'], + ] + ); + $spec->setTemplateLine( + 'Other Authors', 'getDeduplicatedAuthors', 'data-authors.phtml', + [ + 'useCache' => true, + 'context' => [ + 'type' => 'secondary', 'schemaLabel' => 'contributor' + ], + ] + ); + $spec->setLine( + 'Format', 'getFormats', 'RecordHelper', + ['helperMethod' => 'getFormatList'] + ); + $spec->setLine('Language', 'getLanguages'); + $spec->setTemplateLine( + 'Published', 'getPublicationDetails', 'data-publicationDetails.phtml' + ); + $spec->setLine( + 'Edition', 'getEdition', null, + ['prefix' => '<span property="bookEdition">', 'suffix' => '</span>'] + ); + $spec->setTemplateLine('Series', 'getSeries', 'data-series.phtml'); + $spec->setTemplateLine( + 'Subjects', 'getAllSubjectHeadings', 'data-allSubjectHeadings.phtml' + ); + $spec->setTemplateLine( + 'child_records', 'getChildRecordCount', 'data-childRecords.phtml', + ['allowZero' => false] + ); + $spec->setTemplateLine('Online Access', true, 'data-onlineAccess.phtml'); + $spec->setTemplateLine( + 'Related Items', 'getAllRecordLinks', 'data-allRecordLinks.phtml' + ); + $spec->setTemplateLine('Tags', true, 'data-tags.phtml'); + return $spec->getArray(); + } + + /** + * Get default specifications for displaying data in the description tab. + * + * @return array + */ + public function getDefaultDescriptionSpecs() + { + $spec = new RecordDataFormatter\SpecBuilder(); + $spec->setLine('Summary', 'getSummary'); + $spec->setLine('Published', 'getDateSpan'); + $spec->setLine('Item Description', 'getGeneralNotes'); + $spec->setLine('Physical Description', 'getPhysicalDescriptions'); + $spec->setLine('Publication Frequency', 'getPublicationFrequency'); + $spec->setLine('Playing Time', 'getPlayingTimes'); + $spec->setLine('Format', 'getSystemDetails'); + $spec->setLine('Audience', 'getTargetAudienceNotes'); + $spec->setLine('Awards', 'getAwards'); + $spec->setLine('Production Credits', 'getProductionCredits'); + $spec->setLine('Bibliography', 'getBibliographyNotes'); + $spec->setLine('ISBN', 'getISBNs'); + $spec->setLine('ISSN', 'getISSNs'); + $spec->setLine('DOI', 'getCleanDOI'); + $spec->setLine('Related Items', 'getRelationshipNotes'); + $spec->setLine('Access', 'getAccessRestrictions'); + $spec->setLine('Finding Aid', 'getFindingAids'); + $spec->setLine('Publication_Place', 'getHierarchicalPlaceNames'); + $spec->setTemplateLine('Author Notes', true, 'data-authorNotes.phtml'); + return $spec->getArray(); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php new file mode 100644 index 00000000000..4466b8fd6db --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatter/SpecBuilderTest.php @@ -0,0 +1,86 @@ +<?php +/** + * RecordDataFormatter spec builder Test Class + * + * PHP version 5 + * + * Copyright (C) Villanova University 2016. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root\RecordDataFormatter; +use VuFind\View\Helper\Root\RecordDataFormatter\SpecBuilder; + +/** + * RecordDataFormatter spec builder Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class SpecBuilderTest extends \VuFindTest\Unit\ViewHelperTestCase +{ + /** + * Test the spec builder + * + * @return void + */ + public function testBuilder() + { + $builder = new SpecBuilder(); + $builder->setLine('foo', 'getFoo'); + $builder->setLine('bar', 'getBar'); + $builder->setTemplateLine('xyzzy', 'getXyzzy', 'xyzzy.phtml'); + $expected = [ + 'foo' => [ + 'dataMethod' => 'getFoo', + 'renderType' => null, + 'pos' => 100, + ], + 'bar' => [ + 'dataMethod' => 'getBar', + 'renderType' => null, + 'pos' => 200, + ], + 'xyzzy' => [ + 'template' => 'xyzzy.phtml', + 'dataMethod' => 'getXyzzy', + 'renderType' => 'RecordDriverTemplate', + 'pos' => 300, + ], + ]; + $this->assertEquals($expected, $builder->getArray()); + $builder->reorderKeys(['xyzzy', 'bar']); + $expected['xyzzy']['pos'] = 100; + $expected['bar']['pos'] = 200; + $expected['foo']['pos'] = 300; + $this->assertEquals($expected, $builder->getArray()); + $builder->reorderKeys(['xyzzy', 'bar'], 0); + $expected['foo']['pos'] = 0; + $this->assertEquals($expected, $builder->getArray()); + $builder->reorderKeys(['foo', 'baz', 'xyzzy', 'bar']); + $expected['xyzzy']['pos'] = 300; + $expected['bar']['pos'] = 400; + $expected['foo']['pos'] = 100; + $this->assertEquals($expected, $builder->getArray()); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php new file mode 100644 index 00000000000..4d09734a348 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php @@ -0,0 +1,192 @@ +<?php +/** + * RecordDataFormatter Test Class + * + * PHP version 5 + * + * Copyright (C) Villanova University 2016. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root; +use VuFind\View\Helper\Root\RecordDataFormatter; +use VuFind\View\Helper\Root\RecordDataFormatterFactory; + +/** + * RecordDataFormatter Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase +{ + /** + * Setup test case. + * + * Mark test skipped if short_open_tag is not enabled. The partial + * uses short open tags. This directive is PHP_INI_PERDIR, + * i.e. can only be changed via php.ini or a per-directory + * equivalent. The test will fail if the test is run on + * a system with short_open_tag disabled in the system-wide php + * ini-file. + * + * @return void + */ + protected function setup() + { + parent::setup(); + if (!ini_get('short_open_tag')) { + $this->markTestSkipped('Test requires short_open_tag to be enabled'); + } + } + + /** + * Get view helpers needed by test. + * + * @return array + */ + protected function getViewHelpers() + { + $context = new \VuFind\View\Helper\Root\Context(); + return [ + 'auth' => new \VuFind\View\Helper\Root\Auth($this->getMockBuilder('VuFind\Auth\Manager')->disableOriginalConstructor()->getMock()), + 'context' => $context, + 'openUrl' => new \VuFind\View\Helper\Root\OpenUrl($context, []), + 'proxyUrl' => new \VuFind\View\Helper\Root\ProxyUrl(), + 'record' => new \VuFind\View\Helper\Root\Record(), + 'recordLink' => new \VuFind\View\Helper\Root\RecordLink($this->getMockBuilder('VuFind\Record\Router')->disableOriginalConstructor()->getMock()), + 'searchTabs' => $this->getMockBuilder('VuFind\View\Helper\Root\SearchTabs')->disableOriginalConstructor()->getMock(), + 'transEsc' => new \VuFind\View\Helper\Root\TransEsc(), + 'translate' => new \VuFind\View\Helper\Root\Translate(), + 'usertags' => new \VuFind\View\Helper\Root\UserTags(), + ]; + } + + /** + * Get a record driver with fake data. + * + * @param array $overrides Fixture fields to override. + * + * @return SolrDefault + */ + protected function getDriver($overrides = []) + { + // "Mock out" tag functionality to avoid database access: + $record = $this->getMockBuilder('VuFind\RecordDriver\SolrDefault') + ->setMethods(['getBuilding', 'getContainerTitle', 'getTags']) + ->getMock(); + $record->expects($this->any())->method('getTags') + ->will($this->returnValue([])); + // Force a return value of zero so we can test this edge case value (even + // though in the context of "building"/"container title" it makes no sense): + $record->expects($this->any())->method('getBuilding') + ->will($this->returnValue(0)); + $record->expects($this->any())->method('getContainerTitle') + ->will($this->returnValue('0')); + + // Load record data from fixture file: + $fixture = json_decode( + file_get_contents( + realpath( + VUFIND_PHPUNIT_MODULE_PATH . '/fixtures/misc/testbug2.json' + ) + ), + true + ); + $record->setRawData($overrides + $fixture['response']['docs'][0]); + return $record; + } + + /** + * Build a formatter, including necessary mock view w/ helpers. + * + * @return RecordDataFormatter + */ + protected function getFormatter() + { + // Build the formatter: + $factory = new RecordDataFormatterFactory(); + $formatter = $factory->__invoke(); + + // Create a view object with a set of helpers: + $helpers = $this->getViewHelpers(); + $view = $this->getPhpRenderer($helpers); + + // Mock out the router to avoid errors: + $match = new \Zend\Mvc\Router\RouteMatch([]); + $match->setMatchedRouteName('foo'); + $view->plugin('url') + ->setRouter($this->getMock('Zend\Mvc\Router\RouteStackInterface')) + ->setRouteMatch($match); + + // Inject the view object into all of the helpers: + $formatter->setView($view); + foreach ($helpers as $helper) { + $helper->setView($view); + } + + return $formatter; + } + + /** + * Test citation generation + * + * @return void + */ + public function testFormatting() + { + $formatter = $this->getFormatter(); + $spec = $formatter->getDefaults('core'); + $spec['Building'] = ['dataMethod' => 'getBuilding', 'pos' => 0]; + + $expected = [ + 'Building' => '0', + 'Published in' => '0', + 'Main Author' => 'Vico, Giambattista, 1668-1744.', + 'Other Authors' => 'Pandolfi, Claudia.', + 'Format' => 'Book', + 'Language' => 'ItalianLatin', + 'Published' => 'Centro di Studi Vichiani, 1992', + 'Edition' => 'Fictional edition.', + 'Series' => 'Vico, Giambattista, 1668-1744. Works. 1982 ;', + 'Subjects' => 'Naples (Kingdom) History Spanish rule, 1442-1707 Sources', + 'Online Access' => 'http://fictional.com/sample/url', + 'Tags' => 'Add Tag No Tags, Be the first to tag this record!', + ]; + $driver = $this->getDriver(); + $results = $formatter->getData($driver, $spec); + + // Check for expected array keys + $this->assertEquals(array_keys($expected), array_keys($results)); + + // Check for expected text (with markup stripped) + foreach ($expected as $key => $value) { + $this->assertEquals( + $value, trim(preg_replace('/\s+/', ' ', strip_tags($results[$key]))) + ); + } + + // Check for exact markup in representative example: + $this->assertEquals('Italian<br />Latin', $results['Language']); + } +} diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml index 0d411fb7425..4504c410944 100644 --- a/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/core.phtml @@ -1,22 +1,3 @@ -<? - if($loggedin = $this->auth()->isLoggedIn()) { - $user_id = $loggedin->id; - $loggedin = true; - } else { - $user_id = false; - } - - $formatRoles = function ($roles) { - if (count($roles) == 0) { - return ''; - } - $that = $this; - $translate = function ($str) use ($that) { - return $that->transEsc('CreatorRoles::' . $str); - }; - return ' (' . implode(', ', array_unique(array_map($translate, $roles))) . ')'; - }; -?> <div class="row" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> <? $QRCode = $this->record($this->driver)->getQRCode("core"); $cover = $this->record($this->driver)->getCover('core', 'medium', $this->record($this->driver)->getThumbnail('large')); @@ -68,221 +49,17 @@ <? endif; ?> <?/* Display Main Details */?> - <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> - <? $journalTitle = $this->driver->getContainerTitle(); if (!empty($journalTitle)): ?> - <tr> - <th><?=$this->transEsc('Published in')?>:</th> - <td> - <? - $containerSource = $this->driver->getSourceIdentifier(); - $containerID = $this->driver->getContainerRecordID(); - ?> - <a href="<?=($containerID ? $this->recordLink()->getUrl("$containerSource|$containerID") : $this->record($this->driver)->getLink('journaltitle', $journalTitle))?>"><?=$this->escapeHtml($journalTitle)?></a> - <? $ref = $this->driver->getContainerReference(); if (!empty($ref)) { echo $this->escapeHtml($ref); } ?> - </td> - </tr> - <? endif; ?> - - <? $nextTitles = $this->driver->getNewerTitles(); $prevTitles = $this->driver->getPreviousTitles(); ?> - <? if (!empty($nextTitles)): ?> - <tr> - <th><?=$this->transEsc('New Title')?>: </th> - <td> - <? foreach($nextTitles as $field): ?> - <a href="<?=$this->record($this->driver)->getLink('title', $field)?>"><?=$this->escapeHtml($field)?></a><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? if (!empty($prevTitles)): ?> - <tr> - <th><?=$this->transEsc('Previous Title')?>: </th> - <td> - <? foreach($prevTitles as $field): ?> - <a href="<?=$this->record($this->driver)->getLink('title', $field)?>"><?=$this->escapeHtml($field)?></a><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $authors = $this->driver->getDeduplicatedAuthors(); ?> - <? if (isset($authors['main']) && !empty($authors['main'])): ?> - <tr> - <th><?=$this->transEsc(count($authors['main']) > 1 ? 'Main Authors' : 'Main Author')?>: </th> - <td> - <? $i = 0; foreach ($authors['main'] as $author => $roles): ?><?=($i++ == 0)?'':', '?><span property="author"><a href="<?=$this->record($this->driver)->getLink('author', $author)?>"><?=$this->escapeHtml($author)?></a><?=$formatRoles($roles)?></span><? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? if (isset($authors['corporate']) && !empty($authors['corporate'])): ?> - <tr> - <th><?=$this->transEsc(count($authors['corporate']) > 1 ? 'Corporate Author' : 'Corporate Authors')?>: </th> - <td> - <? $i = 0; foreach ($authors['corporate'] as $corporate => $roles): ?><?=($i++ == 0)?'':', '?><span property="creator"><a href="<?=$this->record($this->driver)->getLink('author', $corporate)?>"><?=$this->escapeHtml($corporate)?></a><?=$formatRoles($roles)?></span><? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? if (isset($authors['secondary']) && !empty($authors['secondary'])): ?> - <tr> - <th><?=$this->transEsc('Other Authors')?>: </th> - <td> - <? $i = 0; foreach ($authors['secondary'] as $author => $roles): ?><?=($i++ == 0)?'':', '?><span property="contributor"><a href="<?=$this->record($this->driver)->getLink('author', $author)?>"><?=$this->escapeHtml($author)?></a><?=$formatRoles($roles)?></span><? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? if (count($this->driver->getFormats()) > 0): ?> - <tr> - <th><?=$this->transEsc('Format')?>: </th> - <td><?=$this->record($this->driver)->getFormatList()?></td> - </tr> - <? endif; ?> - - <? $langs = $this->driver->getLanguages(); if (!empty($langs)): ?> - <tr> - <th><?=$this->transEsc('Language')?>: </th> - <td><? foreach ($langs as $lang): ?><?= $this->escapeHtml($lang)?><br/><? endforeach; ?></td> - </tr> - <? endif; ?> - - <? $publications = $this->driver->getPublicationDetails(); if (!empty($publications)): ?> - <tr> - <th><?=$this->transEsc('Published')?>: </th> - <td> - <? foreach ($publications as $field): ?> - <span property="publisher" typeof="Organization"> - <? $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> - <span property="location"><?=$this->escapeHtml($pubPlace)?></span> - <? endif; ?> - <? $pubName = $field->getName(); if (!empty($pubName)): ?> - <span property="name"><?=$this->escapeHtml($pubName)?></span> - <? endif; ?> - </span> - <? $pubDate = $field->getDate(); if (!empty($pubDate)): ?> - <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> - <? endif; ?> - <br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $edition = $this->driver->getEdition(); if (!empty($edition)): ?> - <tr> - <th><?=$this->transEsc('Edition')?>: </th> - <td property="bookEdition"><?=$this->escapeHtml($edition)?></td> - </tr> - <? endif; ?> - - <?/* Display series section if at least one series exists. */?> - <? $series = $this->driver->getSeries(); if (!empty($series)): ?> - <tr> - <th><?=$this->transEsc('Series')?>: </th> - <td> - <? foreach ($series as $field): ?> - <?/* Depending on the record driver, $field may either be an array with - "name" and "number" keys or a flat string containing only the series - name. We should account for both cases to maximize compatibility. */?> - <? if (is_array($field)): ?> - <? if (!empty($field['name'])): ?> - <a href="<?=$this->record($this->driver)->getLink('series', $field['name'])?>"><?=$this->escapeHtml($field['name'])?></a> - <? if (!empty($field['number'])): ?> - <?=$this->escapeHtml($field['number'])?> - <? endif; ?> - <br/> - <? endif; ?> - <? else: ?> - <a href="<?=$this->record($this->driver)->getLink('series', $field)?>"><?=$this->escapeHtml($field)?></a><br/> - <? endif; ?> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $subjects = $this->driver->getAllSubjectHeadings(); if (!empty($subjects)): ?> - <tr> - <th><?=$this->transEsc('Subjects')?>: </th> - <td> - <? foreach ($subjects as $field): ?> - <div class="subject-line" property="keywords"> - <? $subject = ''; ?> - <? if(count($field) == 1) $field = explode('--', $field[0]); ?> - <? $i = 0; foreach ($field as $subfield): ?> - <?=($i++ == 0) ? '' : ' > '?> - <? $subject = trim($subject . ' ' . $subfield); ?> - <a title="<?=$this->escapeHtmlAttr($subject)?>" href="<?=$this->record($this->driver)->getLink('subject', $subject)?>" rel="nofollow"><?=trim($this->escapeHtml($subfield))?></a> - <? endforeach; ?> - </div> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $childRecordCount = $this->driver->tryMethod('getChildRecordCount'); if ($childRecordCount): ?> - <tr> - <th><?=$this->transEsc('child_records')?>: </th> - <td> - <a href="<?=$this->recordLink()->getChildRecordSearchUrl($this->driver)?>"><?=$this->transEsc('child_record_count', array('%%count%%' => $childRecordCount))?></a> - </td> - </tr> - <? endif; ?> - - <? - $openUrl = $this->openUrl($this->driver, 'record'); - $openUrlActive = $openUrl->isActive(); - // Account for replace_other_urls setting - $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); - ?> - <? if (!empty($urls) || $openUrlActive): ?> - <tr> - <th><?=$this->transEsc('Online Access')?>: </th> - <td> - <? foreach ($urls as $current): ?> - <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/> - <? endforeach; ?> - <? if ($openUrlActive): ?> - <?=$openUrl->renderTemplate()?><br/> - <? endif; ?> - </td> - </tr> - <? endif; ?> - - <? $recordLinks = $this->driver->getAllRecordLinks(); ?> - <? if(!empty($recordLinks)): ?> - <tr> - <th><?=$this->transEsc('Related Items')?>:</th> - <td> - <? foreach ($recordLinks as $recordLink): ?> - <?=$this->transEsc($recordLink['title'])?>: - <a href="<?=$this->recordLink()->related($recordLink['link'])?>"><?=$this->escapeHtml($recordLink['value'])?></a><br /> - <? endforeach; ?> - <? /* if we have record links, display relevant explanatory notes */ - $related = $this->driver->getRelationshipNotes(); - if (!empty($related)): ?> - <? foreach ($related as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - <? endif; ?> - </td> - </tr> - <? endif; ?> - - <? if ($this->usertags()->getMode() !== 'disabled'): ?> - <? $tagList = $this->driver->getTags(null, null, 'count', $user_id); ?> - <tr> - <th><?=$this->transEsc('Tags')?>: </th> - <td> - <a class="tag-record btn btn-link pull-right flip" href="<?=$this->recordLink()->getActionUrl($this->driver, 'AddTag')?>" data-lightbox> - <i class="fa fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Add Tag')?> - </a> - <?=$this->context($this)->renderInContext('record/taglist', array('tagList'=>$tagList, 'loggedin'=>$loggedin)) ?> - </td> - </tr> - <? endif; ?> - </table> + <? + $formatter = $this->recordDataFormatter(); + $coreFields = $formatter->getData($driver, $formatter->getDefaults('core')); + ?> + <? if (!empty($coreFields)): ?> + <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> + <? foreach ($coreFields as $key => $value): ?> + <tr><th><?=$this->transEsc($key)?>:</th><td><?=$value?></td></tr> + <? endforeach; ?> + </table> + <? endif; ?> <?/* End Main Details */?> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml new file mode 100644 index 00000000000..f2a8dfd4007 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allRecordLinks.phtml @@ -0,0 +1,11 @@ +<? foreach ($data as $recordLink): ?> + <?=$this->transEsc($recordLink['title'])?>: + <a href="<?=$this->recordLink()->related($recordLink['link'])?>"><?=$this->escapeHtml($recordLink['value'])?></a><br /> +<? endforeach; ?> +<? /* if we have record links, display relevant explanatory notes */ + $related = $this->driver->getRelationshipNotes(); + if (!empty($related)): ?> + <? foreach ($related as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml new file mode 100644 index 00000000000..2ab86fa7b88 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-allSubjectHeadings.phtml @@ -0,0 +1,11 @@ +<? foreach ($data as $field): ?> + <div class="subject-line" property="keywords"> + <? $subject = ''; ?> + <? if(count($field) == 1) $field = explode('--', $field[0]); ?> + <? $i = 0; foreach ($field as $subfield): ?> + <?=($i++ == 0) ? '' : ' > '?> + <? $subject = trim($subject . ' ' . $subfield); ?> + <a title="<?=$this->escapeHtmlAttr($subject)?>" href="<?=$this->record($this->driver)->getLink('subject', $subject)?>" rel="nofollow"><?=trim($this->escapeHtml($subfield))?></a> + <? endforeach; ?> + </div> +<? endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml new file mode 100644 index 00000000000..1d58594cea1 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authorNotes.phtml @@ -0,0 +1,8 @@ +<? $isbn = $this->driver->getCleanISBN(); ?> +<? $authorNotes = empty($isbn) ? [] : $this->authorNotes($isbn); if (!empty($authorNotes)): ?> + <? foreach ($authorNotes as $provider => $list): ?> + <? foreach ($list as $field): ?> + <?=$field['Content']?><br /> + <? endforeach; ?> + <? endforeach; ?> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml new file mode 100644 index 00000000000..c63f6c6274e --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-authors.phtml @@ -0,0 +1,15 @@ +<? + $formatRoles = function ($roles) { + if (count($roles) == 0) { + return ''; + } + $that = $this; + $translate = function ($str) use ($that) { + return $that->transEsc('CreatorRoles::' . $str); + }; + return ' (' . implode(', ', array_unique(array_map($translate, $roles))) . ')'; + }; +?> +<? if (isset($data[$type]) && !empty($data[$type])): ?> + <? $i = 0; foreach ($data[$type] as $author => $roles): ?><?=($i++ == 0)?'':', '?><span property="<?=$this->escapeHtml($schemaLabel)?>"><a href="<?=$this->record($this->driver)->getLink('author', $author)?>"><?=$this->escapeHtml($author)?></a><?=$formatRoles($roles)?></span><? endforeach; ?> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml new file mode 100644 index 00000000000..bcd2de76312 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-childRecords.phtml @@ -0,0 +1 @@ +<a href="<?=$this->recordLink()->getChildRecordSearchUrl($this->driver)?>"><?=$this->transEsc('child_record_count', array('%%count%%' => $data))?></a> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml new file mode 100644 index 00000000000..82cba51d8c6 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-containerTitle.phtml @@ -0,0 +1,10 @@ +<? + $containerSource = $this->driver->getSourceIdentifier(); + $containerID = $this->driver->getContainerRecordID(); + $ref = $this->driver->getContainerReference(); + $link = $containerID + ? $this->recordLink()->getUrl("$containerSource|$containerID") + : $this->record($this->driver)->getLink('journaltitle', $data); +?> +<a href="<?=$link?>"><?=$this->escapeHtml($data)?></a> +<?=empty($ref) ? '' : $this->escapeHtml($ref)?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml new file mode 100644 index 00000000000..2ddb8a1bddf --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-onlineAccess.phtml @@ -0,0 +1,14 @@ +<? + $openUrl = $this->openUrl($this->driver, 'record'); + $openUrlActive = $openUrl->isActive(); + // Account for replace_other_urls setting + $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); +?> +<? if (!empty($urls) || $openUrlActive): ?> + <? foreach ($urls as $current): ?> + <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/> + <? endforeach; ?> + <? if ($openUrlActive): ?> + <?=$openUrl->renderTemplate()?><br/> + <? endif; ?> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml new file mode 100644 index 00000000000..c8b7a78a2eb --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-publicationDetails.phtml @@ -0,0 +1,14 @@ +<? foreach ($data as $field): ?> + <span property="publisher" typeof="Organization"> + <? $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> + <span property="location"><?=$this->escapeHtml($pubPlace)?></span> + <? endif; ?> + <? $pubName = $field->getName(); if (!empty($pubName)): ?> + <span property="name"><?=$this->escapeHtml($pubName)?></span> + <? endif; ?> + </span> + <? $pubDate = $field->getDate(); if (!empty($pubDate)): ?> + <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> + <? endif; ?> + <br/> +<? endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml new file mode 100644 index 00000000000..b71c03ebc2f --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-series.phtml @@ -0,0 +1,16 @@ +<? foreach ($data as $field): ?> + <?/* Depending on the record driver, $field may either be an array with + "name" and "number" keys or a flat string containing only the series + name. We should account for both cases to maximize compatibility. */?> + <? if (is_array($field)): ?> + <? if (!empty($field['name'])): ?> + <a href="<?=$this->record($this->driver)->getLink('series', $field['name'])?>"><?=$this->escapeHtml($field['name'])?></a> + <? if (!empty($field['number'])): ?> + <?=$this->escapeHtml($field['number'])?> + <? endif; ?> + <br/> + <? endif; ?> + <? else: ?> + <a href="<?=$this->record($this->driver)->getLink('series', $field)?>"><?=$this->escapeHtml($field)?></a><br/> + <? endif; ?> +<? endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml new file mode 100644 index 00000000000..285e4ea0d31 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrDefault/data-tags.phtml @@ -0,0 +1,15 @@ +<? + if($loggedin = $this->auth()->isLoggedIn()) { + $user_id = $loggedin->id; + $loggedin = true; + } else { + $user_id = false; + } +?> +<? if ($this->usertags()->getMode() !== 'disabled'): ?> + <? $tagList = $this->driver->getTags(null, null, 'count', $user_id); ?> + <a class="tag-record btn btn-link pull-right flip" href="<?=$this->recordLink()->getActionUrl($this->driver, 'AddTag')?>" data-lightbox> + <i class="fa fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Add Tag')?> + </a> + <?=$this->context($this)->renderInContext('record/taglist', array('tagList'=>$tagList, 'loggedin'=>$loggedin)) ?> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/description.phtml b/themes/bootstrap3/templates/RecordTab/description.phtml index d1794847e4f..e989574ff21 100644 --- a/themes/bootstrap3/templates/RecordTab/description.phtml +++ b/themes/bootstrap3/templates/RecordTab/description.phtml @@ -2,237 +2,15 @@ // Set page title. $this->headTitle($this->translate('Description') . ': ' . $this->driver->getBreadcrumb()); - // Grab clean ISBN for convenience: - $isbn = $this->driver->getCleanISBN(); + $formatter = $this->recordDataFormatter(); + $mainFields = $formatter->getData($driver, $formatter->getDefaults('description')); ?> <table class="table table-striped" summary="<?=$this->transEsc('Description')?>"> - <? $summ = $this->driver->getSummary(); if (!empty($summ)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Summary')?>: </th> - <td> - <? foreach ($summ as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $dateSpan = $this->driver->getDateSpan(); if (!empty($dateSpan)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Published')?>: </th> - <td> - <? foreach ($dateSpan as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $notes = $this->driver->getGeneralNotes(); if (!empty($notes)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Item Description')?>: </th> - <td> - <? foreach ($notes as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $physical = $this->driver->getPhysicalDescriptions(); if (!empty($physical)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Physical Description')?>: </th> - <td> - <? foreach ($physical as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $freq = $this->driver->getPublicationFrequency(); if (!empty($freq)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Publication Frequency')?>: </th> - <td> - <? foreach ($freq as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $playTime = $this->driver->getPlayingTimes(); if (!empty($playTime)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Playing Time')?>: </th> - <td> - <? foreach ($playTime as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $system = $this->driver->getSystemDetails(); if (!empty($system)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Format')?>: </th> - <td> - <? foreach ($system as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $audience = $this->driver->getTargetAudienceNotes(); if (!empty($audience)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Audience')?>: </th> - <td> - <? foreach ($audience as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $awards = $this->driver->getAwards(); if (!empty($awards)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Awards')?>: </th> - <td> - <? foreach ($awards as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $credits = $this->driver->getProductionCredits(); if (!empty($credits)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Production Credits')?>: </th> - <td> - <? foreach ($credits as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $bib = $this->driver->getBibliographyNotes(); if (!empty($bib)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Bibliography')?>: </th> - <td> - <? foreach ($bib as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $isbns = $this->driver->getISBNs(); if (!empty($isbns)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('ISBN')?>: </th> - <td> - <? foreach ($isbns as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $issns = $this->driver->getISSNs(); if (!empty($issns)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('ISSN')?>: </th> - <td> - <? foreach ($issns as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $doi = $this->driver->tryMethod('getCleanDOI'); if (!empty($doi)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('DOI')?>: </th> - <td><?=$this->escapeHtml($doi)?></td> - </tr> - <? endif; ?> - - <? $related = $this->driver->getRelationshipNotes(); if (!empty($related)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Related Items')?>: </th> - <td> - <? foreach ($related as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $access = $this->driver->getAccessRestrictions(); if (!empty($access)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Access')?>: </th> - <td> - <? foreach ($access as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $findingAids = $this->driver->getFindingAids(); if (!empty($findingAids)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Finding Aid')?>: </th> - <td> - <? foreach ($findingAids as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $publicationPlaces = $this->driver->getHierarchicalPlaceNames(); if (!empty($publicationPlaces)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Publication_Place')?>: </th> - <td> - <? foreach ($publicationPlaces as $field): ?> - <?=$this->escapeHtml($field)?><br/> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? $authorNotes = empty($isbn) ? [] : $this->authorNotes($isbn); if (!empty($authorNotes)): ?> - <? $contentDisplayed = true; ?> - <tr> - <th><?=$this->transEsc('Author Notes')?>: </th> - <td> - <? foreach ($authorNotes as $provider => $list): ?> - <? foreach ($list as $field): ?> - <?=$field['Content']?><br/> - <? endforeach; ?> - <? endforeach; ?> - </td> - </tr> - <? endif; ?> - - <? if (!isset($contentDisplayed) || !$contentDisplayed): // Avoid errors if there were no rows above ?> + <? if (!empty($mainFields)): ?> + <? foreach ($mainFields as $key => $value): ?> + <tr><th><?=$this->transEsc($key)?>:</th><td><?=$value?></td></tr> + <? endforeach; ?> + <? else: ?> <tr><td><?=$this->transEsc('no_description')?></td></tr> <? endif; ?> </table> diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index 0de80f7ed5c..a6d343b3517 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -27,6 +27,7 @@ return array( 'piwik' => 'VuFind\View\Helper\Root\Factory::getPiwik', 'recaptcha' => 'VuFind\View\Helper\Root\Factory::getRecaptcha', 'record' => 'VuFind\View\Helper\Root\Factory::getRecord', + 'recorddataformatter' => 'VuFind\View\Helper\Root\RecordDataFormatterFactory', 'recordlink' => 'VuFind\View\Helper\Root\Factory::getRecordLink', 'related' => 'VuFind\View\Helper\Root\Factory::getRelated', 'safemoneyformat' => 'VuFind\View\Helper\Root\Factory::getSafeMoneyFormat', -- GitLab