diff --git a/devops/docker/autoconfig/entrypoint.sh b/devops/docker/autoconfig/entrypoint.sh index e8bcdc261a5f5677c43bf2161bcd7e546c166ba7..8213ec03d8a0b897dd6805047ec0eb8561dd7acb 100755 --- a/devops/docker/autoconfig/entrypoint.sh +++ b/devops/docker/autoconfig/entrypoint.sh @@ -18,7 +18,7 @@ # @license https://opensource.org/licenses/GPL-3.0 GNU GPLv3 if [ "$(whoami)" = "node" ]; then - [ -d "$HOME" ] || PREFIX=$HOME npm install -g @ubleipzig/autoconfig@2.3.2 + [ -d "$HOME" ] || PREFIX=$HOME npm install -g @ubleipzig/autoconfig@2.3.3 echo "$VUFIND_DEFAULTS" > "./data/docker/autoconfig/$VUFIND_SITE.json" exec "$HOME/bin/autoconfig" -c ./data/docker/autoconfig "$@" exit diff --git a/docker-env.0.finc.yml b/docker-env.0.finc.yml index d3766a42100edd5c832883b6ff46721d96ba548e..68452924bcae45c4fe104bf7a89b2719937c5d53 100644 --- a/docker-env.0.finc.yml +++ b/docker-env.0.finc.yml @@ -55,5 +55,7 @@ autoconfig: password: dev searches.ini: IndexShards: {} + searchspecs.yaml: + "@parent_yaml": "../../../config/vufind/searchspecs.yaml" mail: {} \ No newline at end of file diff --git a/local/alpha/config/vufind/searchspecs.yaml b/local/alpha/config/vufind/searchspecs.yaml index a14e9f67517e72cd14a6935db4f32b5c3f289f91..0f5518b83044f980aa4385c47fcad1be7019104e 100644 --- a/local/alpha/config/vufind/searchspecs.yaml +++ b/local/alpha/config/vufind/searchspecs.yaml @@ -1,779 +1,2 @@ --- -# Listing of search types and their component parts and weights. -# -# Format is: -# searchType: -# # CustomMunge is an optional section to define custom pre-processing of -# # user input. See below for details of munge actions. -# CustomMunge: -# MungeName1: -# - [action1, actionParams] -# - [action2, actionParams] -# - [action3, actionParams] -# MungeName2: -# - [action1, actionParams] -# # DismaxFields is optional and defines the fields sent to the Dismax handler -# # when we are able to use it. QueryFields will be used for advanced -# # searches that Dismax cannot support. QueryFields is always used if no -# # DismaxFields section is defined. -# DismaxFields: -# - field1^boost -# - field2^boost -# - field3^boost -# # DismaxParams is optional and allows you to override default Dismax settings -# # (i.e. mm / bf) on a search-by-search basis. Enclose the parameter values -# # in quotes for proper behavior. If you want global default values for these -# # settings, you can edit the appropriate search handler in -# # solr/biblio/conf/solrconfig.xml. -# DismaxParams: -# - [param1_name, param1_value] -# - [param2_name, param2_value] -# - [param3_name, param3_value] -# # This optional setting may be used to specify which Dismax handler to use. By -# # default, VuFind provides two options: dismax (for the old, standard -# # Dismax) and edismax (for Extended Dismax). You can also configure your own -# # in solrconfig.xml, but VuFind relies on the name "edismax" to identify an -# # Extended Dismax handler. If you omit this setting, the default value from -# # the default_dismax_handler setting in the [Index] section of config.ini -# # will be used. -# DismaxHandler: dismax|edismax -# # QueryFields define the fields we are searching when not using Dismax; VuFind -# # detects queries that will not work with Dismax and switches to QueryFields -# # as needed. -# QueryFields: -# SolrField: -# - [howToMungeSearchstring, weight] -# - [differentMunge, weight] -# DifferentSolrField: -# - [howToMunge, weight] -# # The optional FilterQuery section allows you to AND a static query to the -# # dynamic query generated using the QueryFields; see JournalTitle below -# # for an example. This is applied whether we use DismaxFields or -# # QueryFields. -# FilterQuery: (optional Lucene filter query) -# ExactSettings: -# DismaxFields: ... -# QueryFields: ... -# # All the same settings as above, but for exact searches, i.e. search terms -# # enclosed in quotes. Allows different fields or weights for exact -# # searches. See below for commented-out examples. -# -# ...etc. -# -#----------------------------------------------------------------------------------- -# -# Within the QueryFields area, fields are OR'd together, unless they're in an -# anonymous array with a numeric instead of alphanumeric key, in which case the -# first element is a two-value array that tells us what the type (AND or OR) and -# weight of the whole group should be. -# -# So, given: -# -# test: -# QueryFields: -# A: -# - [onephrase, 500] -# - [and, 200] -# B: -# - [and, 100] -# - [or, 50] -# # Start an anonymous array to group; first element indicates AND grouping -# # and a weight of 50 -# 0: -# 0: -# - AND -# - 50 -# C: -# - [onephrase, 200] -# D: -# - [onephrase, 300] -# # Note the "not" attached to the field name as a minus, and the use of ~ -# # to mean null ("no special weight") -# -E: -# - [or, ~] -# D: -# - [or, 100] -# -# ...and the search string -# -# test "one two" -# -# ...we'd get -# -# (A:"test one two"^500 OR -# A:(test AND "one two")^ 200 OR -# B:(test AND "one two")^100 OR -# B:(test OR "one two")^50 -# ( -# C:("test one two")^200 AND -# D:"test one two"^300 AND -# -E:(test OR "one two") -# )^50 OR -# D:(test OR "one two")^100 -# ) -# -#----------------------------------------------------------------------------------- -# -# Munge types are based on the original Solr.php code, and consist of: -# -# onephrase: eliminate all quotes and do it as a single phrase. -# testing "one two" -# ...becomes ("testing one two") -# -# and: AND the terms together -# testing "one two" -# ...becomes (testing AND "one two") -# -# or: OR the terms together -# testing "one two" -# ...becomes (testing OR "one two") -# -# identity: Use the search as-is -# testing "one two" -# ...becomes (testing "one two") -# -# Additional Munge types can be defined in the CustomMunge section. Each array -# entry under CustomMunge defines a new named munge type. Each array entry under -# the name of the munge type specifies a string manipulation operation. Operations -# will be applied in the order listed, and different operations take different -# numbers of parameters. -# -# Munge operations: -# -# [append, text] - Append text to the end of the user's search string -# [lowercase] - Convert string to lowercase -# [preg_replace, pattern, replacement] - Perform a regular expression replace -# using the preg_replace() PHP function. If you use backreferences in your -# replacement phrase, be sure to escape dollar signs (i.e. \$1, not $1). -# [uppercase] - Convert string to uppercase -# -# See the CallNumber search below for an example of custom munging in action. -#----------------------------------------------------------------------------------- - -# These searches use Dismax when possible: -Author: - DismaxParams: - - [bf , ord(publishDateSort)^10] - DismaxFields: - - author^400 - - author2^300 - - author_id^100 - - author_ref^150 - - author_corporate^200 - - author_corporate2^200 - - author_orig^200 - - author2_orig^200 - - author_corporate_orig^200 - - author_corporate2_orig^200 - - author_fuller^50 - - author2_fuller - - author_additional - - author_variant - - author2_variant - QueryFields: - author: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_fuller: - - [onephrase, 200] - - [and, 100] - - [or, 50] - author2: - - [onephrase, 100] - - [and, 50] - - [or, ~] - author_ref: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_corporate: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_corporate2: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_orig: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author2_orig: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_corporate_orig: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_corporate2_orig: - - [onephrase, 350] - - [and, 200] - - [or, 100] - author_id: - - [onephrase, 450] - - [and, 300] - - [or, 200] - author2_fuller: - - [onephrase, 100] - - [and, 50] - - [or, ~] - author_additional: - - [onephrase, 100] - - [and, 50] - - [or, ~] - author_variant: - - [onephrase, 100] - - [and, 50] - - [or, ~] - author2_variant: - - [onephrase, 100] - - [and, 50] - - [or, ~] - -ISN: - DismaxFields: - - isbn - - issn - - ismn - QueryFields: - issn: - - [and, 100] - - [or, ~] - isbn: - - [and, 100] - - [or, ~] - ismn: - - [and, 100] - - [or, ~] - -Signatur: -# DismaxParams: -# - [mm, 0] -# DismaxFields: -# - callnumber_ISIL - QueryFields: - callnumber_ISIL: - - [onephrase, 1000] - - [and, 100] - - [or, ~] - -Barcode: -# DismaxParams: -# - [mm, 0] -# DismaxFields: -# - barcode_ISIL - QueryFields: - barcode_ISIL: - - [onephrase, 1000] - - [and, 100] - - [or, ~] - -Subject: - DismaxFields: - - topic_unstemmed^150 - - topic^100 - - topic_id^100 - - topic_ref^100 - #- geographic^50 - #- genre^50 - #- era - QueryFields: - topic_unstemmed: - - [onephrase, 350] - - [and, 150] - - [or, ~] - topic: - - [onephrase, 300] - - [and, 100] - - [or, ~] - topic_ref: - - [onephrase, 100] - - [and, 50] - - [or, ~] - topic_id: - - [onephrase, 100] - - [and, 50] - - [or, ~] - #- geographic: - # - [onephrase, 300] - # - [and, 100] - # - [or, ~] - #- genre: - # - [onephrase, 300] - # - [and, 100] - # - [or, ~] - #- era: - # - [and, 100] - # - [or, ~] -# ExactSettings: -# DismaxFields: -# - topic_unstemmed^150 -# QueryFields: -# - topic_unstemmed: -# - [onephrase, 350] -# - [and, 150] -# - [or, ~] - -# This field definition is a compromise that supports both journal-level and -# article-level data. The disadvantage is that hits in article titles will -# be mixed in. If you are building a purely article-oriented index, you should -# customize this to remove all of the title_* fields and focus entirely on the -# container_title field. -JournalTitle: - DismaxFields: - - title_short^500 - - title_full_unstemmed^450 - - title_full^400 - - title^300 - - container_title^250 - - title_alt^200 - - title_new^100 - - title_old - - series^100 - - series2 - QueryFields: - title_short: - - [onephrase, 500] - title_full_unstemmed: - - [onephrase, 450] - - [and, 400] - title_full: - - [onephrase, 400] - title: - - [onephrase, 300] - - [and, 250] - container_title: - - [onephrase, 275] - - [and, 225] - title_alt: - - [and, 200] - title_new: - - [and, 100] - title_old: - - [and, ~] - series: - - [onephrase, 100] - - [and, 50] - series2: - - [onephrase, 50] - - [and , ~] - FilterQuery: "format:Journal OR format:Article OR format:ElectronicBookPart" -# ExactSettings: -# DismaxFields: -# - title_full_unstemmed^450 -# QueryFields: -# - title_full_unstemmed: -# - [onephrase, 450] -# - [and, 400] -# FilterQuery: "format:Journal OR format:Article" - -Title: - DismaxParams: - - [mm, 3] - - [bf , ord(publishDateSort)^10] - DismaxFields: -# - title_sub^200 -# - title_short^300 - - title_full_unstemmed^150 - - title_full^100 - - title^900 - - title_alt^200 - - title_new^100 - - title_old - - title_orig^400 - - series^100 - - series2 - - series_orig^100 - QueryFields: - title_short: - - [onephrase, 500] - title_full_unstemmed: - - [onephrase, 150] - - [and, 100] - title_full: - - [onephrase, 100] - title: - - [onephrase, 300] - - [and, 250] - title_alt: - - [and, 200] - title_new: - - [and, 100] - title_old: - - [and, ~] - title_orig: - - [onephrase, 500] - - [and, 200] - series: - - [onephrase, 100] - - [and, 50] - series2: - - [onephrase, 50] - - [and , ~] - series_orig: - - [onephrase, 100] - - [and, 50] -# ExactSettings: -# DismaxFields: -# - title_full_unstemmed^450 -# QueryFields: -# - title_full_unstemmed: -# - [onephrase, 450] -# - [and, 400] - -Series: - DismaxFields: - - series^100 - - series2 - - series_orig^100 - QueryFields: - series: - - [onephrase, 500] - - [and, 200] - - [or, 100] - series2: - - [onephrase, 50] - - [and, 50] - - [or, ~] - series_orig: - - [onephrase, 500] - - [and, 200] - - [or, 100] - -Series2: - DismaxFields: - - series2 - QueryFields: - series2: - - [onephrase, 200] - - [and, 50] - -AllFields: - DismaxParams: - - [mm, 3] - - [bf , ord(publishDateSort)^10] -# - [bf , "if(exists(query({!v='source_id:0'})),10,1)^1000"] - - [bf, "if(exists(query({!v='access_facet:Local*'})),10,1)^1000"] - DismaxFields: - - title_short^1000 - - title_full_unstemmed^1000 - - title_full^400 - - title^500 - - title_alt^200 - - title_new^100 - - title_orig^500 - - series^50 - - series2^30 - - series_orig^50 - - author^500 - - author_fuller^150 - - author_corporate^300 - - author2^400 - - author_corporate2^100 - - author_ref^500 - - author_orig^300 - - author2_orig^300 - - author_corporate_orig^300 - - author_corporate2_orig^100 - - topic_ref^10 - - contents^10 - - topic_unstemmed^15 - - topic^10 - - geographic^10 - - genre^10 - - rvk_label - - allfields_unstemmed^10 - - allfields - - fulltext - - isbn - - issn - - ismn - - QueryFields: - 0: - 0: - - OR - - 50 - title_short: - - [onephrase, 1000] - title_full_unstemmed: - - [onephrase, 1000] - - [and, 500] - title_full: - - [onephrase, 400] - title: - - [onephrase, 300] - - [and, 250] - title_alt: - - [and, 200] - title_new: - - [and, 100] - title_orig: - - [onephrase, 500] - - [and, 400] - series: - - [onephrase, 300] - - [and, 100] - series2: - - [and, 30] - series_orig: - - [onephrase, 200] - - [and, 100] - author: - - [onephrase, 500] - - [and, 250] - author_fuller: - - [onephrase, 150] - - [and, 125] - author_ref: - - [onephrase, 250] - - [and, 250] - - [or, 250] - author_orig: - - [onephrase, 500] - - [and, 250] - author2_orig: - - [and, 50] - author_corporate_orig: - - [onephrase, 500] - - [and, 400] - author_corporate2_orig: - - [and, 50] - author_corporate: - - [onephrase, 500] - - [and, 400] - author2: - - [and, 50] - author_additional: - - [and, 50] - author_corporate2: - - [and, 50] - contents: - - [and, 10] - topic_unstemmed: - - [onephrase, 55] - - [and, 50] - topic: - - [onephrase, 50] - topic_ref: - - [onephrase, 10] - - [and, 5] - - [or, 5] - topic_id: - - [onephrase, 50] - - [and, 25] - allfields_unstemmed: - - [or, 10] -# fulltext_unstemmed: -# - [or, 10] - allfields: - - [or, ~] - fulltext: - - [or, ~] -# description: -# - [or, ~] - rvk_label: - - [onephrase, 500] - - [and, 250] - - [or, 250] - isbn: - - [onephrase, 500] - issn: - - [onephrase, 500] - ismn: - - [onephrase, 500] - imprint: - - [onephrase, 500] - -# ExactSettings: -# DismaxFields: -# - title_full_unstemmed^600 -# - topic_unstemmed^550 -# - allfields_unstemmed^10 -# - fulltext_unstemmed^10 -# - isbn -# - issn -# QueryFields: -# title_full_unstemmed: -# - [onephrase, 600] -# - [and, 500] -# topic_unstemmed: -# - [onephrase, 550] -# - [and, 500] -# allfields_unstemmed: -# - [or, 10] -# fulltext_unstemmed: -# - [or, 10] -# isbn: -# - [onephrase, ~] -# issn: -# - [onephrase, ~] - -# These are advanced searches that never use Dismax: -id: - QueryFields: - id: - - [onephrase, ~] - -ParentID: - QueryFields: - hierarchy_parent_id: - - [onephrase, ~] - -# Fields for exact matches originating from alphabetic browse -ids: - QueryFields: - id: - - [or, ~] - -TopicBrowse: - QueryFields: - topic_browse: - - [onephrase, ~] - -AuthorBrowse: - QueryFields: - author_browse: - - [onephrase, ~] - -TitleBrowse: - QueryFields: - title_full: - - [onephrase, ~] - -DeweyBrowse: - QueryFields: - dewey-raw: - - [onephrase, ~] - -LccBrowse: - QueryFields: - callnumber-a: - - [onephrase, ~] - - - -# CallNumber: - # We use two similar munges here -- one for exact matches, which will get - # a very high boost factor, and one for left-anchored wildcard searches, - # which will return a larger number of hits at a lower boost. - #CustomMunge: - #callnumber_exact: - #- [uppercase] - # Strip whitespace and quotes: - #- [preg_replace, '/[ "]/', ''] - # Escape colons (unescape first to avoid double-escapes): - #- [preg_replace, '/(\\\:)/', ':'] - #- [preg_replace, '/:/', '\:'] - # Strip pre-existing trailing asterisks: - #- [preg_replace, '/\*+$/', ''] - #callnumber_fuzzy: - #- [uppercase] - # Strip whitespace and quotes: - #- [preg_replace, '/[ "]/', ''] - # Escape colons (unescape first to avoid double-escapes): - #- [preg_replace, '/(\\\:)/', ':'] - #- [preg_replace, '/:/', '\:'] - # Strip pre-existing trailing asterisks, then add a new one: - #- [preg_replace, '/\*+$/', ''] - #- [append, "*"] - QueryFields: - callnumber-search: - - [callnumber_exact, 1000] - - [callnumber_fuzzy, ~] - dewey-search: - - [callnumber_exact, 1000] - - [callnumber_fuzzy, ~] - -publisher: - DismaxFields: - - publisher^100 - QueryFields: - publisher: - - [and, 100] - - [or, ~] - -year: - DismaxFields: - - publishDate^100 - QueryFields: - publishDate: - - [and, 100] - - [or, ~] - -language: - QueryFields: - language: - - [and, ~] - -toc: - DismaxFields: - - contents^100 - QueryFields: - contents: - - [and, 100] - - [or, ~] - -topic: - QueryFields: - topic: - - [and, 50] - topic_facet: - - [and, ~] - -geographic: - QueryFields: - geographic: - - [and, 50] - geographic_facet: - - [and, ~] - -genre: - QueryFields: - genre: - - [and, 50] - genre_facet: - - [and, ~] - -era: - QueryFields: - era: - - [and, ~] - -oclc_num: - CustomMunge: - oclc_num: - - [preg_replace, "/[^0-9]/", ""] - # trim leading zeroes: - - [preg_replace, "/^0*/", ""] - QueryFields: - oclc_num: - - [oclc_num, ~] - -rvk: - DismaxFields: - - rvk_facet^100 - QueryFields: - rvk_facet: - - [and, 50] - - [or, 50] - -rvk_path: - QueryFields: - rvk_path: - - [onephrase, ~] - -multipart: - DismaxFields: - - multipart_link^100 - QueryFields: - multipart_link: - - [and, 50] - - [or, 50] - -titleUniform: - QueryFields: - title_id_str_mv: - - [onephrase, ~] \ No newline at end of file +"@parent_yaml": "../../../config/vufind/searchspecs.yaml" \ No newline at end of file diff --git a/local/config/vufind/searches.ini b/local/config/vufind/searches.ini index f6d6e5cdecfcffb5dea73ac3ea3062502d7bed2a..1755366b266c17057da838ee76691cda9bb653ed 100644 --- a/local/config/vufind/searches.ini +++ b/local/config/vufind/searches.ini @@ -98,11 +98,6 @@ retain_filters_by_default = true ;default_filters[] = "institution:MyInstitution" ;default_filters[] = "(format:Book AND institution:MyInstitution)" -; the escaped_colon_searches is used by a listener on the search-pre event -; registered by the MungerInjectionFactory. This listener masks colons in the query string with a backslash -; whenever the search handler is one of the following -escaped_colon_searches[] = "Signatur" - [Cache] ; This controls whether the parsed searchspecs.yaml file will be stored to ; improve search performance; legal options are APC (use APC cache), File (store @@ -677,3 +672,13 @@ height = 320 ;params = "qf=title,title_short,callnumber-label,topic,language,author,publishDate mintf=1 mindf=1"; ; This setting can be used to limit the maximum number of suggestions. Default is 5. ;count = 5 + +; PreMunge is used by a listener on the search-pre event +; registered by the MungerInjectionDelegatorFactory. This listener applies +; the regex patterns as configured for the search handlers +; use the following configuration to define pattern and replacement as used in @see preg_replace +; Handler[pattern] = regex_pattern with delimiters (e.g. slashes at beginning and end) +; Handler[replace] = replacement +[PreMunge] +Signatur[pattern] = '/(?<=\:)\s/' +Signatur[replace] = '\ ' \ No newline at end of file diff --git a/local/config/vufind/searchspecs.yaml b/local/config/vufind/searchspecs.yaml index 04693c444c6f66c5caf3995f329d85a1e26c05da..74bfe85f84c160244469c9db2744ce53ca7ea7c8 100644 --- a/local/config/vufind/searchspecs.yaml +++ b/local/config/vufind/searchspecs.yaml @@ -150,6 +150,8 @@ # See the CallNumber search below for an example of custom munging in action. #----------------------------------------------------------------------------------- +"@parent_yaml": "../../../config/vufind/searchspecs.yaml" + # These searches use Dismax when possible: Author: DismaxParams: @@ -352,7 +354,7 @@ JournalTitle: title_new: - [and, 100] title_old: - - [and, ~] + - [and, ~] series: - [onephrase, 100] - [and, 50] @@ -455,7 +457,7 @@ AllFields: - [mm, 3] - [bf , ord(publishDateSort)^10] # - [bf , "if(exists(query({!v='source_id:0'})),10,1)^1000"] - - [bf, "if(exists(query({!v='access_facet:Local*'})),10,1)^1000"] + - [bf, "if(exists(query({!v='facet_avail:Local*'})),10,1)^1000"] DismaxFields: - title_short^1000 - title_full_unstemmed^1000 @@ -611,147 +613,12 @@ AllFields: # issn: # - [onephrase, ~] -# These are advanced searches that never use Dismax: -id: - QueryFields: - id: - - [onephrase, ~] - -ParentID: - QueryFields: - hierarchy_parent_id: - - [onephrase, ~] - -# Fields for exact matches originating from alphabetic browse -ids: - QueryFields: - id: - - [or, ~] - -TopicBrowse: - QueryFields: - topic_browse: - - [onephrase, ~] - -AuthorBrowse: - QueryFields: - author_browse: - - [onephrase, ~] - -TitleBrowse: - QueryFields: - title_full: - - [onephrase, ~] - -DeweyBrowse: - QueryFields: - dewey-raw: - - [onephrase, ~] - +# These are advanced searches that never use Dismax (finc customized): LccBrowse: QueryFields: callnumber-a: - [onephrase, ~] - - -# CallNumber: - # We use two similar munges here -- one for exact matches, which will get - # a very high boost factor, and one for left-anchored wildcard searches, - # which will return a larger number of hits at a lower boost. - #CustomMunge: - #callnumber_exact: - #- [uppercase] - # Strip whitespace and quotes: - #- [preg_replace, '/[ "]/', ''] - # Escape colons (unescape first to avoid double-escapes): - #- [preg_replace, '/(\\\:)/', ':'] - #- [preg_replace, '/:/', '\:'] - # Strip pre-existing trailing asterisks: - #- [preg_replace, '/\*+$/', ''] - #callnumber_fuzzy: - #- [uppercase] - # Strip whitespace and quotes: - #- [preg_replace, '/[ "]/', ''] - # Escape colons (unescape first to avoid double-escapes): - #- [preg_replace, '/(\\\:)/', ':'] - #- [preg_replace, '/:/', '\:'] - # Strip pre-existing trailing asterisks, then add a new one: - #- [preg_replace, '/\*+$/', ''] - #- [append, "*"] - #QueryFields: - #callnumber-search: - #- [callnumber_exact, 1000] - #- [callnumber_fuzzy, ~] - #dewey-search: - #- [callnumber_exact, 1000] - #- [callnumber_fuzzy, ~] - -publisher: - DismaxFields: - - publisher^100 - QueryFields: - publisher: - - [and, 100] - - [or, ~] - -year: - DismaxFields: - - publishDate^100 - QueryFields: - publishDate: - - [and, 100] - - [or, ~] - -language: - QueryFields: - language: - - [and, ~] - -toc: - DismaxFields: - - contents^100 - QueryFields: - contents: - - [and, 100] - - [or, ~] - -topic: - QueryFields: - topic: - - [and, 50] - topic_facet: - - [and, ~] - -geographic: - QueryFields: - geographic: - - [and, 50] - geographic_facet: - - [and, ~] - -genre: - QueryFields: - genre: - - [and, 50] - genre_facet: - - [and, ~] - -era: - QueryFields: - era: - - [and, ~] - -oclc_num: - CustomMunge: - oclc_num: - - [preg_replace, "/[^0-9]/", ""] - # trim leading zeroes: - - [preg_replace, "/^0*/", ""] - QueryFields: - oclc_num: - - [oclc_num, ~] - rvk: DismaxFields: - rvk_facet^100 diff --git a/local/languages/de.ini b/local/languages/de.ini index 6ccede79bbadd7e71fe3d3af92a8cacbde920a62..33b2bf08b837d996024c0eed35d1684c547ef0bc 100644 --- a/local/languages/de.ini +++ b/local/languages/de.ini @@ -1020,7 +1020,7 @@ DE-1972 = "Robert Schumann Hochschule Düsseldorf" DE-Ch1 = "Technische Universität Chemnitz" DE-D13 = "Staatliche Kunstsammlungen Dresden" DE-D40 = "Staatliche Ethnographische Sammlungen Sachsen, Museum für Völkerkunde Dresden" -DE-D117 = "Hochschule für Musik - Carl Maria von Weber - Dresden" +DE-D117 = "Hochschule für Musik 'Carl Maria von Weber' Dresden" DE-Mit1 = "Hochschule Mittweida" DE-L152 = "Hochschule für Musik und Theater 'Felix Mendelssohn Bartholdy' Leipzig" DE-L189 = "Hochschule für Technik, Wirtschaft und Kultur Leipzig" @@ -1029,7 +1029,6 @@ DE-L242 = "Hochschule für Grafik und Buchkunst Leipzig" DE-L328 = "Halle 14 Kunstbibliothek Leipzig" DE-L330 = "GfZK Leipzig" DE-Kn38 = "Bibliothek der HfMT Köln" -DE-Wim8 = "Hochschule für Musik 'Franz Liszt' Weimar" DE-Zi4 = "Hochschule Zittau/Görlitz" DE-Zwi2 = "Westsächsische Hochschule Zwickau" Dresden SLUB = "Sächsische Landesbibliothek & Staats- und Universitätsbibliothek Dresden (SLUB)" diff --git a/local/languages/en.ini b/local/languages/en.ini index 67e9b0c1a6e40a47dbfa7ea4cb3390a5745ffbd5..2bb335bd894b27c953b63fc72515b61c5125260b 100644 --- a/local/languages/en.ini +++ b/local/languages/en.ini @@ -980,7 +980,7 @@ DE-1972 = "Robert Schumann Academy Düsseldorf" DE-Ch1 = "Technische Universität Chemnitz" DE-D13 = "The Staatliche Kunstsammlungen Dresden (Dresden State Art Collections) " DE-D40 = "Staatliche Ethnographische Sammlungen Sachsen, Museum für Völkerkunde Dresden" -DE-D117 = "Hochschule für Musik - Carl Maria von Weber - Dresden" +DE-D117 = "Hochschule für Musik 'Carl Maria von Weber' Dresden" DE-Mit1 = "Hochschule Mittweida" DE-L152 = "University of Music and Theatre 'Felix Mendelssohn Bartholdy' Leipzig" DE-L189 = "Leipzig University of Applied Sciences" @@ -989,7 +989,6 @@ DE-L242 = "Academy of Visual Arts Leipzig" DE-L328 = "Halle 14 Art Library Leipzig" DE-L330 = "GfZK Leipzig" DE-Kn38 = "Library of HfMT Köln" -DE-Wim8 = "The Liszt School of Music Weimar" DE-Zi4 = "Zittau/Görlitz University of Applied Sciences" DE-Zwi2 = "University of Applied Sciences Zwickau" ;Dresden SLUB = "Sächsische Landesbibliothek & Staats- und Universitätsbibliothek Dresden (SLUB)" diff --git a/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php b/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php index 96a7ca446394680744c9a89ddf866096a8cf2003..5d6f7c179bac46e2e9a432520de03eca80c3b484 100644 --- a/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php +++ b/module/finc/src/finc/AjaxHandler/GetAdditionalAccountInfo.php @@ -61,16 +61,16 @@ class GetAdditionalAccountInfo extends \VuFind\AjaxHandler\AbstractIlsAndUserAct try { $patron = $this->ilsAuthenticator->storedCatalogLogin(); if ($patron) { - $additionalAccountInfos = []; - $additionalAccountInfos['countViewItems'] = $this->ils->countItems( + $countViewItems = $this->ils->countItems( $viewsToCount, $patron ); - $additionalAccountInfos['countFines'] = $this->ils->getFinesTotal( + $countFines = $this->ils->getFinesTotal( $patron ); - return $this->formatResponse(compact('additionalAccountInfos')); + + return $this->formatResponse(compact('countViewItems','countFines')); } } catch (\Exception $e) { // Do nothing -- just fail through to the error message below. diff --git a/module/finc/src/finc/Controller/MyResearchController.php b/module/finc/src/finc/Controller/MyResearchController.php index a48c096b571e7f6777c69da7c13b225b2f246bac..ff311d42b26feb415d8d54625b3d9da985717ae6 100644 --- a/module/finc/src/finc/Controller/MyResearchController.php +++ b/module/finc/src/finc/Controller/MyResearchController.php @@ -26,9 +26,11 @@ * @link https://vufind.org Main Site */ namespace finc\Controller; -use Zend\Log\LoggerAwareInterface as LoggerAwareInterface, - Zend\Mvc\MvcEvent as MvcEvent; + +use VuFind\Search\RecommendListener; +use Zend\Log\LoggerAwareInterface as LoggerAwareInterface; use Zend\Mvc\Controller\Plugin\Url; +use Zend\Mvc\MvcEvent as MvcEvent; /** * Controller for the user account area. @@ -62,10 +64,131 @@ class MyResearchController extends \VuFind\Controller\MyResearchController imple //we achieve that by forcing the follow-up url to be the redirect //but watch out, the redirect via followup will only work if the redirect url //is schemed correctly - if (!is_string($redirect)) $this->flashMessenger()->addErrorMessage('Invalid data type for redirect'); - elseif (!preg_match('/^https?\:\/\//',$redirect)) $this->flashMessenger()->addErrorMessage('Invalid Redirect: '.$redirect); - else $this->followup()->store(['finc-redirect' => $redirect]); + if (!is_string($redirect)) { + $this->flashMessenger()->addErrorMessage('Invalid data type for redirect'); + } elseif (!preg_match('/^https?\:\/\//', $redirect)) { + $this->flashMessenger()->addErrorMessage('Invalid Redirect: ' . $redirect); + } else { + $this->followup()->store(['finc-redirect' => $redirect]); + } } return parent::onDispatch($e); } + + /** + * Send user's saved favorites from a particular list to the view + * + * @return mixed + */ + public function mylistAction() + { + // Fail if lists are disabled: + if (!$this->listsEnabled()) { + throw new ForbiddenException('Lists disabled'); + } + + // Check for "delete item" request; parameter may be in GET or POST depending + // on calling context. + $deleteId = $this->params()->fromPost( + 'delete', $this->params()->fromQuery('delete') + ); + if ($deleteId) { + $deleteSource = $this->params()->fromPost( + 'source', + $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND) + ); + // If the user already confirmed the operation, perform the delete now; + // otherwise prompt for confirmation: + $confirm = $this->params()->fromPost( + 'confirm', $this->params()->fromQuery('confirm') + ); + if ($confirm) { + $layout = $this->params()->fromPost( + 'layout', $this->params()->fromQuery('layout') + ); + + /* #17712 not necessary to fetch items after deleting by ajax */ + $success = $this->performDeleteFavorite($deleteId, $deleteSource); + if ($layout === 'lightbox' || $success !== true) { + return $success; + } + + } else { + return $this->confirmDeleteFavorite($deleteId, $deleteSource); + } + } + + // If we got this far, we just need to display the favorites: + try { + $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); + + // We want to merge together GET, POST and route parameters to + // initialize our search object: + $request = $this->getRequest()->getQuery()->toArray() + + $this->getRequest()->getPost()->toArray() + + ['id' => $this->params()->fromRoute('id')]; + + // Set up listener for recommendations: + $rManager = $this->serviceLocator + ->get('VuFind\Recommend\PluginManager'); + $setupCallback = function ($runner, $params, $searchId) use ($rManager) { + $listener = new RecommendListener($rManager, $searchId); + $listener->setConfig( + $params->getOptions()->getRecommendationSettings() + ); + $listener->attach($runner->getEventManager()->getSharedManager()); + }; + + $results = $runner->run($request, 'Favorites', $setupCallback); + return $this->createViewModel( + ['params' => $results->getParams(), 'results' => $results] + ); + } catch (ListPermissionException $e) { + if (!$this->getUser()) { + return $this->forceLogin(); + } + throw $e; + } + } + + /** + * Delete record + * Overrides Vufind method without flash messages + * + * @param string $id ID of record to delete + * @param string $source Source of record to delete + * + * @return bool|mixed + * + * @throws \Exception + **/ + public function performDeleteFavorite($id, $source) + { + // Force login: + $user = $this->getUser(); + if (!$user) { + return $this->forceLogin(); + } + + // Load/check incoming parameters: + $listID = $this->params()->fromRoute('id'); + $listID = empty($listID) ? null : $listID; + if (empty($id)) { + throw new \Exception('Cannot delete empty ID!'); + } + + // Perform delete #17712 without setting messages + if (null !== $listID) { + // ...Specific List + $table = $this->getTable('UserList'); + $list = $table->getExisting($listID); + $list->removeResourcesById($user, [$id], $source); + } else { + // ...My Favorites + $user->removeResourcesById([$id], $source); + } + + // All done -- return true to indicate success. + return true; + } } diff --git a/module/finc/src/finc/Hierarchy/TreeDataFormatter/NoCollections.php b/module/finc/src/finc/Hierarchy/TreeDataFormatter/NoCollections.php index 5d1fbf8a3538b07a70a246d491df917b057a3d0b..f3a54bc14215f7a6d3cefff826321261fe2330d2 100644 --- a/module/finc/src/finc/Hierarchy/TreeDataFormatter/NoCollections.php +++ b/module/finc/src/finc/Hierarchy/TreeDataFormatter/NoCollections.php @@ -64,4 +64,29 @@ class NoCollections extends \VuFind\Hierarchy\TreeDataFormatter\Json } return parent::pickTitle($record, $parentID); } + + + /** + * 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 strnatcmp($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/finc/src/finc/ILS/Driver/FincLibero.php b/module/finc/src/finc/ILS/Driver/FincLibero.php index f2325e0b0158612d0157af798c1ca1adc290bfad..3448b5bb62a77bd84c8fbf06ac983086bb4d8831 100644 --- a/module/finc/src/finc/ILS/Driver/FincLibero.php +++ b/module/finc/src/finc/ILS/Driver/FincLibero.php @@ -691,4 +691,14 @@ class FincLibero extends FincILS implements TranslatorAwareInterface return $pickUpLocations; } + /** + * TODO: check status of this function + * de_15 -> getBoundItemId() vs. de_l152 -> getBoundItemInfo() + * @param $item + * @return array + */ + protected function getBoundItemId($item) + { + return []; + } } diff --git a/module/finc/src/finc/ILS/Driver/PAIA.php b/module/finc/src/finc/ILS/Driver/PAIA.php index 579991694b2ae73935a3f6e7c6ee52e7ce069054..6eb349d54e3b6137e2f748bb063a95cb8fe64c1f 100644 --- a/module/finc/src/finc/ILS/Driver/PAIA.php +++ b/module/finc/src/finc/ILS/Driver/PAIA.php @@ -71,6 +71,11 @@ class PAIA extends \VuFind\ILS\Driver\PAIA protected $notificationsPrefix; + /** + * @var array + */ + protected $conditions; + /** * Constructor * @@ -97,6 +102,10 @@ class PAIA extends \VuFind\ILS\Driver\PAIA if (isset($this->config['PAIA']['paiaNotificationsPrefix'])) { $this->notificationsPrefix = $this->config['PAIA']['paiaNotificationsPrefix']; } + + if (isset($this->config['PAIA']['paiaConditions'])) { + $this->conditions = $this->config['PAIA']['paiaConditions']; + } } /** @@ -972,6 +981,9 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result['upc'] = null; */ + /* #17508 read optional PAIA information like pickupbranch */ + $this->mapOptions($doc, $result); + $results[] = $result; } return $results; @@ -1511,4 +1523,66 @@ class PAIA extends \VuFind\ILS\Driver\PAIA // return TRUE on success return true; } + + /*** + * finds conditions in PAIA items array and adds it as options in finc converted items array + * + * Conditions according to PAIA default are only intended for request, renew or cancel + * BUT here used for items too, therefore should be only one option per condition + * see: http://gbv.github.io/paia/paia.html#conditions-and-confirmations + * + * @param array $doc Array of PAIA input to be mapped + * @param array $result Array of PAIA output - called by reference + * @param array|null $configConditions conditions to be considered (optional, if null use config) + * + * @return bool True if any option was found, otherwise false. + */ + protected function mapOptions(array $doc, array &$result, array $configConditions = null): bool + { + $configConditions = $configConditions ?? $this->conditions ?? []; + + foreach ($configConditions as $configCondition) { + if ($optionsValues = $doc["condition"][$configCondition]["option"] ?? []) { + $found = $result["options"][$configCondition] = $optionsValues; + } + } + + return !empty($found); + } + + /** + * Get (first) option for given condition and valid options from finc converted PAIA result + * + * @param string $condition + * @param array $result + * @param boolean $onlyFirst + * @param array $validOptions + * + * @return array of option values: each either valid id or label or null if none set + */ + public function getOptions(string $condition, array $result, bool $onlyFirst, array $validOptions = null): array + { + $retval = []; + if (in_array($condition, $this->conditions)) { + $options = $result['options'][$condition] ?? []; + foreach ($options as $option) { + if (!$validOptions) { + $retval[] = $option['about'] ?? $option['id'] ?? null; + if ($onlyFirst) { + break; + } + } else { + foreach ($validOptions as $validOption) { + if (array_intersect($validOption, $option)) { + $retval[] = $option['id'] ?? $option['about'] ?? null; + if ($onlyFirst) { + break 2; + } + } + } + } + } + } + return $retval; + } } diff --git a/module/finc/src/finc/RecordDriver/SolrMarc.php b/module/finc/src/finc/RecordDriver/SolrMarc.php index cc56769933055587c5f099eae4ac05955f508ab2..fb29e876891f8680c6d61ae52edc790219b03fd4 100644 --- a/module/finc/src/finc/RecordDriver/SolrMarc.php +++ b/module/finc/src/finc/RecordDriver/SolrMarc.php @@ -506,13 +506,27 @@ class SolrMarc extends SolrDefault $toc = []; foreach ($fields as $field) { $subfields = $field->getSubfields(); - foreach ($subfields as $subfield) { - // Break the string into appropriate chunks, filtering empty strings, - // and merge them into return array: - $toc = array_merge( - $toc, - array_filter(explode('--', $subfield->getData()), 'trim') - ); + $ind2 = $field->getIndicator(2); + if ($ind2 === '0') { + // advanced entries with + // $t - Title + // $r - Statement of responsibility + $tocSubfields = []; + foreach ($subfields as $subfield) { + $tocSubfields[] = $subfield->getData(); + } + $toc[] = implode(' \ ', $tocSubfields); + } elseif ($ind2 === '#') { + // basic entries + // sub-entries delimited by " -- " + foreach ($subfields as $subfield) { + // Break the string into appropriate chunks, filtering empty strings, + // and merge them into return array: + $toc = array_merge( + $toc, + array_filter(explode('--', $subfield->getData()), 'trim') + ); + } } } return $toc; diff --git a/module/finc/src/finc/RecordDriver/SolrMarcFinc.php b/module/finc/src/finc/RecordDriver/SolrMarcFinc.php index c5de93123f0f84059613535a4ec5f7bf88876dd2..d9e2976d91d20ef81f53c057574bd1dd186131f9 100644 --- a/module/finc/src/finc/RecordDriver/SolrMarcFinc.php +++ b/module/finc/src/finc/RecordDriver/SolrMarcFinc.php @@ -57,7 +57,7 @@ class SolrMarcFinc extends SolrMarc /** * pattern to identify normed data set of GND */ - const GND_PATTERN = '/^(\(DE-588\))(\d+)(\w|)/'; + const GND_PATTERN = '/^(\(DE-588\))(\d+)(\S*)$/'; /** * List of isil of institution diff --git a/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php b/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php index a8e7c73152d7e29db3d4d3dc5a85757aa709e8b7..88a0c6846b618f6f94a4fdfebb2c037e637dbab6 100644 --- a/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php +++ b/module/finc/src/finc/RecordDriver/SolrMarcFincTrait.php @@ -86,7 +86,8 @@ trait SolrMarcFincTrait // Which fields/subfields should we check for URLs? $fieldsToCheck = [ '856' => ['u'], // Standard URL - '555' => ['a'] // Cumulative index/finding aids + '555' => ['a'], // Cumulative index/finding aids + '609' => ['a'] ]; foreach ($fieldsToCheck as $field => $subfields) { @@ -115,18 +116,43 @@ trait SolrMarcFincTrait if ($address) { $address = $address->getData(); - $tmpArr = []; + //$tmpArr = []; // Is there a description? If not, just use the URL // itself. - foreach (['y', '3', 'z', 'x'] as $current) { + /*foreach (['y', '3', 'z', 'x'] as $current) { $desc = $url->getSubfield($current); if ($desc) { $desc = $desc->getData(); $tmpArr[] = $desc; } + }*/ + + $desc = false; + if ($url->getSubfield('y')) { + $desc = $url->getSubfield('y'); + } elseif ($url->getSubfield('n')) { + $desc = $url->getSubfield('n'); + } elseif ($url->getSubfield('3')) { + $desc = $url->getSubfield('3'); } - $tmpArr = array_unique($tmpArr); - $desc = implode(', ', $tmpArr); + + + if ($desc) { + $desc = $desc->getData(); + $desc = strlen($desc) > 3 ? + $desc : 'Online Information'; + + if (isset($this->mainConfig->UrlDesc->desc)) { + $tmpDesc = $this->mainConfig->UrlDesc->desc->toArray(); + if (in_array($desc, $tmpDesc)) { + $desc = 'Online Information'; + } + } + } else { + $desc = 'Online Information'; + } + //$tmpArr = array_unique($tmpArr); + //$desc = implode(', ', $tmpArr); // If no description take url as description // For 856[40] url denoting resource itself @@ -149,16 +175,55 @@ trait SolrMarcFincTrait $retVal ) ) { - $retVal[] = ['url' => $address, 'desc' => $desc]; + $retVal[] = [ + 'url' => $address, 'desc' => $desc, + 'indicators' => $indicator1.$indicator2 + ]; } } } + if ($field == '609') { + // 609 cpntains Gallica (BNF) info. Cf. #10279. Included here via #15042 + $data = $url->getSubfield('a')->getData(); + if (stripos($data, 'http') === 0) { + $retVal[] = ['url' => $data, 'desc' => '', 'indicators' => '40']; + } + } } } } return $retVal; } + + /** + * Return a description about volumes in stock via consortial field + * with subfield $h. + * Currently only used in DE-L152. + * + * @return array + * @access public + */ + public function getVolumesInStock() + { + $volumesInStock = $this->getFieldArray($this->getLocalMarcFieldOfLibrary(), ['h']); + return array_filter($volumesInStock); + } + + /** + * Return a user relevant comment about volumes in stock via consortial field + * with subfield $i. + * Currently only used in DE-L152. + * + * @return array + * @access public + */ + public function getVolumesCommentInStock() + { + $volumesCommentInStock = $this->getFieldArray($this->getLocalMarcFieldOfLibrary(), ['i']); + return array_filter($volumesCommentInStock); + } + /** * Checks if the record is an EBL record (as defined in config.ini section * [Ebl]->product_sigel). Refs #8055 #9634 @@ -766,6 +831,28 @@ trait SolrMarcFincTrait return $retval; } + /** + * Get additional entry meeting or jurisdiction names. + * + * @return array $retval + * @link https://intern.finc.info/issues/9369 + */ + public function getAddedEntryMeetingNames() + { + $retval = []; + + $fields = $this->getMarcRecord()->getFields('711'); + if (!$fields) { + return []; + } + foreach ($fields as $key => $field) { + if ($q = $field->getSubfield('a')) { + $retval[$key] = $q->getData(); + } + } + return $retval; + } + /** * Get all local class subjects. First realization for HGB. Refs #2626 * diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php index 134d1a0dad6f9a6697eeb6f36ff4217bb1188263..37106e439210a6b3eae83e5838085b769c1fbf15 100644 --- a/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php +++ b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php @@ -45,6 +45,28 @@ namespace finc\Role\PermissionProvider; */ class IpRangeFoFor extends \VuFind\Role\PermissionProvider\IpRange { + + /** + * returns remote address based on eventual proxy headers + * + * @return string + */ + private function getRemoteAddr() { + // a list of ips the request is forwarded for - first is latest + $HttpXForwardedForList = explode(',', $this->request->getServer()->get('HTTP_X_FORWARDED_FOR')); + + if ($ip = array_shift($HttpXForwardedForList)) { + return $ip; + } + + // often provided by nginx-reverse-proxies, should be used since its the nature of the value + if ($ip = $this->request->getServer()->get('HTTP_X_REAL_IP')) { + return $ip; + } + + return $this->request->getServer()->get('REMOTE_ADDR'); + } + /** * Return an array of roles which may be granted the permission based on * the options. @@ -57,11 +79,10 @@ class IpRangeFoFor extends \VuFind\Role\PermissionProvider\IpRange */ public function getPermissions($options) { + + // Check if any regex matches.... - $ip = $this->request->getServer()->get('HTTP_X_FORWARDED_FOR') != null - ? $this->request->getServer()->get('HTTP_X_FORWARDED_FOR') - : $this->request->getServer()->get('REMOTE_ADDR'); - if ($this->ipAddressUtils->isInRange($ip, (array)$options)) { + if ($this->ipAddressUtils->isInRange($this->getRemoteAddr(), (array)$options)) { // Match? Grant to all users (guest or logged in). return ['guest', 'loggedin']; } diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php index 07afb30d8f1717f736087d265ef6876b677b042d..c54b27513668296471c0f9843f86463f15f772ac 100644 --- a/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php +++ b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php @@ -22,6 +22,7 @@ * @category VuFind * @package Authorization * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Ulf Seltmann <ulf.seltmann@hmt-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ @@ -33,11 +34,33 @@ namespace finc\Role\PermissionProvider; * @category VuFind * @package Authorization * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Ulf Seltmann <ulf.seltmann@hmt-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page */ class IpRegExFoFor extends \VuFind\Role\PermissionProvider\IpRegEx { + /** + * returns remote address based on eventual proxy headers + * + * @return string + */ + private function getRemoteAddr() { + // a list of ips the request is forwarded for - first is latest + $HttpXForwardedForList = explode(',', $this->request->getServer()->get('HTTP_X_FORWARDED_FOR')); + + if ($ip = array_shift($HttpXForwardedForList)) { + return $ip; + } + + // often provided by nginx-reverse-proxies, should be used since its the nature of the value + if ($ip = $this->request->getServer()->get('HTTP_X_REAL_IP')) { + return $ip; + } + + return $this->request->getServer()->get('REMOTE_ADDR'); + } + /** * Return an array of roles which may be granted the permission based on * the options. @@ -51,9 +74,7 @@ class IpRegExFoFor extends \VuFind\Role\PermissionProvider\IpRegEx public function getPermissions($options) { // Check if any regex matches.... - $ip = $this->request->getServer()->get('HTTP_X_FORWARDED_FOR') != null - ? $this->request->getServer()->get('HTTP_X_FORWARDED_FOR') - : $this->request->getServer()->get('REMOTE_ADDR'); + $ip = $this->getRemoteAddr(); foreach ((array)$options as $current) { if (preg_match($current, $ip)) { // Match? Grant to all users (guest or logged in). diff --git a/module/finc/src/finc/Service/MungerInjectionDelegatorFactory.php b/module/finc/src/finc/Service/MungerInjectionDelegatorFactory.php index 1d65c7b2af5bdcd2a310a71e820c74c1c86059a4..9bc29e20a830a8464dd35d3ca4f6f96f97e3d59c 100644 --- a/module/finc/src/finc/Service/MungerInjectionDelegatorFactory.php +++ b/module/finc/src/finc/Service/MungerInjectionDelegatorFactory.php @@ -56,9 +56,9 @@ class MungerInjectionDelegatorFactory implements DelegatorFactoryInterface protected $instance; /** - * @var array names of search handlers for which colons should be escaped + * @var array configuration for pre-search munging */ - protected $searches_to_escape; + protected $preMungerConfig; /** * @var array shard configuration to register in all queries @@ -86,16 +86,14 @@ class MungerInjectionDelegatorFactory implements DelegatorFactoryInterface $searchConfig = $container->get('VuFind\Config')->get('searches'); $e = $instance->getEventManager()->getSharedManager(); - $handlers = $searchConfig->General->escaped_colon_searches; - if (!empty($handlers)) { - $this->searches_to_escape = $handlers->toArray(); + if ($this->validateMungerConfig($searchConfig)) { $e->attach( 'VuFindSearch', 'pre', function (EventInterface $event) { $params = $event->getParams(); if (isset($params['query'])) { - $params['query'] = $this->escapeColons($params['query']); + $params['query'] = $this->preMunge($params['query']); } } ); @@ -120,26 +118,53 @@ class MungerInjectionDelegatorFactory implements DelegatorFactoryInterface * * @return mixed */ - private function escapeColons($queryOrGroup) + private function preMunge($queryOrGroup) { if ($queryOrGroup instanceof QueryGroup) { $handler = $queryOrGroup->getReducedHandler(); - if (is_null($handler) || in_array($handler, $this->searches_to_escape)) { + if (is_null($handler) || isset($this->preMungerConfig[$handler])) { foreach ($queryOrGroup->getQueries() as $query) { - $this->escapeColons($query); + $this->preMunge($query); } } - } elseif (in_array($queryOrGroup->getHandler(), $this->searches_to_escape)) { + } elseif ($mungerConfig = $this->preMungerConfig[$queryOrGroup->getHandler()] ?? null) { $queryOrGroup->setString( - // mask whitespaces that follow a colon - // that avoids the removal of that very colon via - // \VuFindSearch\Backend\Solr\LuceneSyntaxHelper::normalizeColons - preg_replace('/(?<=\:)\s/', '\ ', $queryOrGroup->getString()) + // apply preg_replace as provided by config + preg_replace($mungerConfig['pattern'], $mungerConfig['replace'], $queryOrGroup->getString()) ); } return $queryOrGroup; } + /** + * @param Config $searchConfig + */ + protected function validateMungerConfig($searchConfig) { + + $config = $searchConfig->PreMunge; + if (empty($config)) { + // an empty configuration is always valid + return null; + } + $preMungerConfig = $config->toArray(); + foreach ($preMungerConfig as $handler => $handlerConfig) { + if (!isset($handlerConfig['pattern']) + || + !isset($handlerConfig['replace']) + ) { + throw new \ConfigurationException("PreMunge configuration for $handler is invalid"); + } + try { + // try preg_replace so the regex engine tells us about more errors + preg_replace($handlerConfig['pattern'], $handlerConfig['replace'], ''); + } catch (\ErrorException $e) { + throw new \ConfigurationException("PreMunge configuration for $handler is invalid. Regex error: ".'"'.$e->getMessage().'"'); + } + } + $this->preMungerConfig = $preMungerConfig; + return true; + } + /** * Event Listener on Search/Pre that registers all configured shards for every * search request diff --git a/module/finc/src/finc/View/Helper/Root/MultiDataFieldsTrait.php b/module/finc/src/finc/View/Helper/Root/MultiDataFieldsTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..be5bd3f42ad61d9ce894e31b91c12c8514082404 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/MultiDataFieldsTrait.php @@ -0,0 +1,137 @@ +<?php +/** + * multi data view helper callback functions + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library, 2020. + * + * 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 Dorian Merz <merz@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace finc\View\Helper\Root; + +/** + * multi data view helper callback functions + * + * @category VuFind + * @package View_Helpers + * @author Dorian Merz <merz@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +trait MultiDataFieldsTrait +{ + /** + * return render details for additionals data set + * @return callback + */ + public function additionals($data, $options) + { + // Sort the data: + $final = []; + foreach ($data as $type => $values) { + $final[] = [ + 'label' => $values['identifier'], + 'values' => [$type => $values], + 'options' => [ + 'pos' => $options['pos'], + 'renderType' => 'RecordDriverTemplate', + 'template' => 'data-additionals.phtml', + ], + ]; + } + return $final; + } + + /** + * return render details for otherRelationshipEntry data set + * @return callback + */ + public function otherRelationShipEntry($data, $options) + { + // Sort the data: + $final = []; + foreach ($data as $type => $values) { + $final[] = [ + 'label' => $values[0]['subject'], + 'values' => [$type => $values], + 'options' => [ + 'pos' => $options['pos'], + 'renderType' => 'RecordDriverTemplate', + 'template' => 'data-otherRelationshipEntry.phtml', + ], + ]; + } + return $final; + } + + /** + * return render details for events data set + * @return callback + */ + public function events($data, $options) + { + // Sort the data: + $final = []; + foreach ($data as $eventType => $values) { + switch ($eventType) { + case 'production': $title = 'expression creation'; break; + case 'publication': $title = 'Time of origin'; break; + default: $title = $eventType; + } + $final[] = [ + 'label' => $title, + 'values' => [$eventType => $values], + 'options' => [ + 'pos' => $options['pos'], + 'renderType' => 'RecordDriverTemplate', + 'template' => 'data-events.phtml', + ], + ]; + } + return $final; + } + + + /** + * return render details for titleUniform data set + * @return callback + */ + public function isbnIssns($data, $options, $driver) + { + // Sort the data: + $final = []; + foreach ($data as $type => $values) { + $final[] = [ + 'label' => $driver->tryMethod('isRDA') + ? 'rda_original_title' + : 'non_rda_original_title', + 'values' => [$type => $values], + 'options' => [ + 'pos' => $options['pos'], + 'renderType' => 'RecordDriverTemplate', + 'template' => 'data-titleUniform.phtml', + ], + ]; + } + return $final; + } + +} diff --git a/module/finc/src/finc/View/Helper/Root/Record.php b/module/finc/src/finc/View/Helper/Root/Record.php index df2d737400ed10d2d944ba7ed146c19003daf30b..5a59179a709d672563e851cd8cc01eae53ce517b 100644 --- a/module/finc/src/finc/View/Helper/Root/Record.php +++ b/module/finc/src/finc/View/Helper/Root/Record.php @@ -309,7 +309,7 @@ class Record extends \VuFind\View\Helper\Root\Record $link = $this->rewriteLink($link); } - return $links; + return array_filter($links); } /** diff --git a/module/finc/src/finc/View/Helper/Root/RecordDataFormatterFactory.php b/module/finc/src/finc/View/Helper/Root/RecordDataFormatterFactory.php index 2867335c86c34d4991a0a90204679d2cbba00c34..5d3672c0b4b64a7e4af23578902dbd4bcd4838a3 100644 --- a/module/finc/src/finc/View/Helper/Root/RecordDataFormatterFactory.php +++ b/module/finc/src/finc/View/Helper/Root/RecordDataFormatterFactory.php @@ -364,9 +364,8 @@ class RecordDataFormatterFactory 'getTitleUniform', 'data-titleUniform.phtml', [ - 'labelFunction' => function () { - return null; - } + 'labelFunction' => + [$this,'titleUniformLabel'] ] ); $spec->setLine( @@ -853,9 +852,8 @@ class RecordDataFormatterFactory 'getTitleUniform', 'data-titleUniform.phtml', [ - 'labelFunction' => function () { - return null; - } + 'labelFunction' => + [$this,'titleUniformLabel'] ] ); $spec->setLine( @@ -949,4 +947,18 @@ class RecordDataFormatterFactory $spec->setTemplateLine('Tags', true, 'data-tags.phtml'); return $spec->getArray(); } + + /** + * Labels titleUniform Data Line based on the record + * being catalogued following RDA, or not. cf. https://projekte.ub.uni-leipzig.de/issues/13830 + * @param $data + * @param $driver + * @return string + */ + public function titleUniformLabel($data, $driver) + { + return $driver->tryMethod('isRDA') + ? 'rda_original_title' + : 'non_rda_original_title'; + } } diff --git a/module/finc/tests/bootstrap.php b/module/finc/tests/bootstrap.php index a03458275c1d520c2a3d245360b0c8fdfe9699d9..62190b89ea090fd0f18525a3a470d51dd26ce0c0 100644 --- a/module/finc/tests/bootstrap.php +++ b/module/finc/tests/bootstrap.php @@ -101,7 +101,8 @@ class Bootstrap ]; self::initEnvironment(); $config = ArrayUtils::merge($baseConfig, $testConfig); - $serviceManager = new ServiceManager(new ServiceManagerConfig()); + $managerConfig = new ServiceManagerConfig(); + $serviceManager = new ServiceManager($managerConfig->toArray()); $serviceManager->setService('ApplicationConfig', $config); $serviceManager->get('ModuleManager')->loadModules(); static::$serviceManager = $serviceManager; @@ -113,7 +114,7 @@ class Bootstrap * * @return void */ - public function initEnvironment() + public static function initEnvironment() { define('APPLICATION_ENV', 'development'); define('FINC_TEST_FIXTURES', realpath(FINC_TESTS_PATH . '/fixtures')); diff --git a/module/finc/tests/fixtures/configs/yaml/searchspecs/config/vufind/searchspecs.yaml b/module/finc/tests/fixtures/configs/yaml/searchspecs/config/vufind/searchspecs.yaml new file mode 120000 index 0000000000000000000000000000000000000000..183f7ab71ef5f14bbcc59307e6b8dca43de50bea --- /dev/null +++ b/module/finc/tests/fixtures/configs/yaml/searchspecs/config/vufind/searchspecs.yaml @@ -0,0 +1 @@ +../../../../../../../../../config/vufind/searchspecs.yaml \ No newline at end of file diff --git a/module/finc/tests/fixtures/configs/yaml/searchspecs/local/alpha/config/vufind/searchspecs.yaml b/module/finc/tests/fixtures/configs/yaml/searchspecs/local/alpha/config/vufind/searchspecs.yaml new file mode 120000 index 0000000000000000000000000000000000000000..59ae9378e2e3c4bd888febe0d254c83cae12fd47 --- /dev/null +++ b/module/finc/tests/fixtures/configs/yaml/searchspecs/local/alpha/config/vufind/searchspecs.yaml @@ -0,0 +1 @@ +../../../../../../../../../../../local/alpha/config/vufind/searchspecs.yaml \ No newline at end of file diff --git a/module/finc/tests/fixtures/configs/yaml/searchspecs/local/config/vufind/searchspecs.yaml b/module/finc/tests/fixtures/configs/yaml/searchspecs/local/config/vufind/searchspecs.yaml new file mode 120000 index 0000000000000000000000000000000000000000..74dc0578aea75218966a506946443c45ff22c962 --- /dev/null +++ b/module/finc/tests/fixtures/configs/yaml/searchspecs/local/config/vufind/searchspecs.yaml @@ -0,0 +1 @@ +../../../../../../../../../../local/config/vufind/searchspecs.yaml \ No newline at end of file diff --git a/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/alpha/searchspecs.yaml b/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/alpha/searchspecs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e8bfdcad8d618d13d088011c0a10904df2be051 --- /dev/null +++ b/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/alpha/searchspecs.yaml @@ -0,0 +1,501 @@ +Author: + DismaxParams: + - + - bf + - ord(publishDateSort)^10 + DismaxFields: + - author^400 + - author2^300 + - author_id^100 + - author_ref^150 + - author_corporate^200 + - author_corporate2^200 + - author_orig^200 + - author2_orig^200 + - author_corporate_orig^200 + - author_corporate2_orig^200 + - author_fuller^50 + - author2_fuller + - author_additional + - author_variant + - author2_variant + QueryFields: + author: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_fuller: + - [onephrase, 200] + - [and, 100] + - [or, 50] + author2: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_ref: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate2: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author2_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate2_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_id: + - [onephrase, 450] + - [and, 300] + - [or, 200] + author2_fuller: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_additional: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_variant: + - [onephrase, 100] + - [and, 50] + - [or, null] + author2_variant: + - [onephrase, 100] + - [and, 50] + - [or, null] +ISN: + DismaxFields: + - isbn + - issn + - ismn + QueryFields: + issn: + - [and, 100] + - [or, null] + isbn: + - [and, 100] + - [or, null] + ismn: + - [and, 100] + - [or, null] +Signatur: + QueryFields: + callnumber_ISIL: + - [onephrase, 1000] + - [and, 100] + - [or, null] +Barcode: + QueryFields: + barcode_ISIL: + - [onephrase, 1000] + - [and, 100] + - [or, null] +Subject: + DismaxFields: + - topic_unstemmed^150 + - topic^100 + - topic_id^100 + - topic_ref^100 + QueryFields: + topic_unstemmed: + - [onephrase, 350] + - [and, 150] + - [or, null] + topic: + - [onephrase, 300] + - [and, 100] + - [or, null] + topic_ref: + - [onephrase, 100] + - [and, 50] + - [or, null] + topic_id: + - [onephrase, 100] + - [and, 50] + - [or, null] +JournalTitle: + DismaxFields: + - title_short^500 + - title_full_unstemmed^450 + - title_full^400 + - title^300 + - container_title^250 + - title_alt^200 + - title_new^100 + - title_old + - series^100 + - series2 + QueryFields: + title_short: + - [onephrase, 500] + title_full_unstemmed: + - [onephrase, 450] + - [and, 400] + title_full: + - [onephrase, 400] + title: + - [onephrase, 300] + - [and, 250] + container_title: + - [onephrase, 275] + - [and, 225] + title_alt: + - [and, 200] + title_new: + - [and, 100] + title_old: + - [and, null] + series: + - [onephrase, 100] + - [and, 50] + series2: + - [onephrase, 50] + - [and, null] + FilterQuery: 'format:Journal OR format:Article OR format:ElectronicBookPart' +Title: + DismaxParams: + - + - mm + - 3 + - + - bf + - ord(publishDateSort)^10 + DismaxFields: + - title_full_unstemmed^150 + - title_full^100 + - title^900 + - title_alt^200 + - title_new^100 + - title_old + - title_orig^400 + - series^100 + - series2 + - series_orig^100 + QueryFields: + title_short: + - [onephrase, 500] + title_full_unstemmed: + - [onephrase, 150] + - [and, 100] + title_full: + - [onephrase, 100] + title: + - [onephrase, 300] + - [and, 250] + title_alt: + - [and, 200] + title_new: + - [and, 100] + title_old: + - [and, null] + title_orig: + - [onephrase, 500] + - [and, 200] + series: + - [onephrase, 100] + - [and, 50] + series2: + - [onephrase, 50] + - [and, null] + series_orig: + - [onephrase, 100] + - [and, 50] +Series: + DismaxFields: + - series^100 + - series2 + - series_orig^100 + QueryFields: + series: + - [onephrase, 500] + - [and, 200] + - [or, 100] + series2: + - [onephrase, 50] + - [and, 50] + - [or, null] + series_orig: + - [onephrase, 500] + - [and, 200] + - [or, 100] +Series2: + DismaxFields: + - series2 + QueryFields: + series2: + - [onephrase, 200] + - [and, 50] +AllFields: + DismaxParams: + - + - mm + - 3 + - + - bf + - ord(publishDateSort)^10 + - + - bf + - 'if(exists(query({!v=''facet_avail:Local*''})),10,1)^1000' + DismaxFields: + - title_short^1000 + - title_full_unstemmed^1000 + - title_full^400 + - title^500 + - title_alt^200 + - title_new^100 + - title_orig^500 + - series^50 + - series2^30 + - series_orig^50 + - author^500 + - author_fuller^150 + - author_corporate^300 + - author2^400 + - author_corporate2^100 + - author_ref^500 + - author_orig^300 + - author2_orig^300 + - author_corporate_orig^300 + - author_corporate2_orig^100 + - topic_ref^10 + - contents^10 + - topic_unstemmed^15 + - topic^10 + - geographic^10 + - genre^10 + - rvk_label + - allfields_unstemmed^10 + - allfields + - fulltext + - isbn + - issn + - ismn + QueryFields: + 0: + 0: [OR, 50] + title_short: [[onephrase, 1000]] + title_full_unstemmed: [[onephrase, 1000], [and, 500]] + title_full: [[onephrase, 400]] + title: [[onephrase, 300], [and, 250]] + title_alt: [[and, 200]] + title_new: [[and, 100]] + title_orig: [[onephrase, 500], [and, 400]] + series: + - [onephrase, 300] + - [and, 100] + series2: + - [and, 30] + series_orig: + - [onephrase, 200] + - [and, 100] + author: + - [onephrase, 500] + - [and, 250] + author_fuller: + - [onephrase, 150] + - [and, 125] + author_ref: + - [onephrase, 250] + - [and, 250] + - [or, 250] + author_orig: + - [onephrase, 500] + - [and, 250] + author2_orig: + - [and, 50] + author_corporate_orig: + - [onephrase, 500] + - [and, 400] + author_corporate2_orig: + - [and, 50] + author_corporate: + - [onephrase, 500] + - [and, 400] + author2: + - [and, 50] + author_additional: + - [and, 50] + author_corporate2: + - [and, 50] + contents: + - [and, 10] + topic_unstemmed: + - [onephrase, 55] + - [and, 50] + topic: + - [onephrase, 50] + topic_ref: + - [onephrase, 10] + - [and, 5] + - [or, 5] + topic_id: + - [onephrase, 50] + - [and, 25] + allfields_unstemmed: + - [or, 10] + allfields: + - [or, null] + fulltext: + - [or, null] + rvk_label: + - [onephrase, 500] + - [and, 250] + - [or, 250] + isbn: + - [onephrase, 500] + issn: + - [onephrase, 500] + ismn: + - [onephrase, 500] + imprint: + - [onephrase, 500] +id: + QueryFields: + id: + - [onephrase, null] +ParentID: + QueryFields: + hierarchy_parent_id: + - [onephrase, null] +ids: + QueryFields: + id: + - [or, null] +TopicBrowse: + QueryFields: + topic_browse: + - [onephrase, null] +AuthorBrowse: + QueryFields: + author_browse: + - [onephrase, null] +TitleBrowse: + QueryFields: + title_full: + - [onephrase, null] +DeweyBrowse: + QueryFields: + dewey-raw: + - [onephrase, null] +LccBrowse: + QueryFields: + callnumber-a: + - [onephrase, null] +publisher: + DismaxFields: + - publisher^100 + QueryFields: + publisher: + - [and, 100] + - [or, null] +year: + DismaxFields: + - publishDate^100 + QueryFields: + publishDate: + - [and, 100] + - [or, null] +language: + QueryFields: + language: + - [and, null] +toc: + DismaxFields: + - contents^100 + QueryFields: + contents: + - [and, 100] + - [or, null] +topic: + QueryFields: + topic: + - [and, 50] + topic_facet: + - [and, null] +geographic: + QueryFields: + geographic: + - [and, 50] + geographic_facet: + - [and, null] +genre: + QueryFields: + genre: + - [and, 50] + genre_facet: + - [and, null] +era: + QueryFields: + era: + - [and, null] +oclc_num: + CustomMunge: + oclc_num: + - [preg_replace, '/[^0-9]/', ''] + - [preg_replace, '/^0*/', ''] + QueryFields: + oclc_num: + - [oclc_num, null] +rvk: + DismaxFields: + - rvk_facet^100 + QueryFields: + rvk_facet: + - [and, 50] + - [or, 50] +rvk_path: + QueryFields: + rvk_path: + - [onephrase, null] +multipart: + DismaxFields: + - multipart_link^100 + QueryFields: + multipart_link: + - [and, 50] + - [or, 50] +titleUniform: + QueryFields: + title_id_str_mv: + - [onephrase, null] +Coordinate: + DismaxFields: + - long_lat_display + DismaxHandler: edismax +CallNumber: + CustomMunge: + callnumber_exact: + - [preg_replace, '/[ "]/', ''] + - [preg_replace, '/(\\:)/', ':'] + - [preg_replace, '/:/', '\:'] + - [preg_replace, '/\*+$/', ''] + callnumber_fuzzy: + - [preg_replace, '/[ "]/', ''] + - [preg_replace, '/(\\:)/', ':'] + - [preg_replace, '/:/', '\:'] + - [preg_replace, '/\*+$/', ''] + - [append, '*'] + QueryFields: + callnumber-search: + - [callnumber_exact, 1000] + - [callnumber_fuzzy, null] + dewey-search: + - [callnumber_exact, 1000] + - [callnumber_fuzzy, null] diff --git a/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/searchspecs.yaml b/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/searchspecs.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1e8bfdcad8d618d13d088011c0a10904df2be051 --- /dev/null +++ b/module/finc/tests/fixtures/configs/yaml/searchspecs/result/local/searchspecs.yaml @@ -0,0 +1,501 @@ +Author: + DismaxParams: + - + - bf + - ord(publishDateSort)^10 + DismaxFields: + - author^400 + - author2^300 + - author_id^100 + - author_ref^150 + - author_corporate^200 + - author_corporate2^200 + - author_orig^200 + - author2_orig^200 + - author_corporate_orig^200 + - author_corporate2_orig^200 + - author_fuller^50 + - author2_fuller + - author_additional + - author_variant + - author2_variant + QueryFields: + author: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_fuller: + - [onephrase, 200] + - [and, 100] + - [or, 50] + author2: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_ref: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate2: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author2_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_corporate2_orig: + - [onephrase, 350] + - [and, 200] + - [or, 100] + author_id: + - [onephrase, 450] + - [and, 300] + - [or, 200] + author2_fuller: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_additional: + - [onephrase, 100] + - [and, 50] + - [or, null] + author_variant: + - [onephrase, 100] + - [and, 50] + - [or, null] + author2_variant: + - [onephrase, 100] + - [and, 50] + - [or, null] +ISN: + DismaxFields: + - isbn + - issn + - ismn + QueryFields: + issn: + - [and, 100] + - [or, null] + isbn: + - [and, 100] + - [or, null] + ismn: + - [and, 100] + - [or, null] +Signatur: + QueryFields: + callnumber_ISIL: + - [onephrase, 1000] + - [and, 100] + - [or, null] +Barcode: + QueryFields: + barcode_ISIL: + - [onephrase, 1000] + - [and, 100] + - [or, null] +Subject: + DismaxFields: + - topic_unstemmed^150 + - topic^100 + - topic_id^100 + - topic_ref^100 + QueryFields: + topic_unstemmed: + - [onephrase, 350] + - [and, 150] + - [or, null] + topic: + - [onephrase, 300] + - [and, 100] + - [or, null] + topic_ref: + - [onephrase, 100] + - [and, 50] + - [or, null] + topic_id: + - [onephrase, 100] + - [and, 50] + - [or, null] +JournalTitle: + DismaxFields: + - title_short^500 + - title_full_unstemmed^450 + - title_full^400 + - title^300 + - container_title^250 + - title_alt^200 + - title_new^100 + - title_old + - series^100 + - series2 + QueryFields: + title_short: + - [onephrase, 500] + title_full_unstemmed: + - [onephrase, 450] + - [and, 400] + title_full: + - [onephrase, 400] + title: + - [onephrase, 300] + - [and, 250] + container_title: + - [onephrase, 275] + - [and, 225] + title_alt: + - [and, 200] + title_new: + - [and, 100] + title_old: + - [and, null] + series: + - [onephrase, 100] + - [and, 50] + series2: + - [onephrase, 50] + - [and, null] + FilterQuery: 'format:Journal OR format:Article OR format:ElectronicBookPart' +Title: + DismaxParams: + - + - mm + - 3 + - + - bf + - ord(publishDateSort)^10 + DismaxFields: + - title_full_unstemmed^150 + - title_full^100 + - title^900 + - title_alt^200 + - title_new^100 + - title_old + - title_orig^400 + - series^100 + - series2 + - series_orig^100 + QueryFields: + title_short: + - [onephrase, 500] + title_full_unstemmed: + - [onephrase, 150] + - [and, 100] + title_full: + - [onephrase, 100] + title: + - [onephrase, 300] + - [and, 250] + title_alt: + - [and, 200] + title_new: + - [and, 100] + title_old: + - [and, null] + title_orig: + - [onephrase, 500] + - [and, 200] + series: + - [onephrase, 100] + - [and, 50] + series2: + - [onephrase, 50] + - [and, null] + series_orig: + - [onephrase, 100] + - [and, 50] +Series: + DismaxFields: + - series^100 + - series2 + - series_orig^100 + QueryFields: + series: + - [onephrase, 500] + - [and, 200] + - [or, 100] + series2: + - [onephrase, 50] + - [and, 50] + - [or, null] + series_orig: + - [onephrase, 500] + - [and, 200] + - [or, 100] +Series2: + DismaxFields: + - series2 + QueryFields: + series2: + - [onephrase, 200] + - [and, 50] +AllFields: + DismaxParams: + - + - mm + - 3 + - + - bf + - ord(publishDateSort)^10 + - + - bf + - 'if(exists(query({!v=''facet_avail:Local*''})),10,1)^1000' + DismaxFields: + - title_short^1000 + - title_full_unstemmed^1000 + - title_full^400 + - title^500 + - title_alt^200 + - title_new^100 + - title_orig^500 + - series^50 + - series2^30 + - series_orig^50 + - author^500 + - author_fuller^150 + - author_corporate^300 + - author2^400 + - author_corporate2^100 + - author_ref^500 + - author_orig^300 + - author2_orig^300 + - author_corporate_orig^300 + - author_corporate2_orig^100 + - topic_ref^10 + - contents^10 + - topic_unstemmed^15 + - topic^10 + - geographic^10 + - genre^10 + - rvk_label + - allfields_unstemmed^10 + - allfields + - fulltext + - isbn + - issn + - ismn + QueryFields: + 0: + 0: [OR, 50] + title_short: [[onephrase, 1000]] + title_full_unstemmed: [[onephrase, 1000], [and, 500]] + title_full: [[onephrase, 400]] + title: [[onephrase, 300], [and, 250]] + title_alt: [[and, 200]] + title_new: [[and, 100]] + title_orig: [[onephrase, 500], [and, 400]] + series: + - [onephrase, 300] + - [and, 100] + series2: + - [and, 30] + series_orig: + - [onephrase, 200] + - [and, 100] + author: + - [onephrase, 500] + - [and, 250] + author_fuller: + - [onephrase, 150] + - [and, 125] + author_ref: + - [onephrase, 250] + - [and, 250] + - [or, 250] + author_orig: + - [onephrase, 500] + - [and, 250] + author2_orig: + - [and, 50] + author_corporate_orig: + - [onephrase, 500] + - [and, 400] + author_corporate2_orig: + - [and, 50] + author_corporate: + - [onephrase, 500] + - [and, 400] + author2: + - [and, 50] + author_additional: + - [and, 50] + author_corporate2: + - [and, 50] + contents: + - [and, 10] + topic_unstemmed: + - [onephrase, 55] + - [and, 50] + topic: + - [onephrase, 50] + topic_ref: + - [onephrase, 10] + - [and, 5] + - [or, 5] + topic_id: + - [onephrase, 50] + - [and, 25] + allfields_unstemmed: + - [or, 10] + allfields: + - [or, null] + fulltext: + - [or, null] + rvk_label: + - [onephrase, 500] + - [and, 250] + - [or, 250] + isbn: + - [onephrase, 500] + issn: + - [onephrase, 500] + ismn: + - [onephrase, 500] + imprint: + - [onephrase, 500] +id: + QueryFields: + id: + - [onephrase, null] +ParentID: + QueryFields: + hierarchy_parent_id: + - [onephrase, null] +ids: + QueryFields: + id: + - [or, null] +TopicBrowse: + QueryFields: + topic_browse: + - [onephrase, null] +AuthorBrowse: + QueryFields: + author_browse: + - [onephrase, null] +TitleBrowse: + QueryFields: + title_full: + - [onephrase, null] +DeweyBrowse: + QueryFields: + dewey-raw: + - [onephrase, null] +LccBrowse: + QueryFields: + callnumber-a: + - [onephrase, null] +publisher: + DismaxFields: + - publisher^100 + QueryFields: + publisher: + - [and, 100] + - [or, null] +year: + DismaxFields: + - publishDate^100 + QueryFields: + publishDate: + - [and, 100] + - [or, null] +language: + QueryFields: + language: + - [and, null] +toc: + DismaxFields: + - contents^100 + QueryFields: + contents: + - [and, 100] + - [or, null] +topic: + QueryFields: + topic: + - [and, 50] + topic_facet: + - [and, null] +geographic: + QueryFields: + geographic: + - [and, 50] + geographic_facet: + - [and, null] +genre: + QueryFields: + genre: + - [and, 50] + genre_facet: + - [and, null] +era: + QueryFields: + era: + - [and, null] +oclc_num: + CustomMunge: + oclc_num: + - [preg_replace, '/[^0-9]/', ''] + - [preg_replace, '/^0*/', ''] + QueryFields: + oclc_num: + - [oclc_num, null] +rvk: + DismaxFields: + - rvk_facet^100 + QueryFields: + rvk_facet: + - [and, 50] + - [or, 50] +rvk_path: + QueryFields: + rvk_path: + - [onephrase, null] +multipart: + DismaxFields: + - multipart_link^100 + QueryFields: + multipart_link: + - [and, 50] + - [or, 50] +titleUniform: + QueryFields: + title_id_str_mv: + - [onephrase, null] +Coordinate: + DismaxFields: + - long_lat_display + DismaxHandler: edismax +CallNumber: + CustomMunge: + callnumber_exact: + - [preg_replace, '/[ "]/', ''] + - [preg_replace, '/(\\:)/', ':'] + - [preg_replace, '/:/', '\:'] + - [preg_replace, '/\*+$/', ''] + callnumber_fuzzy: + - [preg_replace, '/[ "]/', ''] + - [preg_replace, '/(\\:)/', ':'] + - [preg_replace, '/:/', '\:'] + - [preg_replace, '/\*+$/', ''] + - [append, '*'] + QueryFields: + callnumber-search: + - [callnumber_exact, 1000] + - [callnumber_fuzzy, null] + dewey-search: + - [callnumber_exact, 1000] + - [callnumber_fuzzy, null] diff --git a/module/finc/tests/fixtures/paia/response/fincWithAnotherPickupBranchOption.json b/module/finc/tests/fixtures/paia/response/fincWithAnotherPickupBranchOption.json new file mode 100644 index 0000000000000000000000000000000000000000..75a8a368417b49ad518a215e0aea93df9a3d5734 --- /dev/null +++ b/module/finc/tests/fixtures/paia/response/fincWithAnotherPickupBranchOption.json @@ -0,0 +1,50 @@ +HTTP/1.1 200 OK +Server: vzg-paia/2.2-RC3 (Wed Jun 01 17:38:24 CEST 2016) +Date: Mon, 20 Jun 2020 10:38:21 GMT +Cache-Control: no-cache +Access-Control-Allow-Origin: * +Access-Control-Expose-Headers: X-OAuth-Scopes, X-Accepted-OAuth-Scopes +Access-Control-Allow-Headers: Content-Type, Authorization +Pragma: no-cache +X-OAuth-Scopes: change_password read_items read_fees read_patron +X-Accepted-OAuth-Scopes: read_patron +Content-Type: application/json; charset=UTF-8 + +{ + "doc": [ + { + "status": 3, + "item": "DE-Zi4:barcode:03201978", + "about": "Urbanität als Habitus", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 4, + "item": "DE-Zi4:barcode:80086919", + "about": "Fernleihe mit Abholstation", + "condition": { + "http://purl.org/ontology/paia#PickupCondition": { + "option": [ + { + "id": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI", + "about": "Zweigbibliothek Zittau", + "amount": "2,50 EUR" + } + ] + } + }, + "storage": "Hochschulbibliothek Görlitz", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR" + }, + { + "status": 0, + "item": "ILL:13940", + "about": "Aushandlungen städtischer Größe - Eckert, Anna", + "label": "Medium im Vormerkregal", + "starttime": "2020-03-12T00:00:00+01:00" + } + ] +} diff --git a/module/finc/tests/fixtures/paia/response/fincWithMultiplePickupBranchOptions.json b/module/finc/tests/fixtures/paia/response/fincWithMultiplePickupBranchOptions.json new file mode 100644 index 0000000000000000000000000000000000000000..19225a142cbce6509093f88ca72aa45642982717 --- /dev/null +++ b/module/finc/tests/fixtures/paia/response/fincWithMultiplePickupBranchOptions.json @@ -0,0 +1,53 @@ +HTTP/1.1 200 OK +Server: vzg-paia/2.2-RC3 (Wed Jun 01 17:38:24 CEST 2016) +Date: Mon, 20 Jun 2020 10:38:21 GMT +Cache-Control: no-cache +Access-Control-Allow-Origin: * +Access-Control-Expose-Headers: X-OAuth-Scopes, X-Accepted-OAuth-Scopes +Access-Control-Allow-Headers: Content-Type, Authorization +Pragma: no-cache +X-OAuth-Scopes: change_password read_items read_fees read_patron +X-Accepted-OAuth-Scopes: read_patron +Content-Type: application/json; charset=UTF-8 + +{ + "doc": [ + { + "status": 3, + "item": "DE-Zi4:barcode:03201978", + "about": "Urbanität als Habitus", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 4, + "item": "DE-Zi4:barcode:80086919", + "about": "Fernleihe mit Abholstation", + "condition": { + "http://purl.org/ontology/paia#PickupCondition": { + "option": [ + { + "id": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI", + "about": "Zweigbibliothek Zittau" + }, + { + "id": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR", + "about": "Zweigbibliothek Görlitz" + } + ] + } + }, + "storage": "Hochschulbibliothek Görlitz", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR" + }, + { + "status": 0, + "item": "ILL:13940", + "about": "Aushandlungen städtischer Größe - Eckert, Anna", + "label": "Medium im Vormerkregal", + "starttime": "2020-03-12T00:00:00+01:00" + } + ] +} diff --git a/module/finc/tests/fixtures/paia/response/fincWithOnePickupBranchOption.json b/module/finc/tests/fixtures/paia/response/fincWithOnePickupBranchOption.json new file mode 100644 index 0000000000000000000000000000000000000000..c00ed7217e75f197d4c4f79841eadf4f2b45b291 --- /dev/null +++ b/module/finc/tests/fixtures/paia/response/fincWithOnePickupBranchOption.json @@ -0,0 +1,66 @@ +HTTP/1.1 200 OK +Server: vzg-paia/2.2-RC3 (Wed Jun 01 17:38:24 CEST 2016) +Date: Mon, 20 Jun 2020 10:38:21 GMT +Cache-Control: no-cache +Access-Control-Allow-Origin: * +Access-Control-Expose-Headers: X-OAuth-Scopes, X-Accepted-OAuth-Scopes +Access-Control-Allow-Headers: Content-Type, Authorization +Pragma: no-cache +X-OAuth-Scopes: change_password read_items read_fees read_patron +X-Accepted-OAuth-Scopes: read_patron +Content-Type: application/json; charset=UTF-8 + +{ + "doc": [ + { + "status": 4, + "item": "DE-Zi4:barcode:80086919", + "about": "Fernleihe mit Abholstation", + "condition": { + "http://purl.org/ontology/paia#PickupCondition": { + "option": [ + { + "id": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR", + "about": "Zweigbibliothek Görlitz", + "amount": "2.50 EUR" + } + ] + } + }, + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:02415857", + "about": "Die Eigenlogik der Städte", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 4, + "item": "DE-Zi4:barcode:03039724", + "about": "Stadtsoziologie", + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:03201978", + "about": "Urbanität als Habitus", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 0, + "item": "ILL:13940", + "about": "Aushandlungen städtischer Größe - Eckert, Anna", + "label": "Medium im Vormerkregal", + "starttime": "2020-03-12T00:00:00+01:00" + } + ] +} diff --git a/module/finc/tests/fixtures/paia/response/fincWithUnknownOption.json b/module/finc/tests/fixtures/paia/response/fincWithUnknownOption.json new file mode 100644 index 0000000000000000000000000000000000000000..e3120951b9cca8f9e4393574c317a8b3fd709b59 --- /dev/null +++ b/module/finc/tests/fixtures/paia/response/fincWithUnknownOption.json @@ -0,0 +1,65 @@ +HTTP/1.1 200 OK +Server: vzg-paia/2.2-RC3 (Wed Jun 01 17:38:24 CEST 2016) +Date: Mon, 20 Jun 2020 10:38:21 GMT +Cache-Control: no-cache +Access-Control-Allow-Origin: * +Access-Control-Expose-Headers: X-OAuth-Scopes, X-Accepted-OAuth-Scopes +Access-Control-Allow-Headers: Content-Type, Authorization +Pragma: no-cache +X-OAuth-Scopes: change_password read_items read_fees read_patron +X-Accepted-OAuth-Scopes: read_patron +Content-Type: application/json; charset=UTF-8 + +{ + "doc": [ + { + "status": 4, + "item": "DE-Zi4:barcode:80086919", + "about": "Fernleihe mit Abholstation", + "condition": { + "http://purl.org/ontology/paia#PickupCondition": { + "option": [ + { + "id": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/unknownLocation", + "about": "Zweigbibliothek Neu aber noch nicht bekannt / konfiguriert => soll nicht angezeigt werden" + } + ] + } + }, + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:02415857", + "about": "Die Eigenlogik der Städte", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 4, + "item": "DE-Zi4:barcode:03039724", + "about": "Stadtsoziologie", + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:03201978", + "about": "Urbanität als Habitus", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 0, + "item": "ILL:13940", + "about": "Aushandlungen städtischer Größe - Eckert, Anna", + "label": "Medium im Vormerkregal", + "starttime": "2020-03-12T00:00:00+01:00" + } + ] +} diff --git a/module/finc/tests/fixtures/paia/response/fincWithoutOptions.json b/module/finc/tests/fixtures/paia/response/fincWithoutOptions.json new file mode 100644 index 0000000000000000000000000000000000000000..f75b1602ac84a2139575bf72f82023931b3292f7 --- /dev/null +++ b/module/finc/tests/fixtures/paia/response/fincWithoutOptions.json @@ -0,0 +1,55 @@ +HTTP/1.1 200 OK +Server: vzg-paia/2.2-RC3 (Wed Jun 01 17:38:24 CEST 2016) +Date: Mon, 20 Jun 2020 10:38:21 GMT +Cache-Control: no-cache +Access-Control-Allow-Origin: * +Access-Control-Expose-Headers: X-OAuth-Scopes, X-Accepted-OAuth-Scopes +Access-Control-Allow-Headers: Content-Type, Authorization +Pragma: no-cache +X-OAuth-Scopes: change_password read_items read_fees read_patron +X-Accepted-OAuth-Scopes: read_patron +Content-Type: application/json; charset=UTF-8 + +{ + "doc": [ + { + "status": 4, + "item": "DE-Zi4:barcode:80086919", + "about": "Fernleihe mit Abholstation", + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:02415857", + "about": "Die Eigenlogik der Städte", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 4, + "item": "DE-Zi4:barcode:03039724", + "about": "Stadtsoziologie", + "storage": "Hochschulbibliothek Zittau", + "storageid": "http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI" + }, + { + "status": 3, + "item": "DE-Zi4:barcode:03201978", + "about": "Urbanität als Habitus", + "renewals": 3, + "starttime": "2020-03-13T00:00:00+01:00", + "endtime": "2020-07-10T00:00:00+02:00", + "canrenew": true + }, + { + "status": 0, + "item": "ILL:13940", + "about": "Aushandlungen städtischer Größe - Eckert, Anna", + "label": "Medium im Vormerkregal", + "starttime": "2020-03-12T00:00:00+01:00" + } + ] +} diff --git a/module/finc/tests/phpunit.xml b/module/finc/tests/phpunit.xml index bd716aae1508c2e3ccd169baa8d13fc6b3c95068..6aacf6705b8fdc6170500a4fc4da35a23c2f89f2 100644 --- a/module/finc/tests/phpunit.xml +++ b/module/finc/tests/phpunit.xml @@ -6,4 +6,12 @@ <testsuite name="fincTest"> <directory>./unit-tests/src</directory> </testsuite> + <php> + <!-- Docker environment variables --> + <env name="LOCAL_CACHE_DIR" value="/usr/local/vufind/data/cache"/> + <env name="LOCAL_OVERRIDE_DIR" value="/usr/local/vufind/de_15/dev"/> + <env name="APPLICATION_PATH" value="/usr/local/vufind"/> + <env name="VUFIND_LOCAL_DIR" value="/usr/local/vufind/de_15/dev"/> + <env name="VUFIND_ENV" value="development"/> + </php> </phpunit> diff --git a/module/finc/tests/unit-tests/src/fincTest/Config/SearchSpecsReaderTest.php b/module/finc/tests/unit-tests/src/fincTest/Config/SearchSpecsReaderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e172829e6cab132cffe3edd5c76ee73deb7b78d6 --- /dev/null +++ b/module/finc/tests/unit-tests/src/fincTest/Config/SearchSpecsReaderTest.php @@ -0,0 +1,133 @@ +<?php +/** + * Config SearchSpecsReader Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * Copyright (C) Leipzig University Library 2020. + * + * 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 Finc + * @package Tests + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace fincTest\Config; + +use Symfony\Component\Yaml\Yaml; +use VuFind\Config\Locator; +use VuFind\Config\SearchSpecsReader; + +/** + * Config SearchSpecsReader Test Class + * + * @category VuFind + * @package Tests + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class SearchSpecsReaderTest extends \VuFindTest\Config\SearchSpecsReaderTest +{ + /** + * Name of default yaml name to test. + * + * @var string + */ + protected static $defaultYamlName = 'searchspecs.yaml'; + + /** + * YamlReader for SearchSpecs files. + * + * @var SearchSpecsReader + */ + protected static $reader; + + /** + * Path to symlinks and expected searchspecs result. + * + * @var string + */ + protected $basePathToFixtures; + + /** + * Path to local logging / printing. + * + * @var string + */ + protected $basePathToLogging; + + /** + * Override standard setup method. + * + * @return void + */ + public static function setUpBeforeClass() + { + // Don't create test files, use existing. + + // CAUTION, DONT DELETE REAL CONFIG FILES! + self::$filesToDelete = []; + self::$reader = new SearchSpecsReader(); + } + + /** + * Test @parent_yaml directive. + * + * @return void + */ + public function testParentYaml() + { + if (self::$writeFailed) { + $this->markTestSkipped('Could not write test configurations.'); + } + $this->basePathToFixtures = __DIR__ . "/../../../../fixtures/configs/yaml/searchspecs"; + $this->basePathToLogging = __DIR__ . "/log/"; + + /* check live finc searchspecs.yaml is equal to old /desired live searchspecs.yaml */ + $this->isEcpectedSpec('local'); + + /* check alpha finc searchspecs.yaml is equal to old /desired alpha searchspecs.yaml */ + $this->isEcpectedSpec('local/alpha'); + } + + /** + * @param string $environment + * @param bool $printResult + * @throws \ReflectionException + */ + protected function isEcpectedSpec(string $environment, bool $printResult = false): void + { + $basePathAppendix = "config/vufind/" . self::$defaultYamlName; + + $vufindYaml = "$this->basePathToFixtures/$basePathAppendix"; + $currentEnvYaml = "$this->basePathToFixtures/$environment/$basePathAppendix"; + $expectedYaml = "$this->basePathToFixtures/result/$environment/" . self::$defaultYamlName; + + $resultArray = $this->callMethod(self::$reader, 'getFromPaths', [$vufindYaml, $currentEnvYaml]); + $expectedArray = $this->callMethod(self::$reader, 'getFromPaths', [$expectedYaml]); + + if ($printResult) { + file_put_contents("$this->basePathToLogging/input_reverse.yaml", Yaml::dump($resultArray, 4, 2)); + } + + $this->assertEquals( + $resultArray, + $expectedArray + ); + } +} diff --git a/module/finc/tests/unit-tests/src/fincTest/ILS/Driver/PAIATest.php b/module/finc/tests/unit-tests/src/fincTest/ILS/Driver/PAIATest.php new file mode 100644 index 0000000000000000000000000000000000000000..8169a7e9ad06ef74b46563d1a974d2ae9086a4b4 --- /dev/null +++ b/module/finc/tests/unit-tests/src/fincTest/ILS/Driver/PAIATest.php @@ -0,0 +1,767 @@ +<?php +/** + * ILS driver test + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2020. + * + * 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 Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\ILS\Driver; + +use finc\ILS\Driver\PAIA as fincPAIA; +use InvalidArgumentException; +use Zend\Http\Client\Adapter\Test as TestAdapter; +use Zend\Http\Response as HttpResponse; + +/** + * ILS driver test + * + * @category VuFind + * @package FincTest + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class PAIAFincTest extends \VuFindTest\ILS\Driver\PAIATest +{ + public static $pickupCondition = 'http://purl.org/ontology/paia#PickupCondition'; + + public static $validConfigWithPickUpOption = [ + 'DAIA' => + [ + 'baseUrl' => 'http://daia.gbv.de/', + ], + 'PAIA' => + [ + 'baseUrl' => 'http://paia.gbv.de/', + 'paiaConditions' => + [ + 'http://purl.org/ontology/paia#PickupCondition' + ] + ] + ]; + + public static $validConfigWithOptionForOtherInstance = [ + 'DAIA' => + [ + 'baseUrl' => 'http://daia.gbv.de/', + ], + 'PAIA' => + [ + 'baseUrl' => 'http://paia.gbv.de/', + 'paiaConditions' => + [ + 'http://purl.org/ontology/paia#PickupCondition' => 'http://data.ub.uni-leipzig.de/resource/DE-15/department/wrongPickup' + ] + ] + ]; + + /** + * Test + * + * @return void + */ + public function testMapOptionsPickupBranchTrue() + { + list($method, $paia) = $this->prepareByReflection(); + + $pickUpId = 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR'; + $pickUpAbout = 'Zweigbibliothek Görlitz'; + $input = [ + 'condition' => [ + self::$pickupCondition => [ + 'option' => [ + 0 => [ + 'id' => $pickUpId, + 'about' => $pickUpAbout + ] + ] + ] + ] + ]; + + $conditions = [ + self::$pickupCondition + ]; + + $output = []; + $method->invokeArgs($paia, [$input, &$output, $conditions]); + + $this->assertEquals($pickUpId, $output['options'][self::$pickupCondition][0]['id'], + "Wrong pickup branch id!" + ); + + $this->assertEquals($pickUpAbout, $output['options'][self::$pickupCondition][0]['about'], + "Wrong pickup branch label!" + ); + } + + /** + * Test + * + * @return void + */ + public function testMapOptionsNewTrue() + { + list($method, $paia) = $this->prepareByReflection(); + + $pickUpId = 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR'; + $pickUpAbout = 'Zweigbibliothek Görlitz'; + $newOptionId = 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/newOption'; + $newOption = 'a new option within new condition'; + $input = [ + 'condition' => [ + self::$pickupCondition => [ + 'option' => [ + 0 => [ + 'id' => $pickUpId, + 'about' => $pickUpAbout + ] + ] + ], + 'http://purl.org/ontology/paia#newCondition' => [ + 'option' => [ + 0 => [ + 'id' => $newOptionId, + 'about' => $newOption + ] + ] + ] + ] + ]; + + $conditions = [ + self::$pickupCondition, + 'http://purl.org/ontology/paia#newCondition' + ]; + + $output = []; + $method->invokeArgs($paia, [$input, &$output, $conditions]); + $this->assertEquals($pickUpId, $output['options'][self::$pickupCondition][0]['id'], + "Wrong pickup branch id!" + ); + $this->assertEquals($pickUpAbout, $output['options'][self::$pickupCondition][0]['about'], + "Wrong pickup branch label!" + ); + $this->assertEquals($newOptionId, $output['options']['http://purl.org/ontology/paia#newCondition'][0]['id'], + "New option id missing!" + ); + $this->assertEquals($newOption, $output['options']['http://purl.org/ontology/paia#newCondition'][0]['about'], + "New option label missing!" + ); + } + + /** + * Test + * + * @return void + */ + public function testMapOptionsPickupBranchFalseNoConditions() + { + list($method, $paia) = $this->prepareByReflection(); + + $pickUpBranch = 'Zweigbibliothek Görlitz'; + $input = [ + 'condition' => [ + self::$pickupCondition => [ + 'option' => [ + 0 => [ + 'id' => 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR', + 'about' => $pickUpBranch + ] + ] + ] + ] + ]; + + $output = []; + $method->invokeArgs($paia, [$input, &$output, []]); + $this->assertFalse(isset($output["options"]), + "Unknown option was mapped" + ); + } + + /** + * Test + * + * @return void + */ + public function testMapOptionsPickupBranchTrueUnknownOptionId() + { + /* validate new options otherwise, e.g. by checking $presentationPickups later */ + list($method, $paia) = $this->prepareByReflection(); + + $pickUpBranch = 'Zweigbibliothek Görlitz'; + $input = [ + 'condition' => [ + self::$pickupCondition => [ + 'option' => [ + 0 => [ + 'id' => 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/fake', + 'about' => $pickUpBranch + ] + ] + ] + ] + ]; + + $conditions = [ + self::$pickupCondition + ]; + + $output = []; + $method->invokeArgs($paia, [$input, &$output, $conditions]); + $this->assertTrue(isset($output["options"]), + "Unknown option was not mapped" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsWithoutOptionConfigurationDefined() + { + $conn = $this->createConnector('fincWithOnePickupBranchOption.json'); + /* default config */ + $conn->setConfig($this->validConfig); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* no option or pickup branch */ + $this->assertTrue(count($filtered) == 0, + "Two many items with options!" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsWithoutPaiaResponseOptions() + { + $conn = $this->createConnector('fincWithoutOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* no option nor pickup branch */ + $this->assertTrue(count($filtered) == 0, + "Two many items with options!" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsAllowUnknownOptions() + { + $conn = $this->createConnector('fincWithUnknownOption.json'); + /* default config */ + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* no option */ + $this->assertTrue(count($filtered) > 0, + "Two many items with options!" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsWrongInstance() + { + $conn = $this->createConnector('fincWithOnePickupBranchOption.json'); + /* default config */ + $conn->setConfig(self::$validConfigWithOptionForOtherInstance); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* no option */ + $this->assertTrue(count($filtered) == 0, + "Two many items with options!" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsWithPickUpBranch() + { + $conn = $this->createConnector('fincWithOnePickupBranchOption.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* check pickupbranch */ + $this->assertTrue(count($filtered[0]["options"][self::$pickupCondition]) > 0, + "No pickup branch mapped!"); + + $this->assertEquals("Zweigbibliothek Görlitz", $filtered[0]["options"][self::$pickupCondition][0]["about"], + "Wrong pickup branch!" + ); + + /* but keep storage id (location) */ + $this->assertEquals("Hochschulbibliothek Zittau", $filtered[0]["location"], + "Wrong pickup branch!" + ); + } + + /** + * Test + * + * @return void + */ + public function testGetMyHoldsWithAnotherPickUpBranch() + { + $conn = $this->createConnector('fincWithAnotherPickupBranchOption.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* check pickupbranch */ + $this->assertTrue(count($filtered[0]["options"][self::$pickupCondition]) > 0, + "No pickup branch mapped!"); + + $this->assertEquals("http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI", $filtered[0]["options"][self::$pickupCondition][0]["id"], + "Wrong pickup branch id!" + ); + + $this->assertEquals("Zweigbibliothek Zittau", $filtered[0]["options"][self::$pickupCondition][0]["about"], + "Wrong pickup branch label!" + ); + + $this->assertEquals("2,50 EUR", $filtered[0]["options"][self::$pickupCondition][0]["amount"], + "Wrong pickup branch amount!" + ); + + /* but keep storage id (location) */ + $this->assertEquals("Hochschulbibliothek Görlitz", $filtered[0]["location"], + "Wrong pickup branch!" + ); + } + + /** + * Test Multiple Options => use first option when displaying items + * + * @return void + */ + public function testGetMyHoldsWithMultipleOptionsPickUpBranch() + { + $conn = $this->createConnector('fincWithMultiplePickupBranchOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $result = $conn->getMyHolds($this->patron); + + $filtered = array_filter( + $result, + function ($resultItem) { + if (isset($resultItem['options'])) { + return true; + } + } + ); + + /* check pickupbranch */ + $this->assertTrue(count($filtered) == 1, + "No pickup branch mapped!"); + + $this->assertEquals("http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwZI", $filtered[0]["options"][self::$pickupCondition][0]["id"], + "Wrong pickup branch id!" + ); + + $this->assertEquals("Zweigbibliothek Zittau", $filtered[0]["options"][self::$pickupCondition][0]["about"], + "Wrong pickup branch label!" + ); + + $this->assertEquals("http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR", $filtered[0]["options"][self::$pickupCondition][1]["id"], + "Wrong pickup branch id!" + ); + + $this->assertEquals("Zweigbibliothek Görlitz", $filtered[0]["options"][self::$pickupCondition][1]["about"], + "Wrong pickup branch label!" + ); + + /* but keep storage id (location) */ + $this->assertEquals("Hochschulbibliothek Görlitz", $filtered[0]["location"], + "Wrong pickup branch!" + ); + } + + /** + * Test + * + * @return void + */ + public function testgetOptionsReturnsIDTrue() + { + $conn = $this->createConnector('fincWithMultiplePickupBranchOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $options['options'][self::$pickupCondition][0]['id'] = 'TESTID'; + + $this->assertEquals( + ['TESTID'], + $conn->getOptions(self::$pickupCondition, $options, true), + "Wrong option value" + ); + + $validOptions[0][0] = 'TESTID'; + $this->assertEquals( + ['TESTID'], + $conn->getOptions(self::$pickupCondition, $options, true, $validOptions), + "Wrong option value" + ); + } + + /** + * Test + * + * @return void + */ + public function testgetOptionsNotOnlyFirstButAll() + { + $conn = $this->createConnector('fincWithMultiplePickupBranchOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $options['options'][self::$pickupCondition][0]['id'] = 'TESTID1'; + $options['options'][self::$pickupCondition][1]['id'] = 'TESTID2'; + + $this->assertEquals( + ['TESTID1', 'TESTID2'], + $conn->getOptions(self::$pickupCondition, $options, false), + "Wrong option value" + ); + + $validOptions[0][0] = 'TESTID1'; + $validOptions[0][1] = 'TESTID2'; + $this->assertEquals( + ['TESTID1', 'TESTID2'], + $conn->getOptions(self::$pickupCondition, $options, false, $validOptions), + "Wrong option value" + ); + + /* first option only label set */ + $options['options'][self::$pickupCondition][0]['id'] = null; + $options['options'][self::$pickupCondition][0]['about'] = 'TESTLABEL1'; + $options['options'][self::$pickupCondition][1]['id'] = 'TESTID2'; + + $this->assertEquals( + ['TESTLABEL1', 'TESTID2'], + $conn->getOptions(self::$pickupCondition, $options, false, null), + "Wrong option value" + ); + } + + /** + * Test + * + * @return void + */ + public function testgetOptionsReturnsLabelTrue() + { + $conn = $this->createConnector('fincWithMultiplePickupBranchOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + $options['options'][self::$pickupCondition][0]['id'] = null; + $options['options'][self::$pickupCondition][0]['amount'] = '2,50 Euro'; + $options['options'][self::$pickupCondition][0]['about'] = 'use label when id missing'; + + $this->assertEquals( + ['use label when id missing'], + $conn->getOptions(self::$pickupCondition, $options, true), + "Wrong option value!" + ); + + /* also return label instead of id when no valid options are given */ + $options['options'][self::$pickupCondition][0]['id'] = 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR'; + $options['options'][self::$pickupCondition][0]['about'] = 'Take label, if parameter valid Options missing'; + $this->assertEquals( + ['Take label, if parameter valid Options missing'], + $conn->getOptions(self::$pickupCondition, $options, true), + "Wrong option value!" + ); + } + + /** + * Test + * + * @return void + */ + public function testgetOptionsReturnsEmptyArray() + { + $conn = $this->createConnector('fincWithMultiplePickupBranchOptions.json'); + $conn->setConfig(self::$validConfigWithPickUpOption); + $conn->init(); + + $options['options'][self::$pickupCondition][0]['id'] = 'http://data.ub.uni-leipzig.de/resource/DE-Zi4/department/zwGR'; + $options['options'][self::$pickupCondition][0]['about'] = 'Zweigbibliothek Görlitz'; + + /* invalid condition */ + $this->assertEquals( + [], + $conn->getOptions('fakeCondition', $options, true), + "Wrong option value!" + ); + + /* no options in result array */ + $this->assertEquals( + [], + $conn->getOptions('fakeCondition', [], true), + "Wrong option value!" + ); + + /* invalid option */ + $validOptions[0][0] = 'xxx'; + $this->assertEquals( + [], + $conn->getOptions(self::$pickupCondition, $options, true, $validOptions), + "Wrong option value!" + ); + + /* no option values */ + $options['options'][self::$pickupCondition][0]['id'] = null; + $options['options'][self::$pickupCondition][0]['about'] = null; + $this->assertEquals( + [""], + $conn->getOptions(self::$pickupCondition, $options, true), + "Wrong option value!" + ); + } + + /** + * Test + * + * @return void + */ + public function testCallMapOptionsTrue() : void + { + $paia = $this->getMockBuilder(PAIAMock::class) + ->enableProxyingToOriginalMethods() + ->setMethods(['mapOptions']) + ->setConstructorArgs([new \VuFind\Date\Converter(), new \Zend\Session\SessionManager()]) + ->getMock(); + + $paia->prepare("fincWithOnePickupBranchOption.json"); + + $paia->expects($this->atLeastOnce()) + ->method('mapOptions'); + + /* only used by holdings so far */ + $paia->getMyHolds($this->patron); + } + + /** + * Test + * + * @return void + */ + public function testCallMapOptionsFalse() : void + { + $paia = $this->getMockBuilder(PAIAMock::class) + ->enableProxyingToOriginalMethods() + ->setMethods(['mapOptions']) + ->setConstructorArgs([new \VuFind\Date\Converter(), new \Zend\Session\SessionManager()]) + ->getMock(); + + $paia->prepare("fincWithOnePickupBranchOption.json"); + + $paia->expects($this->never()) + ->method('mapOptions'); + + $paia->getMyILLRequests($this->patron); + $paia->getMyFines($this->patron); + $paia->getMyStorageRetrievalRequests($this->patron); + $paia->getMyTransactions($this->patron); + } + + /** + * Create connector with fixture file. + * + * @param string $fixture Fixture file + * + * @return Connector + * + * @throws InvalidArgumentException Fixture file does not exist + */ + protected function createConnector($fixture = null) + { + if (empty($fixture) || strpos($fixture, 'finc') === false) { + return parent::createConnector($fixture); + } + + $adapter = new TestAdapter(); + if ($fixture) { + $file = realpath( + __DIR__ . + '/../../../../../fixtures/paia/response/' . $fixture + ); + if (!is_string($file) || !file_exists($file) || !is_readable($file)) { + throw new InvalidArgumentException( + sprintf('Unable to load fixture file: %s ', $file) + ); + } + $response = file_get_contents($file); + $responseObj = HttpResponse::fromString($response); + $adapter->setResponse($responseObj); + } + $service = new \VuFindHttp\HttpService(); + $service->setDefaultAdapter($adapter); + $conn = new PAIAMock( + new \VuFind\Date\Converter(), + new \Zend\Session\SessionManager() + ); + $conn->setHttpService($service); + return $conn; + } + + /** + * @return array + * @throws \ReflectionException + */ + protected function prepareByReflection() + { + /* use reflection due to protected access level */ + $class = new \ReflectionClass('finc\ILS\Driver\PAIA'); + $method = $class->getMethod("mapOptions"); + $method->setAccessible(true); + + $paia = new fincPAIA( + new \VuFind\Date\Converter(), + new \Zend\Session\SessionManager() + ); + + return [$method, $paia]; + } +} + +/* helper class to make protected finc methods public and reuse Vufind mocking approach */ +class PAIAMock extends fincPAIA +{ + public function prepare($fixture) + { + $adapter = new TestAdapter(); + if ($fixture) { + $file = realpath( + __DIR__ . + '/../../../../../fixtures/paia/response/' . $fixture + ); + if (!is_string($file) || !file_exists($file) || !is_readable($file)) { + throw new InvalidArgumentException( + sprintf('Unable to load fixture file: %s ', $file) + ); + } + $response = file_get_contents($file); + $responseObj = HttpResponse::fromString($response); + $adapter->setResponse($responseObj); + } + $service = new \VuFindHttp\HttpService(); + $service->setDefaultAdapter($adapter); + $this->setHttpService($service); + $this->setConfig(PAIAFincTest::$validConfigWithPickUpOption); + parent::init(); + } + + public function parsePAIAJson($file) + { + return $this->paiaParseJsonAsArray($file); + } + + public function myHoldsMapping($items) + { + return parent::myHoldsMapping($items); + } + + public function paiaGetItems($patron, $filter = []) + { + return parent::paiaGetItems($patron, $filter); + } + + public function paiaCheckScope($scope) + { + return true; + } +} diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss index 5de35874c1530a7d73e880eecd1ac4017d48d688..c7c3e947032f2993164611e444831ae815a12345 100644 --- a/themes/finc/scss/compiled.scss +++ b/themes/finc/scss/compiled.scss @@ -2360,6 +2360,11 @@ footer ul { // FOOTER - END // MODALS +//// Prevent transparent lightbox bug -- keep !important to override! +.modal-dialog .mainbody.left { + float: none !important; +} + //// Set modal width to make better use of the screen @media (max-width: $screen-xs-max) { .modal-dialog { diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-additionals.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-additionals.phtml index fbb7def9e17e2281bbec2900454076631813c747..7ee1dc4a8e260244adb97a318820e7075cf26164 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-additionals.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-additionals.phtml @@ -2,23 +2,16 @@ <?php if (!empty($data) && is_array($data)): ?> <?php foreach ($data as $additional) : ?> <?php if (isset($additional['identifier'])): ?> - <tr> - <th> - <?=$this->transEsc($additional['identifier'])?>: - </th> - <td> - <?php if(isset($additional['id']) && $url = $this->recordLink()->getRecordLink($additional['id'],'id')): ?> - <?php if (isset($additional['label']) && !empty($additional['label'])): ?> - <a href="<?=$url?>"><?=$this->escapeHtml($additional['label'])?></a><?=$this->escapeHtml($additional['suffix'])?> - <?php else: ?> - <a href="<?=$url?>"><?=$this->escapeHtml($additional['text'])?></a> - <?php endif; ?> - <?php unset($url) ?> + <?php if(isset($additional['id']) && $url = $this->recordLink()->getRecordLink($additional['id'],'id')): ?> + <?php if (isset($additional['label']) && !empty($additional['label'])): ?> + <a href="<?=$url?>"><?=$this->escapeHtml($additional['label'])?></a><?=$this->escapeHtml($additional['suffix'])?> <?php else: ?> - <?=$this->escapeHtml($additional['text'])?> + <a href="<?=$url?>"><?=$this->escapeHtml($additional['text'])?></a> <?php endif; ?> - </td> - </tr> + <?php unset($url) ?> + <?php else: ?> + <?=$this->escapeHtml($additional['text'])?> + <?php endif; ?> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-otherRelationshipEntry.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-otherRelationshipEntry.phtml index e57ce111e50868f7d50cc4ee96f7b2cb2054e976..7ec5de28450e5453f4c1bada47f171d41c67266f 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-otherRelationshipEntry.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-otherRelationshipEntry.phtml @@ -1,19 +1,14 @@ <!-- finc: RecordDriver - DefaultRecord - data-otherRelationshipEntry --> <?php if (!empty($data)): ?> <?php foreach ($data as $subject => $values): ?> - <tr> - <th><?=$this->transEsc($values[0]['subject'])?>: </th> - <td> - <?php foreach ($values as $v): ?> - <?php if (isset($v['id'])): ?> - <a href="<?=$this->recordLink()->getUrl($v['id'])?>"><?=$this->escapeHtml($v['text'])?></a> - <?php else: ?> - <?=$this->escapeHtml($v['text'])?> - <?php endif; ?> - <br/> - <?php endforeach; ?> - </td> - </tr> + <?php foreach ($values as $v): ?> + <?php if (isset($v['id'])): ?> + <a href="<?=$this->recordLink()->getUrl($v['id'])?>"><?=$this->escapeHtml($v['text'])?></a> + <?php else: ?> + <?=$this->escapeHtml($v['text'])?> + <?php endif; ?> + <br/> + <?php endforeach; ?> <?php endforeach; ?> <?php endif; ?> <!-- finc: RecordDriver - DefaultRecord - data-otherRelationshipEntry - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-titleUniform.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-titleUniform.phtml index 237b057941bb913d62df0b1a3df44b3db681e6a5..3ff56c5bf39a67b811014ef6cb7b15a3f0528ba3 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-titleUniform.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-titleUniform.phtml @@ -1,25 +1,16 @@ <!-- finc: RecordDriver - DefaultRecord - data-titleUniform --> <?php if (!empty($data)): ?> - <tr> - <th> - <?=$this->driver->isRDA() - ? $this->transEsc('rda_original_title') - : $this->transEsc('non_rda_original_title')?>: - </th> - <td property="title"> - <?php if (is_array($data)): ?> - <?php if ($data['titleUniform']): ?> - <a href="<?= $this->record($this->driver)->getLink('titleUniform', $data['titleUniform']) ?>"> - <?php endif; ?> - <?= $this->escapeHtml($data['title']) ?> - <?php if ($data['titleUniform']): ?> - </a> - <?php endif; ?> - <?php if (isset($data['lang'])): ?> ⟨<?= $this->escapeHtml($data['lang']) ?>⟩<?php endif; ?> - <?php else: ?> - <a href="<?=$this->record($this->driver)->getLink('title', $data)?>"><?=$this->escapeHtml($data)?></a> - <?php endif; ?> - </td> - </tr> + <?php if (is_array($data)): ?> + <?php if (isset($data['titleUniform'])): ?> + <a href="<?= $this->record($this->driver)->getLink('titleUniform', $data['titleUniform']) ?>"> + <?php endif; ?> + <?= $this->escapeHtml($data['title']) ?> + <?php if (isset($data['titleUniform'])): ?> + </a> + <?php endif; ?> + <?php if (isset($data['lang'])): ?> ⟨<?= $this->escapeHtml($data['lang']) ?>⟩<?php endif; ?> + <?php else: ?> + <a href="<?=$this->record($this->driver)->getLink('title', $data)?>"><?=$this->escapeHtml($data)?></a> + <?php endif; ?> <?php endif; ?> <!-- finc: RecordDriver - DefaultRecord - data-titleUniform - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml index c3381f131baaa6724b1c7ca2ea84bad9af34e510..d0d8326caaef4bbc5d495c3e14216d16edcce82e 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml @@ -4,11 +4,11 @@ $id = $this->driver->getUniqueId(); $source = $this->driver->getSourceIdentifier(); if (isset($this->list) && is_object($this->list)) { - $list_id = $this->list->id; - $user_id = $this->list->user_id; + $list_id = $this->list->id; + $user_id = $this->list->user_id; } else { - $list_id = null; - $user_id = $this->user ? $this->user->id : null; + $list_id = null; + $user_id = $this->user ? $this->user->id : null; } // finc: next line finc-specific; required to display public favorites lists, #12052, see also below - CK $isEditable = $this->user && $this->user->id === $user_id; @@ -91,15 +91,15 @@ if ($cover): <div class="last"> <?php if (!$this->driver->isCollection()) { - if ($snippet = $this->driver->getHighlightedSnippet()) { - if (!empty($snippet['caption'])) { - echo '<strong>' . $this->transEsc($snippet['caption']) . ':</strong> '; - } - if (!empty($snippet['snippet'])) { - echo '<span class="quotestart">“</span>...' . $this->highlight($snippet['snippet']) . '...<span class="quoteend">”</span><br/>'; - } - } - } ?> + if ($snippet = $this->driver->getHighlightedSnippet()) { + if (!empty($snippet['caption'])) { + echo '<strong>' . $this->transEsc($snippet['caption']) . ':</strong> '; + } + if (!empty($snippet['snippet'])) { + echo '<span class="quotestart">“</span>...' . $this->highlight($snippet['snippet']) . '...<span class="quoteend">”</span><br/>'; + } + } + } ?> <?php $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags( null === $list_id ? true : $list_id, // get tags for all lists if no single list is selected @@ -126,7 +126,7 @@ if ($cover): <strong><?=$this->transEsc('Saved in')?>:</strong> <?php $i = 0; foreach ($this->lists as $current): ?> - <a href="<?=$this->url('userList', ['id' => $current->id])?>"><?=$this->escapeHtml($current->title)?></a><?php if($i++ < count($this->lists) - 1): ?>,<?php endif; ?> + <a href="<?=$this->url('userList', ['id' => $current->id])?>"><?=$this->escapeHtml($current->title)?></a><?php if ($i++ < count($this->lists) - 1): ?>,<?php endif; ?> <?php endforeach; ?> <br/> <?php endif; ?> @@ -174,8 +174,8 @@ if ($cover): <?php endif; ?> <?php if (!is_array($urls)) { - $urls = []; - } + $urls = []; + } if (!$this->driver->isCollection()): foreach ($urls as $current): ?> <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" @@ -208,16 +208,45 @@ if ($cover): : $this->url('userList', ['id' => $list_id]); $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&source=' . urlencode($source); + /* #17712 not necessary to fetch items after deleting by ajax in controller */ + $deleteUrl .= '?layout=lightbox'; $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id); ?> <div class="dropdown"> - <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> <a class="dropdown-toggle" id="<?=$dLabel?>" role="button" data-toggle="dropdown" href="<?=$deleteUrlGet?>"> - <?=$this->transEsc('Delete')?> + <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> + <a class="dropdown-toggle" id="<?= $dLabel ?>" + role="button" data-toggle="dropdown" + href="<?= $deleteUrlGet ?>"> + <?= $this->transEsc('Delete') ?> </a> - <ul class="dropdown-menu" role="menu" aria-labelledby="<?=$dLabel?>"> + <ul class="dropdown-menu" role="menu" aria-labelledby="<?= $dLabel ?>"> <li> - <a href="javascript:" onClick="$.post('<?=$deleteUrl?>', {'delete':'<?=$this->escapeJs($id)?>','source':'<?=$this->escapeJs($source)?>','confirm':true},function(){location.reload(true)})" title="<?=$this->transEsc('confirm_delete_brief')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> - <li><a href="javascript:document.getElementById('<?=$dLabel?>').focus();"><?=$this->transEsc('confirm_dialog_no')?></a></li> + <?php /* #17711 give user feedback and dont reload page after deleting */ ?> + <a title="<?= $this->transEsc('confirm_delete_brief') ?>" onClick="$.post( + '<?= $deleteUrl ?>', + { + 'delete':'<?= $this->escapeJs($id) ?>', + 'source':'<?= $this->escapeJs($source) ?>', + 'confirm':true + }, function(){ + $('input[value=<?=$id?>]').parent().remove(0); + <?php /* reset needed for possible old MyResearchController::mylistAction in instances withot clearMessages */ ?> + <?php if (null === $list_id) :?> + VuFind.lightbox.alert('<?= $this->transEsc('Item removed from favorites')?>', 'success'); + <?php else :?> + VuFind.lightbox.alert('<?= $this->transEsc('Item removed from list')?>', 'success'); + <?php endif; ?> + }).fail(function(data) { + $('.fa-spinner.fa-spin').removeClass('fa-spinner fa-spin').addClass('fa-trash-o'); + VuFind.lightbox.alert( + '<?= $this->transEsc('Delete') . ' ' . $this->transEsc('of') . ' ' . htmlspecialchars($this->record($this->driver)->getTitleHtml()) . ': ' . $this->transEsc('errorcode_error')?>', + 'danger' + ) + }); + $(this).closest('.dropdown').find('.fa-trash-o').removeClass('fa-trash-o').addClass('fa-spinner fa-spin');"> + <?= $this->transEsc('confirm_dialog_yes') ?> + </a> + </li> </ul> </div> diff --git a/themes/finc/templates/RecordDriver/SolrLido/data-events.phtml b/themes/finc/templates/RecordDriver/SolrLido/data-events.phtml index 8030e6f371c7e39bc1798efa91466c96d07d19bb..504ed249498f2ff21ae3a604253a8ccc1b6936ab 100644 --- a/themes/finc/templates/RecordDriver/SolrLido/data-events.phtml +++ b/themes/finc/templates/RecordDriver/SolrLido/data-events.phtml @@ -1,18 +1,5 @@ -<?php $publicationIsSet = false; ?> -<?php if (count($event = $this->driver->getEvents()) > 0): ?> - <?php foreach ($event as $eventType => $events): ?> - <tr class="recordEvents"> - <th> - <?php if ($eventType == 'production'): ?> - <?=$this->transEsc('expression creation')?>: - <?php elseif ($eventType == 'publication'): ?> - <?=$this->transEsc('Time of origin')?>: - <?php $publicationIsSet = true; ?> - <?php else: ?> - <?=$this->transEsc($eventType)?>: - <?php endif; ?> - </th> - <td> + <?php foreach ($data as $eventType => $events): ?> + <?php $publicationIsSet = $eventType === 'Publication'; ?> <div class="truncate-field"> <?php foreach ($events as $event): ?> <?php if ($event != $events[0]): ?><br/><?php endif; ?> @@ -36,7 +23,4 @@ <?php if (!empty($event['description'])): ?><?=$this->escapeHtml($event['description'])?><br/><?php endif; ?> <?php endforeach; ?> </div> - </td> - </tr> <?php endforeach; ?> -<?php endif; ?> diff --git a/themes/finc/templates/RecordTab/holdingsils.phtml b/themes/finc/templates/RecordTab/holdingsils.phtml index f2788c10708ecb7d90c825df1723e21e5fe952a5..0d631f88d4fdcc04b4d869b7d208cf1fe9e8bd83 100644 --- a/themes/finc/templates/RecordTab/holdingsils.phtml +++ b/themes/finc/templates/RecordTab/holdingsils.phtml @@ -179,7 +179,7 @@ if (!empty($holdingTitleHold)): ?> <h2><?=$this->transEsc("external_access")?></h2> <?php if (!empty($extUrls)): ?> <?php foreach ($extUrls as $current): ?> - <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($this->transEsc($current['desc']))?></a> + <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->translate($current['desc'])?></a> <br/> <?php endforeach; ?> <?php endif; ?>