diff --git a/config/vufind/searchspecs.yaml b/config/vufind/searchspecs.yaml index e945c79ad982bd74ef8ab3ba1fb11e78f923a8d4..62e12e5d243f06e5b00e44b60d075df05e5c7036 100644 --- a/config/vufind/searchspecs.yaml +++ b/config/vufind/searchspecs.yaml @@ -148,6 +148,19 @@ # [uppercase] - Convert string to uppercase # # See the CallNumber search below for an example of custom munging in action. +# +#----------------------------------------------------------------------------------- +# +# Note that you may create a "@parent_yaml" entry at the very top of the file to +# inherit sections from another file. For example: +# +# @parent_yaml: "/path/to/my/file.yaml" +# +# Only sections not found in this file will be loaded in from the parent file. +# In some complex scenarios, this can be a useful way of sharing settings +# between multiple configured VuFind instances. You can create a chain of parent +# files if necessary. +# #----------------------------------------------------------------------------------- # These searches use Dismax when possible: diff --git a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php index 8bc6b752e9fca8f11b684477c7e5ba47582a8834..d772c346e30ba51cc73be9f42cb18387234205c6 100644 --- a/module/VuFind/src/VuFind/Config/SearchSpecsReader.php +++ b/module/VuFind/src/VuFind/Config/SearchSpecsReader.php @@ -134,6 +134,13 @@ class SearchSpecsReader $results = (!empty($file) && file_exists($file)) ? Yaml::parse(file_get_contents($file)) : []; + // Override default parent with explicitly-defined parent, if present: + if (isset($results['@parent_yaml'])) { + $defaultParent = $results['@parent_yaml']; + // Swallow the directive after processing it: + unset($results['@parent_yaml']); + } + // Now load in missing sections from parent, if applicable: if (null !== $defaultParent) { foreach ($this->parseYaml($defaultParent) as $section => $contents) { diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php index ce7e7fdf408c907e239bc3c520b2b6db762f9826..52d8c0f294722b12b7c986f5f04618488bf2e542 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php @@ -26,7 +26,7 @@ * @link https://vufind.org/wiki/development:testing:unit_tests Wiki */ namespace VuFindTest\Config; -use VuFind\Config\SearchSpecsReader; +use VuFind\Config\Locator, VuFind\Config\SearchSpecsReader; /** * Config SearchSpecsReader Test Class @@ -40,6 +40,49 @@ use VuFind\Config\SearchSpecsReader; */ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase { + /** + * Flag -- did writing config files fail? + * + * @var bool + */ + protected static $writeFailed = false; + + /** + * Array of files to clean up after test. + * + * @var array + */ + protected static $filesToDelete = []; + + /** + * Standard setup method. + * + * @return void + */ + public static function setUpBeforeClass() + { + // Create test files: + $parentPath = Locator::getLocalConfigPath('top.yaml', null, true); + $parent = "top: foo"; + $childPath = Locator::getLocalConfigPath('middle.yaml', null, true); + $child = "@parent_yaml: $parentPath\nmiddle: bar"; + $grandchildPath = Locator::getLocalConfigPath('bottom.yaml', null, true); + $grandchild = "@parent_yaml: $childPath\nbottom: baz"; + + // Fail if we are unable to write files: + if (null === $parentPath || null === $childPath || null === $grandchildPath + || !file_put_contents($parentPath, $parent) + || !file_put_contents($childPath, $child) + || !file_put_contents($grandchildPath, $grandchild) + ) { + self::$writeFailed = true; + return; + } + + // Mark for cleanup: + self::$filesToDelete = [$parentPath, $childPath, $grandchildPath]; + } + /** * Test loading of a YAML file. * @@ -114,4 +157,38 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase $this->callMethod($reader, 'getFromPaths', [$core, $local]) ); } + + /** + * Test @parent_yaml directive. + * + * @return void + */ + public function testParentYaml() + { + if (self::$writeFailed) { + $this->markTestSkipped('Could not write test configurations.'); + } + $reader = new SearchSpecsReader(); + $core = Locator::getLocalConfigPath('middle.yaml', null, true); + $local = Locator::getLocalConfigPath('bottom.yaml', null, true); + $this->assertEquals( + [ + 'top' => 'foo', + 'middle' => 'bar', + 'bottom' => 'baz', + ], + $this->callMethod($reader, 'getFromPaths', [$core, $local]) + ); + } + + /** + * Standard teardown method. + * + * @return void + */ + public static function tearDownAfterClass() + { + // Clean up test files: + array_map('unlink', self::$filesToDelete); + } }