diff --git a/config/vufind/DAIA.ini b/config/vufind/DAIA.ini index c1d828105cdb43a1add459a4646b8cfd8a2e0e07..20861f3794f19c6c8ad9b417e56d2cafa868048e 100644 --- a/config/vufind/DAIA.ini +++ b/config/vufind/DAIA.ini @@ -1,2 +1,41 @@ -[Global] -baseUrl = [your DAIA server base url] \ No newline at end of file +; DAIA driver expects a DAIA Query API as specified in: +; http://gbv.github.io/daiaspec/daia.html#query-api +; +; The settings in the [DAIA] section will be used for all DAIA requests. +; The name of this section got refactored with VuFind 2.4, although the old +; configuration using the [Global] section still works, it should be replaced +; with the new [DAIA] section. +; Note: Settings for daiaResponseFormat and daiaIdPrefix are not supported if +; a pre VuFind 2.4 configuration with the [Global] section is used. +; i.e.: +; [Global] +; baseUrl = [your DAIA server base url] +; daiaIdPrefix = [this setting will have no effect] +; daiaResponseFormat = [this setting will have no effect] +; +; A default DAIA call looks like this: +; https://daia.myuniversity.edu/?id=ppn:12345678&format=json +; +; This default DAIA call would be configured as: +; [DAIA] +; baseUrl = https://daia.myuniversity.edu +; daiaidprefix = ppn: +; daiaResponseFormat = json +; + +[DAIA] +; The base URL for the DAIA webservice. +baseUrl = [your DAIA server base url] + +; The prefix prepended to the VuFind record Id resulting in the document URI +; used for the DAIA request (default = ppn:) (the prefix usually defines the +; field which the DAIA server uses for the loookup - e.g. ppn: or isbn:). +;daiaIdPrefix = "ppn:" + +; Set the requested DAIA response format: xml (default), json +;daiaResponseFormat = xml + +; Set multiQuery to true if your DAIA webservice supports queries with multiple +; ids (cf. http://gbv.github.io/daiaspec/daia.html#query-api). +; Default is false. +;multiQuery = false \ No newline at end of file diff --git a/local/alpha/config/vufind/FincDAIA.ini b/local/alpha/config/vufind/FincILS.ini similarity index 65% rename from local/alpha/config/vufind/FincDAIA.ini rename to local/alpha/config/vufind/FincILS.ini index 95dfb04cbd6c263b16adbe1c6ab5f9b1b64ecab6..7e1ea6671bf0048c3b988f6bf50f6f4a02bca71b 100644 --- a/local/alpha/config/vufind/FincDAIA.ini +++ b/local/alpha/config/vufind/FincILS.ini @@ -8,7 +8,7 @@ ; [Parent_Config] -relative_path = ../../../config/vufind/FincDAIA.ini +relative_path = ../../../config/vufind/FincILS.ini ; A comma-separated list of config sections from the parent which should be ; completely overwritten by the equivalent sections in this configuration; @@ -20,21 +20,3 @@ relative_path = ../../../config/vufind/FincDAIA.ini ; ;##################### DO NOT DELETE THIS HEADER #################### ;#################################################################### - -;[DAIA] -; this config configures the extended DAIA-Driver FincDAIA -; refer to ticket #4499 for further info on configuring it - -; config-examples: - -; DE-15 -; baseUrl = http://data.ub.uni-leipzig.de/item/DE-15/barcode/ -; ilsIdentifier = "barcode" -; ISIL = "DE-15" - -; DE-Gla 1 -; baseUrl = http://139.18.19.238:8080/DaiaThecaMssql/rs/DE-Gla%201/daia/ -; ilsIdentifier = "record_id" - -; default -; ilsIdentifier = "default" \ No newline at end of file diff --git a/local/alpha/config/vufind/SolrAI.ini b/local/alpha/config/vufind/SolrAI.ini new file mode 100644 index 0000000000000000000000000000000000000000..2860cc758552e04bd002acc05e4046f6da6ab528 --- /dev/null +++ b/local/alpha/config/vufind/SolrAI.ini @@ -0,0 +1,22 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default ALPHA-INI-file and inherits +; all the settings from the INI-file defined in [Parent_Config] which +; points to the default INI-file located in the folder vufind2/local +; + +[Parent_Config] +relative_path = ../../../config/vufind/SolrAI.ini + +; A comma-separated list of config sections from the parent which should be +; completely overwritten by the equivalent sections in this configuration; +; any sections not listed here will be merged on a section-by-section basis. +;override_full_sections = "Languages,AlphaBrowse_Types" + +; +; Add ALPHA-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### diff --git a/local/alpha/config/vufind/SolrMarcRemoteFinc.ini b/local/alpha/config/vufind/SolrMarcRemoteFinc.ini new file mode 100644 index 0000000000000000000000000000000000000000..f5359f864a5102010135e74cc64f7159e6576caf --- /dev/null +++ b/local/alpha/config/vufind/SolrMarcRemoteFinc.ini @@ -0,0 +1,22 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default ALPHA-INI-file and inherits +; all the settings from the INI-file defined in [Parent_Config] which +; points to the default INI-file located in the folder vufind2/local +; + +[Parent_Config] +relative_path = ../../../config/vufind/SolrMarcRemoteFinc.ini + +; A comma-separated list of config sections from the parent which should be +; completely overwritten by the equivalent sections in this configuration; +; any sections not listed here will be merged on a section-by-section basis. +;override_full_sections = "Languages,AlphaBrowse_Types" + +; +; Add ALPHA-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### diff --git a/local/alpha/languages/en.ini b/local/alpha/languages/en.ini new file mode 100644 index 0000000000000000000000000000000000000000..c72b1004e811dfd31a573a5652918e76cd23763b --- /dev/null +++ b/local/alpha/languages/en.ini @@ -0,0 +1,18 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is a default ALPHA-LANGUAGE-file and inherits +; all the settings from the LANGUAGE-file defined in @parent_ini which +; points to the default LANGUAGE-file located in the folder +; vufind2/local/languages +; + +@parent_ini = "../../languages/en.ini" + +; +; Add ALPHA-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### + diff --git a/local/config/vufind/FincDAIA.ini b/local/config/vufind/FincDAIA.ini deleted file mode 100644 index 3ecfc707be66a67f0770fb0f6e6c0595360f7a89..0000000000000000000000000000000000000000 --- a/local/config/vufind/FincDAIA.ini +++ /dev/null @@ -1,25 +0,0 @@ -;[DAIA] -; this config configures the extended DAIA-Driver FincDAIA -; refer to ticket #4499 for further info on configuring it -;baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ -;ilsIdentifier = "default" -;ISIL = "ISIL" - -; config-examples: - -; DE-15 -; baseUrl = http://data.ub.uni-leipzig.de/item/DE-15/barcode/ -; ilsIdentifier = "barcode" -; ISIL = "DE-15" - -; DE-Gla 1 -; baseUrl = http://139.18.19.238:8080/DaiaThecaMssql/rs/DE-Gla%201/daia/ -; ilsIdentifier = "record_id" - -; default -; ilsIdentifier = "default" - -[Global] -; The base URL for the DAIA webservice. -; refer to http://data.ub.uni-leipzig.de/ldinfo for setting up specific DAIA-urls -baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ diff --git a/local/config/vufind/FincILS.ini b/local/config/vufind/FincILS.ini new file mode 100644 index 0000000000000000000000000000000000000000..ac4fc97621ee5a87639e641f0c4fff6b93b3a4a5 --- /dev/null +++ b/local/config/vufind/FincILS.ini @@ -0,0 +1,40 @@ +;DAIA configuration +;[DAIA] +; The base URL for the DAIA webservice. +;baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ +; The prefix prepended to the VuFind record Id resulting in the document URI +; used for the DAIA request (default = ppn:) (the prefix usually defines the +; field which the DAIA server uses for the loookup - e.g. ppn: or isbn:). +;daiaIdPrefix = "ppn:" +; Set the requested DAIA response format: xml (default), json +;daiaResponseFormat = xml + +;FincILS configuration +; refer to ticket #4499 for further info on configuring it +;ilsIdentifier = "default" + +; config-examples: + +; DE-15 +; baseUrl = http://data.ub.uni-leipzig.de/item/DE-15/barcode/ +; ilsIdentifier = "barcode" + +; DE-Gla 1 +; baseUrl = http://139.18.19.238:8080/DaiaThecaMssql/rs/DE-Gla%201/daia/ +; ilsIdentifier = "record_id" + +; default +; ilsIdentifier = "default" + + +; For Legacy support use the [Global] section +;[Global] +; The base URL for the DAIA webservice. +; refer to http://data.ub.uni-leipzig.de/ldinfo for setting up specific DAIA-urls +;baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ + + +;PAIA configuration +;[PAIA] +; base URL of the PAIA server +;baseUrl = http://data.ub.uni-leipzig.de/item/ISIL/identifier/ \ No newline at end of file diff --git a/local/config/vufind/SolrAI.ini b/local/config/vufind/SolrAI.ini new file mode 100644 index 0000000000000000000000000000000000000000..4ace37943b27db4a43d7ff56b53c40aa90900b4a --- /dev/null +++ b/local/config/vufind/SolrAI.ini @@ -0,0 +1,3 @@ +[General] +; Set the URI-pattern of the server which serves the raw Ai-record-data. +baseUrl = "https://ai.ub.uni-leipzig.de/blob?%s" diff --git a/local/config/vufind/SolrMarcRemoteFinc.ini b/local/config/vufind/SolrMarcRemoteFinc.ini new file mode 100644 index 0000000000000000000000000000000000000000..81cb438500faa7f5eda38dc7a8ed40d9f8dcdc2c --- /dev/null +++ b/local/config/vufind/SolrMarcRemoteFinc.ini @@ -0,0 +1,3 @@ +[General] +; Set the URI-pattern of the server which serves the raw Marc-data. +baseUrl = http://172.18.113.147/%s \ No newline at end of file diff --git a/local/config/vufind/config.ini b/local/config/vufind/config.ini index 7ccc30fb3a66e64bb5772aca9054c6bec6e02032..a9472b017b70f795962056998114c191450f3651 100644 --- a/local/config/vufind/config.ini +++ b/local/config/vufind/config.ini @@ -1145,6 +1145,26 @@ max_tag_length = 64 ;sigel = "15" ;isil = "DE-15" ;bik = "952000-4" + +; This section defines libraries forms a group. +;[LibraryGroup] +;libraries = DE-15,DE-15-292,DE-15-100 + +; This section contains all site related customization for finc +;[CustomSite] +; Combine more formats to one css class. If false first format entry will taken +; to display icon symbol +;combinedIcons = false +;namespace = ubl + +; This section contains all index related customizations for finc +;[CustomIndex] +; Special settings to control single instances of libaries within one vufind +; installation. +;indexExtension = "de15" ; for solr index of hmt +; take general format field of Solr index. If false it takes the format fields +; with index extension defined above. +;generalFormats = true ;for ubl & htwk it should be true ; ***************** ; * EOF finc ; ***************** diff --git a/local/dev/config/vufind/FincDAIA.ini.sample b/local/dev/config/vufind/FincILS.ini.sample similarity index 65% rename from local/dev/config/vufind/FincDAIA.ini.sample rename to local/dev/config/vufind/FincILS.ini.sample index 8bb38a75fb8be3a0ee69b0df2d0ffddd0fc5b709..06bbd78fc7ca70e06c081f5dc631c3022b37abdd 100644 --- a/local/dev/config/vufind/FincDAIA.ini.sample +++ b/local/dev/config/vufind/FincILS.ini.sample @@ -8,7 +8,7 @@ ; [Parent_Config] -relative_path = ../../../config/vufind/FincDAIA.ini +relative_path = ../../../config/vufind/FincILS.ini ; A comma-separated list of config sections from the parent which should be ; completely overwritten by the equivalent sections in this configuration; @@ -20,21 +20,3 @@ relative_path = ../../../config/vufind/FincDAIA.ini ; ;##################### DO NOT DELETE THIS HEADER #################### ;#################################################################### - -;[DAIA] -; this config configures the extended DAIA-Driver FincDAIA -; refer to ticket #4499 for further info on configuring it - -; config-examples: - -; DE-15 -; baseUrl = http://data.ub.uni-leipzig.de/item/DE-15/barcode/ -; ilsIdentifier = "barcode" -; ISIL = "DE-15" - -; DE-Gla 1 -; baseUrl = http://139.18.19.238:8080/DaiaThecaMssql/rs/DE-Gla%201/daia/ -; ilsIdentifier = "record_id" - -; default -; ilsIdentifier = "default" \ No newline at end of file diff --git a/local/dev/config/vufind/SolrAI.ini.sample b/local/dev/config/vufind/SolrAI.ini.sample new file mode 100644 index 0000000000000000000000000000000000000000..84c18ffc83b80a63f674b8a90e7dd3d420a46b1d --- /dev/null +++ b/local/dev/config/vufind/SolrAI.ini.sample @@ -0,0 +1,26 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default DEV-INI-file and inherits +; all the settings from the INI-file defined in [Parent_Config] which +; points to the default INI-file located in the folder vufind2/local +; + +;[Parent_Config] +;relative_path = ../../../config/vufind/SolrAI.ini + +; A comma-separated list of config sections from the parent which should be +; completely overwritten by the equivalent sections in this configuration; +; any sections not listed here will be merged on a section-by-section basis. +;override_full_sections = "Languages,AlphaBrowse_Types" + +; +; Add DEV-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### + +[General] +; base url to ai blob server +baseUrl = "https://ai.ub.uni-leipzig.de/blob?%s" diff --git a/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample b/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample new file mode 100644 index 0000000000000000000000000000000000000000..7a04483f3cc6179d48e78bc39d1f24d04d5d4a47 --- /dev/null +++ b/local/dev/config/vufind/SolrMarcRemoteFinc.ini.sample @@ -0,0 +1,22 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is the default DEV-INI-file and inherits +; all the settings from the INI-file defined in [Parent_Config] which +; points to the default INI-file located in the folder vufind2/local +; + +[Parent_Config] +relative_path = ../../../config/vufind/SolrMarcRemoteFinc.ini + +; A comma-separated list of config sections from the parent which should be +; completely overwritten by the equivalent sections in this configuration; +; any sections not listed here will be merged on a section-by-section basis. +;override_full_sections = "Languages,AlphaBrowse_Types" + +; +; Add DEV-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### diff --git a/local/dev/config/vufind/config.ini.sample b/local/dev/config/vufind/config.ini.sample index 1f3c3a72f60fb0b3a09401cb46e9ee8cb38781bc..8f5345b61d2b4489822e474f9b24048cf880b7f8 100644 --- a/local/dev/config/vufind/config.ini.sample +++ b/local/dev/config/vufind/config.ini.sample @@ -41,3 +41,16 @@ hash_passwords = "1" encrypt_ils_password = "1" ils_encryption_key = "3e2ac9b644dd4e0b64b179a0309ead13bb59202a" + +; OpenURL configuration institution specified +; sample configuration for ubl +[OpenURL] +url = "http://www.redi-bw.de/links/ubl?rl_site=ubl" +rfr_id = www.ub.uni-leipzig.de +resolver = redi +window_settings = "toolbar=no,location=no,directories=no,buttons=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=550,height=600" +show_in_results = false ; include in search results +show_in_record = false ; include in core record metadata +show_in_holdings = true ; include in holdings tab of record view +embed = true +replace_other_urls = true diff --git a/local/dev/languages/en.ini b/local/dev/languages/en.ini new file mode 100644 index 0000000000000000000000000000000000000000..2298100b0c6da33cbe4caf532d408c1c038a8ebd --- /dev/null +++ b/local/dev/languages/en.ini @@ -0,0 +1,18 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2015 ############## +; +; This is a default DEV-LANGUAGE-file and inherits +; all the settings from the LANGUAGE-file defined in @parent_ini which +; points to the default LANGUAGE-file located in the folder +; vufind2/local/languages +; + +@parent_ini = "../../languages/en.ini" + +; +; Add DEV-specific customization after this header. +; +;##################### DO NOT DELETE THIS HEADER #################### +;#################################################################### + diff --git a/local/languages/de.ini b/local/languages/de.ini index a03b05e9b0de1de75f0583866e0a5e44bd4b55d2..b58150081b70812dd392a1ea9d37e438a6c68b7e 100644 --- a/local/languages/de.ini +++ b/local/languages/de.ini @@ -1763,3 +1763,61 @@ Zapotec = Zapotekisch Zhuang = Zhuang Zulu = Zulu Zuni = Zuni + +nep_misc = "Allgemeines" +nep_bio = "Biologie" +nep_geo = "Geographie, Geowissenschaften" +nep_hist = "Geschichte" +nep_art = "Kunst" +nep_ling = "Literatur- und Sprachwissenschaft" +nep_math = "Mathematik, Informatik" +nep_med = "Medizin" +nep_mus = "MusiK" +nep_nat = "Naturwissenschaften, Chemie, Physik, Technik" +nep_paed = "Pädagogik" +nep_phil = "Philosophie" +nep_pol = "Politik" +nep_psy = "Psychologie" +nep_jur = "Recht" +nep_soz = "Soziologie" +nep_sport = "Sport" +nep_vet = "Tiermedizin" +nep_theo = "Theologie" +nep_ethno = "Volks- und Völkerkunde" + +Category = "Fachgebiet" +Check interlibrary loans = "Fernleihe prüfen" +Check availability via interlibrary loans = "Verfügbarkeit über Fernleihe prüfen" +Initiate purchase order = "Beschaffungsauftrag auslösen" +Please purchase = "Bitte beschaffen" + +;------ +;missing lines from en.ini (compiled from files in /usr/local/vufind2/languages, /usr/local/vufind2/local/dev/languages) +;------ +note_762 = Subseries +note_767 = Translation +note_776 = Additional form +password_error_not_unique = "Password was not changed" +password_maximum_length = "Maximum password length is %%maxlength%% characters" +password_minimum_length = "Minimum password length is %%minlength%% characters" +pronounced = pronounced +# Add to favorites = Add to Favorites +# Author = Creator +Bereich = Bereich +# Brazilian Portuguese = Português (Brasil) +Brazilian Portuguese = "Portugiesisch (Brasilien)" +Document delivery - Medicine = "Dokumentlieferung - Medizin" +# German = Deutsch +Interlibrary loan - Medicine = "Fernleihe - Medizin" +Kollektion = Kollektion +Kontakt = Kontakt +Participants = Mitwirkende +Search RVK-Online = "Suche in RVK-Online" +Zweigbibliothek = Zweigbibliothek +Fachgebiet = Fachgebiet +history = Suchverlauf +renew_item_maximum = "There is a maximum of three renewals allowed." +guser_dunning_process = "User account is blocked due to a dunning process" + +################### finc-spezifisch inzugefuegt ########################## +p. = S. \ No newline at end of file diff --git a/local/languages/en.ini b/local/languages/en.ini new file mode 100644 index 0000000000000000000000000000000000000000..e29e8218f402a95beb73b6ead18b91cde5f11149 --- /dev/null +++ b/local/languages/en.ini @@ -0,0 +1,1767 @@ +AND = AND +APA Citation = "APA Citation" +Access = Access +Account = Account +Add = Add +Add a Note = "Add a Note" +# Add to favorites = "Add to Favorites" +Add to favorites = "Favorite" +Add your comment = "Add your Comment" +Address = Address +Address Additional = "Address Addendum" +Advanced = Advanced +Audience = Audience +Audio = Audio +# Author = Creator +Author = "Person / Institution" +Available = Available +Awards = Awards +Backtrace = Backtrace +Bag = Bag +Balance = Balance +Bereich = Area +Bibliography = Bibliography +Book = Book +Bookchapter = "Book Chapter" +Bookmark = Bookmark +Books = Books +Braille = Braille +# Brazilian Portuguese = "Português (Brasil)" +Brazilian Portuguese = "Portuguese (Brazil)" +Browse = Browse +Browse the Collection = "Browse the Collection" +By = By +By Alphabetical = "In Alphabetic Order" +CD = CD +Cannot find similar records = "Cannot find similar records." +Cassette = Cassette +Checkedoutpermanent = "Checked Out Permanent Items" +# Chinese = "ä¸æ–‡ï¼ˆç¹é«”)" +Chinese = Chinese +Collection mode = "" +Clock = "" +Code = Code +Comments = Comments +Contents = Contents +Copies = Copies +Copy = Copy +Course = Course +Create New Account = "Create New Account" +Created = Created +DVD = DVD +Date = Date +Days = Days +Delete = Delete +Department = Department +Description = Description +Desired Username = "Desired User Name" +Displaying the top = "Displaying the Top" +Document delivery = "Document Delivery" +Document delivery - Medicine = "Document Delivery - Medicine" +Due = Due +# Dutch = Nederlands +Dutch = Dutch +Edit = Edit +Edition = Edition +Electronic = "Electronic Resource" +Electronic Resources = "Electronic Resources" +Electronicjournal = "Electronic Journal" +Email = E-Mail +Email Address = "E-Mail Address" +Email Record = "E-Mail Record" +Email address is invalid = "E-mail address is invalid" +Email this = "E-mail this" +Email this Search = "E-Mail this Search" +Empty = Empty +English = English +Era = Era +Extent Author = [et al.] +Excerpt = Excerpt +Expires = Expires +Export = Export +Favorites = Favorites +Fee = Fee +Find = Find +Fine = Fine +Fines = Fines +Format = "Type of Resource" +Former catalogue = "Former Catalog" +# French = Français +French = French +Full description = "Full Description" +full text = "Full Text" +Genre = Genre +Geography = Geography +# German = Deutsch +German = German +Get full text = "Get additional information online" +Globe = Globe +# Greek = Ελληνικά +Greek = Greek +Grid = Grid +Group = Group +# Hebrew = עברית +Hebrew = Hebrew +History = History +Holdings = Holdings +Holdings details from = "Holdings Details from" +Holdnep-Submit = "Patron Driven Acquisition" +Holds = Holds +Home = Home +ISBN = ISBN +ISBN/ISSN = "ISBN / ISSN" +ISSN = ISSN +Illustrated = Illustrated +In order to establish you account profile, please enter the following information = "In order to establish your account profile, please enter the following information" +Institution = Institution +Instructor = Instructor +Internet = Internet +Interlibrary loan = "Interlibrary Loan" +Interlibrary loan information = "Interlibrary Loan Information" +Interlibrary loan - Medicine = "Interlibrary Loan - Medicine" +Interlibraryloans = "Interlibrary Loans" +Invalid Recipient Email Address = "Invalid recipient e-mail address" +Invalid Sender Email Address = "Invalid sender e-mail address" +# Irish = Gaeilge +Irish = Irish +Issue = Issue +# Italian = Italiano +Italian = Italian +# Japanese = 日本語 +Japanese = Japanese +Journal = Journal +Journals = Journals +Kit = Kit +Kollektion = Collection +Kontakt = Contact +Language = Language +Library = Library +Library Catalog Username = "Library Catalog User Name" +Limit To = "Limit to" +Link to = "To Website" +List = List +Loading = Loading +Located = Located +Location = Location +Lockaccount = "Block my Account" +Login = "My Account" +Logout = Logout +Manuscript = Manuscript +Map = Map +Maps = Maps +Membercode = "Library Card No." +Message = Message +Message From Sender = "Message from Sender" +Microfilm = Microfilm +More Summon results = "More Summon Results" +More catalog results = "More Catalog Results" +More options = "More Options" +Multiple Branches = "" +Multiple Call Numbers = "" +MyResearch Help = "MyResearch Help" +NOT = NOT +Narrow Search = "Refine my Results" +Nep = "Acquisition" +Newspaper = Newspaper +Next = Next +No Preference = "No Preference" +No Tags = "No Tags" +No citations are available for this record = "No citations are available for this record." +No reviews were found for this record = "No reviews were found for this record." +Not On Reserve = "Not on Reserve" +Note = Note +Notes = Notes +Number = Number +OR = OR +On Reserve - Ask at Circulation Desk = "On Reserve - Please contact staff". +On Site = Library +Participants = Contributors +Password = Password +Password Again = "Repeat new password" +Password Change = "Change Password" +Password Current = "Current password" +Password New = "New password" +Password Strength = "Password strength" +Past = Past +Permanent Checked Out Items = "Checked Out Permanent Items" +Photo = Photo +Place a Hold = "Request" +Please check back soon = "Please check back soon." +Please contact the Library Reference Department for assistance = "Please contact staff." +# Portuguese = "Português" +Portuguese = Portuguese +Prev = Prev +Print = Print +Private = Private +Profile = Profile +Provider = Provider +Public = Public +Publication = Publication +Published = published +PubList = "Public List" +Range = Range +Range slider = "Range Slider" +Reader Card = "Library Card No." +Readingroommedia = "Reading Room Items" +Readyforcollection = "Circulation Desk" +Readyforcollectionstacks = "Circulation Desk" +Recall This = "Recall this" +Recent acquisitions = "Recent Acquisitions" +Region = Region +Remove all Filters = "remove all Filters" +Reserves = Reserves +Results per page = "Results per Page" +Reviews = Reviews +RVK = "RVK Notation" +Save = Save +Search = Search +Search For = "Search for" +Search For Items on Reserve = "Search for Items on Reserve" +Search RVK-Online = "Search via RVK-Online" +See also = "See Also" +Select your carrier = "Select your Carrier" +Selfcheckout = "Self-Checkout" +Send = Send +Serial = Serial +Series = Series +Set = Set +Set Multipart = Series +Showing = Showing +# Simplified Chinese = "ä¸æ–‡(简体)" +Simplified Chinese = "Simplified Chinese" +Slide = Slide +Software = Software +Sort = "Sorted by" +# Spanish = Español +Spanish = Spanish +StackRequest = "Stack Request" +Start Page = "Start Page" +Status = Status +Subject = Subject +Submit = Submit +Suggestions for acquisition = "Suggestions for Acquisition" +Summary = Summary +Switch view to = "Switch View to" +Systemmessage = "System Messages" +Tag = Tag +Tags = Tags +That email address is already used = "This e-mail address is already used." +That username is already taken = "This user name is already taken." +This email was sent from = "This e-mail was sent from" +Title = Title +Topic = Topic +Topics = Topics +# Turkish = Türkçe +Turkish = Turkish +Unknown = Unknown +Username = "User ID" +Username cannot be blank = "User name cannot be blank" +Valid until = "Card valid until" +VHS = VHS +Video = Video +Videos = Videos +View online: Full view Book Preview from the Hathi Trust = "View Online: View full book preview from Hathi Trust" +Volume = Volume +Warning: These citations may not always be 100% accurate = "Warning: These citations may not always be 100% correct" +Web = Web +# Welsh = "Cymraeg" +Welsh = Welsh +Yesterday = Yesterday +You do not have any fines = "You do not have any fines." +You do not have any holds or recalls placed = "You do not have any holds or recalls placed." +You do not have any items checked out = "You do not have any items checked out." +You do not have any saved resources = "You do not have any saved resources." +You must be logged in first = "You must log in first" +Your Account = "My Account" +Your book bag is empty = "Your Book Bag is empty" +Your search terms = "Your Search Terms" +Zip = Zip +### Standardbenennung sollte nach englisch geändert und dann in der de.ini auf +### deutsche Bezeichnung gemappt werden: +Zweigbibliothek = "Branch Library" +Fachgebiet = Subject +#### +add_favorite_fail = "Error: Record not saved" +add_favorite_prefix = Add +add_favorite_suffix = "to Favorites" +add_list_fail = "Error: List not created" +add_other_libraries = "Include articles at other libraries" +add_search = "Add Search Field" +add_search_group = "Add Search Group" +add_tag_error = "Error: Could not save tags" +add_tag_error_chars = "Tag should have 2 chars and at minimum and 25 at maximum." +add_tag_example = "Example: "Romeo and Julia" Shakespeare English" +add_tag_note = "Use a comma or press [ENTER] to separate tags. Maximum length of a tag is 25 characters." +add_tag_success = "Tags saved" +advSearchError_noRights = "Sorry, but you don't have permission to edit this search. Perhaps your browser session has expired?" +advSearchError_notAdvanced = "The search you have requested to edit is not an advanced search." +advSearchError_notFound = "The search you have requested was not found." +adv_search_all = "All Fields" +adv_search_author = "Person / Institution" +adv_search_callnumber = "Call Number" +adv_search_filters = "Applied Filters" +adv_search_isn = "ISBN / ISSN" +adv_search_journaltitle = "Journal Title" +adv_search_label = "Search for" +adv_search_publisher = Publisher +adv_search_publishPlace = "Place of Publication" +adv_search_rvk = RVK +adv_search_select_all = "Select All" +adv_search_series = Series +adv_search_subject = Subject +adv_search_title = Title +adv_search_toc = "Table of Contents" +adv_search_year = "Year of Publication" +alphabrowse_matches = "Titles" +authentication_error_admin = "We cannot log you in at this time. Please contact staff." +authentication_error_blank = "Login information cannot be blank." +authentication_error_denied = "Credentials do not match! Access denied." +authentication_error_invalid = "Invalid login -- please try again." +authentication_error_technical = "We cannot log you in at this time. Please try again later." +basic_search_keep_filters = "Retain my Current Filters" +banner_text = "Your opinion, please" +banner_link = "comment on the blog!" +bookbag_confirm_empty = "Are you sure you want to empty your Book Bag?" +bookbag_delete = "Delete Selected Book Bag Items" +bookbag_delete_selected = "Delete Selected" +bookbag_email = "E-mail Selected Book Bag Items" +bookbag_email_selected = "E-mail Selected" +bookbag_export = "Export Selected Book Bag items" +bookbag_export_selected = "Export Selected" +bookbag_full = "Full" +bookbag_full_msg = "Your Book Bag is full" +bookbag_is_empty = "Your Book Bag is empty" +bookbag_print_selected = "Print Selected" +bookbag_save = "Save Selected Book Bag Items" +bookbag_save_selected = "Save Selected" +bookchapter = "Book Chapter" +book_chapter = "Book Chapter" +booking_nep_successful = "Thank you for your purchase order. We will inform you about further proceedings via e-mail." +bounded_book = "Please follow this link to check availability." +browse_dewey = "Call Number (Dewey)" +browse_lcc = "Call Number (LC)" +bulk_email_success = "Your item(s) were e-mailed" +bulk_email_title = "Library Catalog Items" +bulk_error_missing = "Some data was missing. Your request was not successful." +bulk_export_not_supported = "The record(s) you have selected do not support bulk export." +bulk_fail = "Sorry, an error has occurred. Please try again." +bulk_noitems_advice = "No items were selected. Please click on a checkbox next to an item and try again." +bulk_save_error = "Some data was missing. Your items were not saved." +bulk_save_success = "Your item(s) were saved successfully" +by = by +callnumber_abbrev = "Call #" +cat_establish_account = "In order to establish your account profile, please enter the following information:" +cat_password_abbrev = "Catalog Password" +cat_username_abbrev = "Catalog User Name" +close = close +comment_error_load = "Error: Could not redraw comment list" +comment_error_save = "Error: Could not save comment" +confirm_delete = "Are you sure you want to delete this?" +confirm_delete_list_text = "Are you sure you want to delete this list?" +confirm_hold_cancel_all_text = "Do you wish to cancel all your current holds?" +confirm_hold_cancel_selected_text = "Do you wish to cancel your selected holds?" +course_reserves_empty_list = "No matching Course Reserves found." +date_day_placeholder = "D" +date_from = "From" +date_month_placeholder = "M" +date_to = "To" +date_year_placeholder = "Y" +del_search = "Remove Search Group" +delete_list = "Delete List" +delete_selected = "Delete Selected" +delete_selected_favorites = "Delete Selected Favorites" +delete_selected_reserves = "Cancel Selected Reservations" +eBook = eBook +edit_list = "Edit List" +edit_list_fail = "Sorry, you are not permitted to edit this list" +edit_list_success = "List successfully updated." +email_failure = "Error - Message cannot be sent" +email_link = "Link" +email_selected = "E-mail Selected" +email_selected_favorites = "E-mail Selected Favorites" +email_sending = "Sending Message..." +email_success = "Message Sent" +error_inconsistent_parameters = "Sorry, an error has occurred. Inconsistent parameters detected" +errorcode_error = "Sorry, an error has occurred. Please try it again" +errorcode_http_status_error = "No network available for the Integrated Library System" +errorcode_empty_response_error = "No response received from the Integrated Library System" +errorcode_opac_error = "Sorry, an error occurred at the Integrated Library System" +errorcode_login_error = "The login failed" +errorcode_empty_member_code = "Some data was missing. No member number was submitted" +errorcode_empty_password = "Some data was missing. No password was submitted" +errorcode_member_not_found = "The member number does not exist" +errorcode_password_validation_error = "Your passed password is not correct" +errorcode_empty_req_param_error = "All required fields have to be filled to submit successfully the form" +exclude_newspapers = "Exclude Newspaper Articles" +export_download = "Download File" +export_exporting = "Creating Export File" +export_fail = "Your items were not exported" +export_missing = "Some data was missing. Your items were not exported." +export_refworks = "Start Export to RefWorks" +export_save = "Save File" +export_selected = "Export Selected" +export_selected_favorites = "Export Selected Favorites" +export_success = "Export Complete" +export_unsupported_format = "Unsupported Export Format" +fav_delete = "Delete Selected Favorites" +fav_delete_deleting = "Your favorite(s) are being deleted." +fav_delete_fail = "Sorry, an error has occurred. Your favorite(s) were not deleted." +fav_delete_missing = "Some data was missing. Your favorite(s) were not deleted." +fav_delete_success = "Your favorite(s) were deleted." +fav_delete_warn = "You are about to delete these favorites from all your lists - If you want to delete favorites from a specific list only, please select the list before clicking delete." +fav_email_fail = "Sorry, an error has occurred. Your favorite(s) were not e-mailed." +fav_email_missing = "Some data was missing. Your favorite(s) were not e-mailed." +fav_email_success = "Your favorite(s) were e-mailed as requested." +fav_export = "Export Favorites" +fav_list_delete = "Your favorites list was deleted" +fav_list_delete_cancel = "This list was not deleted" +fav_list_delete_fail = "Sorry, an error has occurred. Your list was not deleted." +fee_desc_BE = "Fee for Book Compensation" +fee_desc_EG = "Fee for Processing" +fee_desc_F = "Overdue Media" +fee_desc_FL = "Fee for Interlibrary Loan" +fee_desc_FLK = "Fee for Interlibrary Loan Copies" +fee_desc_FLZ = "Additional Fee for Interlibrary Loan" +fee_desc_K = "Fee for Copies of Fotostelle" +fee_desc_KOR = "Fee for Accounting Error and Correction" +fee_desc_LE = "Replacement of Library Card" +fee_desc_M1 = "Overdue Fines (First Reminder)" +fee_desc_M2 = "Overdue Fines (Second Reminder)" +fee_desc_M3 = "Overdue Fines (Third Reminder)" +fee_desc_P1 = "Postage (First Reminder)" +fee_desc_P2 = "Postage (Second Reminder)" +fee_desc_P3 = "Postage (Third Reminder)" +fee_desc_R = "Fee for Reservation" +fee_desc_S = "Other Types of Fees" +fee_desc_STORNO = "Fee for Cancellation" +fine_limit_patron = "You have reached your fines limit and cannot renew items" +for search = "for Search" +found = found +fulltext_limit = "Limit to articles with full text available" +google_map_cluster = "Cluster" +group_AND = "ALL Groups" +group_OR = "ANY Groups" +history = "Search History" +history_delete = Delete +history_delete_link = Delete +history_empty_search = "Anything (Empty Search)" +history_limits = Limits +history_no_searches = "Currently there are no searches in your history." +history_purge = "Discard my Unsaved Searches" +history_recent_searches = "Your Recent Searches" +history_results = Results +history_save = Save? +history_save_link = Save +history_saved_searches = "Your Saved Searches" +history_search = Search +history_time = Time +hold_available = "Available for Pickup" +hold_cancel = "Cancel Hold" +hold_cancel_all = "Cancel All Holds" +hold_cancel_fail = "Your request was not canceled. Please contact staff" +hold_cancel_selected = "Cancel Selected Holds" +hold_cancel_success = "Your request was canceled successfully" +hold_cancel_success_items = "Your request(s) were canceled successfully" +hold_date_invalid = "Please enter a valid date" +hold_date_past = "Please enter a date in the future" +hold_empty_selection = "No holds were selected" +hold_error_blocked = "You do not have sufficient privileges to place a hold on this item." +hold_error_fail = "Your request failed. Please contact staff" +hold_invalid_pickup = "An invalid pick up location was entered. Please try again" +hold_login = "for Hold and Recall Information" +hold_place = "Place Request" +hold_place_fail_missing = "Your request failed. Some data was missing. Please contact staff" +hold_place_success = "Your request was successful" +hold_queue_position = "Queue Position" +hold_required_by = "No longer required after" +hold_success = "Your request was successful" +home_browse = "Browse by" +in = in +item_permanent_loan = "Item is a Permanent Loan." +items = items +items_added_to_bookbag = "Item(s) added to your Book Bag" +items_already_in_bookbag = "Item(s) are either already in your Book Bag or could not be added" +less = less +lightbox_error = "Error: Cannot load popup box" +list_access_denied = "You do not have permission to view this list." +list_edit_name_required = "List name is required." +load_tag_error = "Error: Could not load tags" +lock_account_already_blocked = "User account is already blocked. Please contact staff." +lock_account_notice = "Please note that the blocking of a user account can only be removed by staff." +lock_account_button = "Block my account now" +lock_account_error = "Error occurred. System cannot block user account." +map_results_label = "At this Location:" +msg_renew_prev = "The item" +msg_renew_middle = "has been successfully renewed till:" +msg_renew_after = "." +msg_renew_error_prev = "The item" +msg_renew_error_after = "has failed to renew." +more = more +no_description = "Description not available." +no_items_selected = "No items were selected" +nohit_filters = "Filters currently applied to this search:" +nohit_adjust_filters = "Please adjust your filters or" +nohit_heading = "No Results." +nohit_no_filters = "No filters were applied to this search." +nohit_parse_error = "There seems to be a problem with your search query. Please check the syntax. If you are not trying to use advanced features, putting the query inside double quotes may help." +nohit_prefix = "Your Search" +nohit_spelling = "Perhaps you should try some spelling variations" +nohit_suffix = "did not match any resources." +nohit_suggest = "You may want to try to revise your search phrase by removing some words or check your spelling." +not_applicable = "n/a" +note_760 = "Main Series" +note_765 = "Translation of" +note_770 = "Has Supplement" +note_772 = "Supplement to" +note_773 = "Contained in" +note_774 = "Constituent Unit" +note_775 = "Other Edition Available" +note_777 = "Issued with" +note_780_0 = "Continues" +note_780_1 = "Continues in Part" +note_780_5 = "Absorbed" +note_785_0 = "Continued by" +note_785_7 = "Merged with" +notice_journal_order = "Please note: Journals published before 1850 will be provided at Forschungslesesaal." +of = of +on_reserve = "Reserves - Please contact staff" +online_resources = "Online Resources" +create a new list = "Create a New List" +password_too_short = "Too short" +password_very_weak = "Very weak" +password_weak = "Weak" +password_better = "Better" +password_medium = "Medium" +password_good = "Good" +password_strong = "Very good" +peer_reviewed = "Peer Reviewed" +peer_reviewed_limit = "Limit to articles from peer-reviewed journals" +pick_up_location = "Pickup Library" +posted_on = "on" +print_selected = "Print Selected" +profile_update = "Your profile was updated as requested" +query time = "Query Time" +renew_all = "Renew All Items" +renew_determine_fail = "We were unable to determine if your item can be renewed. Please contact staff." +renew_empty_selection = "No items were selected" +renew_error = "We were unable to renew your item(s) - Please contact staff" +renew_fail = "This item could not be renewed" +renew_item = "Renew Item" +renew_item_count = "Number of Renewals" +renew_item_due = "Item due within the next 24 hours" +renew_item_limit = "This item cannot be renewed." +renew_item_maximum = "There is a maximum of three renewals allowed." +renew_item_no = "This item cannot be renewed" +renew_item_overdue = "Item overdue" +renew_item_requested ="This item has been requested by another user" +renew_select_box = "Renew Item" +renew_selected = "Renew Selected Items" +renew_success = "Renewal Successful" +request_place_text = "Place a Request" +request_submit_text = "Submit Request" +reserved_item_canceled_successful = "Reserved items were canceled successfully." +reserved_item_canceled_failure = "An error has occurred. Reserved items could not be canceled." +reserved_title = "Amount of Reserved Items:" +reserved_no_title = "No Items Reserved." +save_search = "Save Search" +save_search_remove = "Remove Saved Search" +scholarly_limit = "Limit to articles from scholarly journals" +search results of = "Search Results of" +search_AND = "ALL Terms" +search_NOT = "NO Terms" +search_OR = "ANY Terms" +search_groups = "Search Groups" +search_match = Match +search_save_success = "Search saved successfully." +search_unsave_success = "Saved search removed successfully." +see all = "See All" +select_all = "Select All" +select_journal_item = "Please fill in number and year of item" +select_page = "Select Page" +sms_failure = "Error - Could not send message." +sms_phone_number = "10-Digit Phone Number" +sms_sending = "Sending Message..." +sms_success = "Message Sent." +sort_author = "Person / Institution" +sort_author_author = Alphabetical +sort_author_relevance = Popularity +sort_callnumber = "Call Number" +sort_relevance = Relevance +sort_title = Title +sort_year = "Date Descending" +sort_year asc = "Date Ascending" +spell_expand_alt = "Expand Search" +spell_suggest = "Search Alternatives" +starting from = "Starting from" +summon_database_recommendations = "You may find additional resources here:" +sysmsg_expireddate_before = "Your Library Card will expire on" +sysmsg_expireddate_after = "." +sysmsg_expireddate_warning_before = "Your Library Card will expire on" +sysmsg_expireddate_warning_after = ". Please contact staff." +sysmsg_fines_on_account = "There are fines on your account" +sysmsg_readyforcollection_after = " item(s) available for collection." +sysmsg_readyforcollection_before = "There are" +sysmsg_user_blocked_status = "Your account is blocked. Please contact staff." +title = "Title" +title_ordered_by_library = "This title has been ordered by the library. It is not available yet." +too_many_favorites = "This list is too large to display all at once. Try rearranging your favorites into more lists or limiting using tags." +too_many_new_items = "There are too many new items to display in a single list. Try limiting your search." +too_many_reserves = "There are too many course reserves to display in a single list. Try limiting your search." +top_facet_additional_prefix = "Additional " +top_facet_suffix = "... within your Search." +user_signed_out = "User is signed out" +user_blocked = "User is blocked" +user_blocked_3 = "Error on 3M SelfCheck system: Please contact the circulation desk." +user_blocked_a = "Wrong address: You're locked for use. Your address isn't correct. Please contact the circulation desk." +user_blocked_b = "Wrong address: You're locked for use. Your address isn't correct. Please contact the circulation desk." +user_blocked_c = "Incomplete address: You're locked for use. Your address isn't complete. Please contact the circulation desk." +user_blocked_d = "Lost library card: You're locked for use. Please contact the circulation desk." +user_blocked_e = "Double Registration: You're locked for use. Please contact the circulation desk." +user_blocked_f = "Lost property in the account: Please contact the circulation desk." +user_blocked_g = "Return pre-ordered media: You're locked for use. Please return pre-ordered media." +user_blocked_h = "Locked with internal notice: You're locked for use. Please contact the circulation desk." +user_blocked_k = "Fees 6 months overdue: You're locked for use. Please pay your fees promptly." +user_blocked_l = "Reached final warning: You're locked for use. You've reached the last dunning level. Please return your media and pay your fees." +user_blocked_m = "Fees limit reached: You're locked for use. You've reached the charge limit. Please pay your fees promptly." +user_blocked_o = "Wrong Email address: Please correct your Email address at the circulation desk." +user_blocked_p = "Wrong password entered in catalogue: Please contact the circulation desk." +user_blocked_s = "Account locked by user itself: Please contact the circulation desk." +user_blocked_u = "Temporarily your account is blocked. Please ask a librarian." +user_blocked_x = "Temporarily your account is blocked. Please ask a librarian." +user_blocked_mu = "Reached no warning, charge limit: You are locked for use. Please pay your dues promptly." +user_deactivated_check_chipcard = "User account is blocked. Please contact staff." +user_locked_account = "Account was blocked by the user. Please contact staff" +guser_dunning_process = "User account is blocked due to a dunning process" +user_lost_reader_card = "User account is blocked due to loss of library card" +user_equipment_overdued = "User account is blocked due to overdue media" +userpin = "User PIN" +userpin_change_selfcheckout = "Change User PIN for Self-Checkout" +userpin_confirm = "Confirm User PIN" +userpin_for_selfcheckout = "User PIN for Self-Checkout" +view already selected = "View Already Selected" +view_links_volumes = "View links, volumes..." +wcterms_broader = "Broader Subjects" +wcterms_exact = "Related Subjects" +wcterms_narrower = "Narrower Subjects" +wiki_link = "Provided by Wikipedia" +with filters = "with Filters" +with_selected = "Selected" + +DE-14 = "Saxon State and University Library Dresden (SLUB)" +DE-15 = "Leipzig University Library" +DE-15-292 = "Leipzig University Library, Central Library Medicine" +DE-105 = "Technische Universität Bergakademie Freiberg" +DE-197 = "City Library Leipzig" +DE-520 = "Dresden University of Applied Sciences" +DE-540 = "Dresden Academy of Fine Arts" +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-Mit1 = "Hochschule Mittweida" +DE-L152 = "University of Music and Theatre 'Felix Mendelssohn Bartholdy' Leipzig" +DE-L189 = "Leipzig University of Applied Sciences" +DE-L228 = "Staatliche Ethnographische Sammlungen Sachsen, Museum für Völkerkunde Leipzig" +DE-L242 = "Academy of Visual Arts Leipzig" +DE-L328 = "Halle 14 Art Library Leipzig" +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)" + +;select fields for NE-Print +nep_misc = "Micellaneous" +nep_bio = "Biology" +nep_geo = "Geography, Geosciences" +nep_hist = "History" +nep_art = "Arts" +nep_ling = "Literary and linguistics" +nep_math = "Mathematics, Computer science" +nep_med = "Human medicine" +nep_mus = "Music" +nep_nat = "Natural science, Chemistry, Physic, Engineering" +nep_paed = "Pedagogy" +nep_phil = "Philosophy" +nep_pol = "Politic" +nep_psy = "Psychology" +nep_jur = "Law" +nep_soz = "Sociology" +nep_sport = "Sport science" +nep_vet = "Veterinary medicine" +nep_theo = "Theology" +nep_ethno = "Ethnology" + +Category = "Subject" + +;------ +;missing lines from de.ini (compiled from files in /usr/local/vufind2/languages, /usr/local/vufind2/local/dev/languages) +;------ +ElectronicJournal = ElectronicJournal +ElectronicSerial = ElectronicSerial +Electronicserial = Electronicserial +ElectronicNewspaper = ElectronicNewspaper +Electronicnewspaper = Electronicnewspaper +Article = Article +ElectronicArticle = ElectronicArticle +Electronicarticle = Electronicarticle +ElectronicThesis = ElectronicThesis +Electronicthesis = Electronicthesis +Thesis = Thesis +Atlas = Atlas +AudioTape = AudioTape +Audiotape = Audiotape +Cd = Cd +DVDAudio = DVDAudio +Dvdaudio = Dvdaudio +MusicRecording = MusicRecording +Musicrecording = Musicrecording +Record = Record +SoundRecordingMedium = SoundRecordingMedium +Soundrecordingmedium = Soundrecordingmedium +SoundCassette = SoundCassette +Soundcassette = Soundcassette +SoundDisc = SoundDisc +Sounddisc = Sounddisc +SoundRecording = SoundRecording +Soundrecording = Soundrecording +AudioVisualMedia = AudioVisualMedia +Audiovisualmedia = Audiovisualmedia +BluRayDisc = BluRayDisc +Bluraydisc = Bluraydisc +DVDVideo = DVDVideo +Dvdvideo = Dvdvideo +Filmstrip = Filmstrip +MotionPicture = MotionPicture +Motionpicture = Motionpicture +VideoTape = VideoTape +Videotape = Videotape +VideoCartridge = VideoCartridge +Videocartridge = Videocartridge +VideoCassette = VideoCassette +Videocassette = Videocassette +VideoDisc = VideoDisc +Videodisc = Videodisc +VideoReel = VideoReel +Videoreel = Videoreel +ArtPrint = ArtPrint +Artprint = Artprint +Chart = Chart +Collage = Collage +Drawing = Drawing +FlashCard = FlashCard +Flashcard = Flashcard +Painting = Painting +Photonegative = Photonegative +Placard = Placard +SensorImage = SensorImage +Sensorimage = Sensorimage +Transparency = Transparency +CDROM = CDROM +Cdrom = Cdrom +ChipCartridge = ChipCartridge +Chipcartridge = Chipcartridge +DiscCartridge = DiscCartridge +Disccartridge = Disccartridge +DVDO = DVDO +DVDROM = DVDROM +Dvdrom = Dvdrom +FloppyDisk = FloppyDisk +Floppydisk = Floppydisk +TapeCartridge = TapeCartridge +Tapecartridge = Tapecartridge +TapeCassette = TapeCassette +Tapecassette = Tapecassette +TapeReel = TapeReel +Tapereel = Tapereel +Microfiche = Microfiche +Microform = Microform +MusicalScore = MusicalScore +Musicalscore = Musicalscore +NotatedMusic = NotatedMusic +Notatedmusic = Notatedmusic +ElectronicMusicalScore = ElectronicMusicalScore +Electronicmusicalscore = Electronicmusicalscore +Nachlass = Nachlass +PhysicalObject = PhysicalObject +Physicalobject = Physicalobject +# Electronic = # Electronic +ElectronicResourceDataCarrier = ElectronicResourceDataCarrier +Electronicresourcedatacarrier = Electronicresourcedatacarrier +ElectronicResourceRemoteAccess = ElectronicResourceRemoteAccess +Electronicresourceremoteaccess = Electronicresourceremoteaccess +Unknown Format = Unknown Format +Conference Proceedings = Conference Proceedings +conference_proceedings = conference_proceedings +conference_proceeding = conference_proceeding +Conferenceproceeding = Conferenceproceeding +dissertations = dissertations +Dissertations = Dissertations +Legal Document = Legal Document +Multimedia = Multimedia +Newspaper Article = Newspaper Article +Newspaper Articles = Newspaper Articles +newspaper_articles = newspaper_articles +Reference Entry = Reference Entry +Review = Review +review = review +reviews = reviews +Text = Text +Textresource = Textresource +text_resource = text_resource +text_resources = text_resources +Accession = Accession +accession = accession +Art Print = Art Print +articleearticle = articleearticle +Articleearticle = Articleearticle +Article, E-Article = Article, E-Article +article = article +Articles = Articles +articles = articles +Audio Tape = Audio Tape +Audio-Visual Media = Audio-Visual Media +Blu-Ray Disc = Blu-Ray Disc +Book, E-Book = Book, E-Book +books = books +Chip Cartridge = Chip Cartridge +Create QR Code = Create QR Code +dvdo = dvdo +Ebook = Ebook +Electronic Article = Electronic Article +Electronic Journal = Electronic Journal +Electronic Media = Electronic Media +Electronic Newspaper = Electronic Newspaper +Electronic Resource (Data Carrier) = Electronic Resource (Data Carrier) +Electronic Resource (Remote Access) = Electronic Resource (Remote Access) +Electronic Serial = Electronic Serial +Electronic Thesis = Electronic Thesis +Floppy Disk = Floppy Disk +Images = Images +Journal / Newspaper = Journal / Newspaper +Journal, E-Journal = Journal, E-Journal +Motion Picture = Motion Picture +Music Recording = Music Recording +Notated Music = Notated Music +Newspaper, E-Paper = Newspaper, E-Paper +Sound Cassette = Sound Cassette +Sound Disc = Sound Disc +Sound Recording Medium = Sound Recording Medium +Sound Recording = Sound Recording +Tape Cartridge = Tape Cartridge +Tape Cassette = Tape Cassette +Tape Reel = Tape Reel +Theses = Theses +Video Cartridge = Video Cartridge +Video Cassette = Video Cassette +Video Disc = Video Disc +Video Reel = Video Reel +Video Tape = Video Tape +Visual Media = Visual Media +Accompanying material = Accompanying material +Additional Information = Additional Information +Additional Items = Additional Items +Additional Items unavailable = Additional Items unavailable +Additional Searches = Additional Searches +Address Data = Address Data +Amount payable = Amount payable +Archäologie = Archäologie +Available Stock = Available Stock +Barcode = Barcode +Book Suggestion = Book Suggestion +Branch = Branch +Branch to go = Branch to go +Cancel = Cancel +Cannot update data = Cannot update data +Change Profile Data = Change Profile Data +Change User PIN of Self-Checkout = Change User PIN of Self-Checkout +Click here to view book = Click here to view book +Close = Close +Comment = Comment +combined search (OR) = combined search (OR) +Confirm = Confirm +Confirm with your password = Confirm with your password +Contact = Contact +Contains = Contains +Date of birth = Date of birth +Date of issue = Date of issue +Date of loan = Date of loan +Each field should have a value = Each field should have a value +For technical reasons, this title can not be saved. = For technical reasons, this title can not be saved. +Enter at least 6 characters = Enter at least 6 characters +Enter exactly 4 numbers = Enter exactly 4 numbers +Expires on = Expires on +Faculty = Faculty +Favorite = Favorite +Field volume should have a value = Field volume should have a value +Fields are required = Fields are required +Find Similar Items = Find Similar Items +Footnotes = Footnotes +Form of address = Form of address +Friday = Friday +Gender = Gender +Help and information material = Help and information material +Here = Here +Hold-Submit = Hold-Submit +Identity Number = Identity Number +Information literacy = Information literacy +In acquisition = In acquisition +Index of German prints = Index of German prints +Interlibrary Loans = Interlibrary Loans +ISBD Citation = ISBD Citation +ISSN of parallel title = ISSN of parallel title +Journal holdings = Journal holdings +Key = Key +Languages = Languages +Legal Documents = Legal Documents +Legal Info = Legal Info +Less = Less +List of new items = List of new items +Local Holdings = Local Holdings +Local Subjects = Local Subjects +Mark record for renewing = Mark record for renewing +Medicine = Medicine +Membercode not found = Membercode not found +Membercode is not valid = Membercode is not valid +Monday = Monday +More = More +More Information = More Information +Multipart = Multipart +Museum für Musikinstrumente = Museum für Musikinstrumente +My Loan History = My Loan History +Name = Name +National Catalogues = National Catalogues +New password does not correspond with confirmation password = New password does not correspond with confirmation password +New publications = New publications +New search for all parts = New search for all parts +no subject assigned = no subject assigned +No subject assigned = No subject assigned +None = None +not assigned = not assigned +Not available = Not available +Notice = Notice +Online = Online +Only numbers are allowed = Only numbers are allowed +Opening hours = Opening hours +Opus Catalogue Number = Opus Catalogue Number +Order form = Order form +Ordered = Ordered +Ordered on = Ordered on +Order Comments = Order Comments +Orientwissenschaften, Ägyptologie = Orientwissenschaften, Ägyptologie +other = other +Questions = Questions +Parallel Editions = Parallel Editions +Partrelated = Partrelated +Partnotrelated = Partnotrelated +Password Current is missing = Password Current is missing +Password not replaced = Password not replaced +Password replaced successfully = Password replaced successfully +Passwords are not conformed = Passwords are not conformed +Performer = Performer +Physical = Physical +Place = Place +Please reason briefly the purchase of the title = Please reason briefly the purchase of the title +Please select the subject = Please select the subject +Postcode = Postcode +Proceed browsing from here = Proceed browsing from here +Profile data updated successfully = Profile data updated successfully +Public list = Public list +Publishing Number = Publishing Number +Purchasesuggestion = Purchasesuggestion +Purchase Suggestion = Purchase Suggestion +Put on hold until = Put on hold until +Reading Room = Reading Room +Reading Room items = Reading Room items +Ready For Collection = Ready For Collection +Ready For Collection Stacks = Ready For Collection Stacks +Reason = Reason +Received on = Received on +Reference management = Reference management +Related Title = Related Title +Renew Login = Renew Login +Repeat your password = Repeat your password +Reserve = Reserve +Reserved = Reserved +Reserved on = Reserved on +Reset = Reset +Rvk-Path = Rvk-Path +Saturday = Saturday +Send Registration = Send Registration +Services = Services +Set back parts = Set back parts +Sets = Sets +Shelf = Shelf +Stackrequest = Stackrequest +Stack Request = Stack Request +Stacks = Stacks +Start browsing again = Start browsing again +String contains whitespaces = String contains whitespaces +Student Number = Student Number +Sunday = Sunday +System Message = System Message +System Message Number = System Message Number +System Messages = System Messages +Theses statement = Theses statement +There are not any items available = There are not any items available +Thursday = Thursday +Title Statement = Title Statement +Tome = Tome +Tomes = Tomes +Tuesday = Tuesday +Unavailable = Unavailable +unknown = unknown +Unspecified identifier type = Unspecified identifier type +User PINs do not match = User PINs do not match +User-Registration = User-Registration +Varying Forms = Varying Forms +Veterinärmedizin = Veterinärmedizin +View all parts = View all parts +View next parts = View next parts +Visit our blog to the new catalogue = Visit our blog to the new catalogue +Volumes in Stock = Volumes in Stock +Wednesday = Wednesday +Year = Year +You do not have any interlibrary loans = You do not have any interlibrary loans +You do not have any items for the public reading room = You do not have any items for the public reading room +You do not have any media ready for collection = You do not have any media ready for collection +You do not have any orders = You do not have any orders +You do not have reserved any media = You do not have reserved any media +You do not have any system messages = You do not have any system messages +Your = Your +Your Interlibrary Loans = Your Interlibrary Loans +Your Media Ready for Collection = Your Media Ready for Collection +Your Orders = Your Orders +Your password does not match = Your password does not match +Your System Messages = Your System Messages +Your old password does not match = Your old password does not match +advanced = advanced +adv_search_hgb = adv_search_hgb +and = and +bookbag_print = bookbag_print +booking_journal_successful = booking_journal_successful +cited_articles = cited_articles +external = external +fee_desc_A = fee_desc_A +fee_desc_B = fee_desc_B +fee_desc_BO = fee_desc_BO +fee_desc_CP = fee_desc_CP +fee_desc_DL = fee_desc_DL +fee_desc_EK = fee_desc_EK +fee_desc_FLA = fee_desc_FLA +fee_desc_FLE = fee_desc_FLE +fee_desc_M = fee_desc_M +fee_desc_N = fee_desc_N +fee_desc_PA = fee_desc_PA +fee_desc_PF = fee_desc_PF +fee_desc_PM = fee_desc_PM +fee_desc_PV = fee_desc_PV +hold_submit = hold_submit +ils_offline = ils_offline +info_export_item = info_export_item +internal = internal +maps = maps +medium_ordered_successful = medium_ordered_successful +medium_reserved_successful = medium_reserved_successful +or = or +remove = remove +request_reserve = request_reserve +request_reserve_submit = request_reserve_submit +rvk = rvk +#sort_year = #sort_year +#sort_year asc = #sort_year asc +sysmsg_the_title = sysmsg_the_title +systemmessage_not_removed = systemmessage_not_removed +systemmessage_removed_successful = systemmessage_removed_successful +systemmessageid_not_given = systemmessageid_not_given +user_dunning_process = user_dunning_process +user_access_restricted = user_access_restricted +DE-Mit1 = DE-Mit1 +Dresden SLUB = Dresden SLUB +According to your search = According to your search +been added. = been added. +Europe = Europe +Andorra = Andorra +Albania = Albania +Austria = Austria +Austria / Burgenland = Austria / Burgenland +Austria / Carinthia = Austria / Carinthia +Austria / Lower Austria = Austria / Lower Austria +Austria / Upper Austria = Austria / Upper Austria +Austria / Salzburg = Austria / Salzburg +Austria / Styria = Austria / Styria +Austria / Tyrol = Austria / Tyrol +Austria / Vorarlberg = Austria / Vorarlberg +Austria / Vienna = Austria / Vienna +Ã…land Islands = Ã…land Islands +Bosnia and Herzegovina = Bosnia and Herzegovina +Belgium = Belgium +Bulgaria = Bulgaria +Belarus = Belarus +Switzerland = Switzerland +Switzerland / Aargau = Switzerland / Aargau +Switzerland / Appenzell Innerrhoden = Switzerland / Appenzell Innerrhoden +Switzerland / Appenzell Ausserrhoden = Switzerland / Appenzell Ausserrhoden +Switzerland / Bern = Switzerland / Bern +Switzerland / Basel-Landschaft = Switzerland / Basel-Landschaft +Switzerland / Basel = Switzerland / Basel +Switzerland / Fribourg = Switzerland / Fribourg +Switzerland / Geneva = Switzerland / Geneva +Switzerland / Glarus = Switzerland / Glarus +Switzerland / Graubünden = Switzerland / Graubünden +Switzerland / Jura = Switzerland / Jura +Switzerland / Lucerne = Switzerland / Lucerne +Switzerland / Neuchâtel = Switzerland / Neuchâtel +Switzerland / Nidwalden = Switzerland / Nidwalden +Switzerland / Obwalden = Switzerland / Obwalden +Switzerland / St. Gallen = Switzerland / St. Gallen +Switzerland / Schaffhausen = Switzerland / Schaffhausen +Switzerland / Solothurn = Switzerland / Solothurn +Switzerland / Schwyz = Switzerland / Schwyz +Switzerland / Thurgau = Switzerland / Thurgau +Switzerland / Ticino = Switzerland / Ticino +Switzerland / Uri = Switzerland / Uri +Switzerland / Vaud = Switzerland / Vaud +Switzerland / Valais = Switzerland / Valais +Switzerland / Zug = Switzerland / Zug +Switzerland / Zurich = Switzerland / Zurich +Cyprus = Cyprus +Czech Republic = Czech Republic +Germany = Germany +Germany / Brandenburg = Germany / Brandenburg +Germany / Berlin = Germany / Berlin +Germany / Baden-Württemberg = Germany / Baden-Württemberg +Germany / Bavaria = Germany / Bavaria +Germany / Bremen = Germany / Bremen +Germany / Hesse = Germany / Hesse +Germany / Hamburg = Germany / Hamburg +Germany / Mecklenburg-Vorpommern = Germany / Mecklenburg-Vorpommern +Germany / Lower Saxony = Germany / Lower Saxony +Germany / North Rhine-Wesphalia = Germany / North Rhine-Wesphalia +Germany / Rhineland-Palatinate = Germany / Rhineland-Palatinate +Germany / Schleswig-Holstein = Germany / Schleswig-Holstein +Germany / Saarland = Germany / Saarland +Germany / Saxony = Germany / Saxony +Germany / Saxony-Anhalt = Germany / Saxony-Anhalt +Germany / Thuringia = Germany / Thuringia +Denmark = Denmark +Estonia = Estonia +Spain = Spain +Finland = Finland +France = France +United Kingdom = United Kingdom +Guernsey = Guernsey +Gibraltar = Gibraltar +Greece = Greece +Croatia = Croatia +Hungary = Hungary +Ireland = Ireland +Isle of Man = Isle of Man +Iceland = Iceland +Italy = Italy +Italy / Trentino-Alto Adige = Italy / Trentino-Alto Adige +Jersey = Jersey +Liechtenstein = Liechtenstein +Lithuania = Lithuania +Luxembourg = Luxembourg +Latvia = Latvia +Monaco = Monaco +Moldova = Moldova +Montenegro = Montenegro +Macedonia = Macedonia +Malta = Malta +Netherlands = Netherlands +Norway = Norway +Poland = Poland +Portugal = Portugal +Romania = Romania +Serbia = Serbia +Russia = Russia +Sweden = Sweden +Slovenia = Slovenia +Slovakia = Slovakia +San Marino = San Marino +Ukraine = Ukraine +Vatican City = Vatican City +Asia = Asia +United Arab Emirates = United Arab Emirates +Afghanistan = Afghanistan +Armenia = Armenia +Azerbaijan = Azerbaijan +Bangladesh = Bangladesh +Bahrain = Bahrain +Brunei = Brunei +Bhutan = Bhutan +China = China +Tibet = Tibet +Georgia = Georgia +Hong Kong = Hong Kong +Indonesia = Indonesia +Israel = Israel +India = India +Iraq = Iraq +Iran = Iran +Jordan = Jordan +Japan = Japan +Kyrgyzstan = Kyrgyzstan +Cambodia = Cambodia +North Korea = North Korea +South Korea = South Korea +Kuwait = Kuwait +Kazakhstan = Kazakhstan +Laos = Laos +Lebanon = Lebanon +Sri Lanka = Sri Lanka +Myanmar = Myanmar +Mongolia = Mongolia +Macao = Macao +Maldives = Maldives +Malaysia = Malaysia +Nepal = Nepal +Oman = Oman +Philippines = Philippines +Pakistan = Pakistan +Palestinian territories = Palestinian territories +Qatar = Qatar +Saudi Arabia = Saudi Arabia +Singapore = Singapore +Syria = Syria +Thailand = Thailand +Tajikistan = Tajikistan +Timor-Leste = Timor-Leste +Turkmenistan = Turkmenistan +Turkey = Turkey +Taiwan = Taiwan +Uzbekistan = Uzbekistan +Vietnam = Vietnam +Yemen = Yemen +Africa = Africa +Angola = Angola +Burkina Faso = Burkina Faso +Burundi = Burundi +Benin = Benin +Botswana = Botswana +Congo, Democratic Republic of the = Congo, Democratic Republic of the +Central African Republic = Central African Republic +Congo, Republic of the = Congo, Republic of the +Côte d'Ivoire = Côte d'Ivoire +Cameroon = Cameroon +Cape Verde = Cape Verde +Djibouti = Djibouti +Algeria = Algeria +Egypt = Egypt +Western Sahara = Western Sahara +Eritrea = Eritrea +Ethiopia = Ethiopia +Gabon = Gabon +Ghana = Ghana +Gambia = Gambia +Guinea = Guinea +Equatorial Guinea = Equatorial Guinea +Guinea-Bissau = Guinea-Bissau +Kenya = Kenya +Comoros = Comoros +Liberia = Liberia +Lesotho = Lesotho +Libya = Libya +Morocco = Morocco +Madagascar = Madagascar +Mali = Mali +Mauritania = Mauritania +Mauritius = Mauritius +Malawi = Malawi +Mozambique = Mozambique +Namibia = Namibia +Niger = Niger +Nigeria = Nigeria +Rwanda = Rwanda +Seychelles = Seychelles +Sudan = Sudan +Sierra Leone = Sierra Leone +Senegal = Senegal +Somalia = Somalia +South Sudan = South Sudan +São Tomé and PrÃncipe = São Tomé and PrÃncipe +Swaziland = Swaziland +Chad = Chad +Togo = Togo +Tunisia = Tunisia +Tanzania = Tanzania +Uganda = Uganda +Mayotte = Mayotte +South Africa = South Africa +Zambia = Zambia +Zimbabwe = Zimbabwe +America = America +Antigua and Barbuda = Antigua and Barbuda +Anguilla = Anguilla +Argentina = Argentina +American Samoa = American Samoa +Aruba = Aruba +Barbados = Barbados +Saint Barthélemy = Saint Barthélemy +Bermuda = Bermuda +Bolivia = Bolivia +Bonaire, Sint Eustatius, Saba (Caribbean Netherlands) = Bonaire, Sint Eustatius, Saba (Caribbean Netherlands) +Brazil = Brazil +Bahamas = Bahamas +Belize = Belize +Canada = Canada +Chile = Chile +Colombia = Colombia +Costa Rica = Costa Rica +Cuba = Cuba +Curaçao = Curaçao +Dominica = Dominica +Dominican Republic = Dominican Republic +Ecuador = Ecuador +Grenada = Grenada +French Guiana = French Guiana +Guadeloupe = Guadeloupe +Guatemala = Guatemala +Guyana = Guyana +Honduras = Honduras +Haiti = Haiti +Jamaica = Jamaica +Saint Kitts und Nevis = Saint Kitts und Nevis +Cayman Islands = Cayman Islands +Saint Lucia = Saint Lucia +Saint Martin = Saint Martin +Martinique = Martinique +Montserrat = Montserrat +Mexico = Mexico +Nicaragua = Nicaragua +Panama = Panama +Peru = Peru +Saint Pierre and Miquelon = Saint Pierre and Miquelon +Puerto Rico = Puerto Rico +Paraguay = Paraguay +Suriname = Suriname +El Salvador = El Salvador +Sint Maarten = Sint Maarten +Turks and Caicos Islands = Turks and Caicos Islands +Trinidad and Tobago = Trinidad and Tobago +USA = USA +Uruguay = Uruguay +Saint Vincent and the Grenadines = Saint Vincent and the Grenadines +Venezuela = Venezuela +Virgin Islands, British = Virgin Islands, British +Virgin Islands, United States = Virgin Islands, United States +Australia and Oceania = Australia and Oceania +Australia = Australia +Cocos (Keeling) Islands = Cocos (Keeling) Islands +Cook Islands = Cook Islands +Christmas Island <Australia> = Christmas Island <Australia> +Fiji = Fiji +Micronesia, Federated States of = Micronesia, Federated States of +Guam = Guam +Kiribati = Kiribati +Marshall Islands = Marshall Islands +Northern Mariana Islands = Northern Mariana Islands +New Caledonia = New Caledonia +Norfolk Island = Norfolk Island +Nauru = Nauru +New Zealand = New Zealand +French Polynesia = French Polynesia +Papua New Guinea = Papua New Guinea +Palau = Palau +Solomon Islands = Solomon Islands +Tokelau = Tokelau +Tonga = Tonga +Tuvalu = Tuvalu +Vanuatu = Vanuatu +Wallis and Futuna = Wallis and Futuna +Samoa = Samoa +Arctic = Arctic +Antarctic = Antarctic +Antarctica = Antarctica +Heard Islands and McDonald Islands = Heard Islands and McDonald Islands +Atlantic Ocean = Atlantic Ocean +Bouvet Island = Bouvet Island +Falkland Islands = Falkland Islands +Faroe Islands = Faroe Islands +Greenland = Greenland +South Georgia and the South Sandwich Islands = South Georgia and the South Sandwich Islands +Saint Helena, Ascension and Tristan da Cunha = Saint Helena, Ascension and Tristan da Cunha +Svalbard and Jan Mayen = Svalbard and Jan Mayen +Indian Ocean = Indian Ocean +British Indian Ocean Territory = British Indian Ocean Territory +Réunion = Réunion +French Southern and Antarctic Lands = French Southern and Antarctic Lands +Pacific Ocean = Pacific Ocean +Niue = Niue +Pitcairn = Pitcairn +United States Minor Outlying Islands = United States Minor Outlying Islands +Outer Space = Outer Space +International organisations and communities of states = International organisations and communities of states +Entire world = Entire world +Palestinian people = Palestinian people +Arab people = Arab people +Jewish people = Jewish people +Fictitious places = Fictitious places +Other places = Other places +Ancient Near East = Ancient Near East +Ancient Greece = Ancient Greece +Roman Empire = Roman Empire +Byzantine Empire = Byzantine Empire +Ottoman Empire = Ottoman Empire +Saudi-Iraqi neutral zone (-1993) = Saudi-Iraqi neutral zone (-1993) +Austria (- 12.11.1918) = Austria (- 12.11.1918) +Czechoslovakia (-1993) = Czechoslovakia (-1993) +Serbia and Montenegro (2003-2006) = Serbia and Montenegro (2003-2006) +German Democratic Republic (-1990) = German Democratic Republic (-1990) +German Reich = German Reich +Metropolitan France (1993-1997) = Metropolitan France (1993-1997) +Soviet Union (-1992) = Soviet Union (-1992) +Yugoslavia (-2003) = Yugoslavia (-2003) +Burma (-1989) = Burma (-1989) +Sikkim (-1975) = Sikkim (-1975) +East Timor (-2002) = East Timor (-2002) +Vietnam, Republic of (i.e. South Vietnam) (-1977) = Vietnam, Republic of (i.e. South Vietnam) (-1977) +Yemen (i.e. South Yemen) (-1990) = Yemen (i.e. South Yemen) (-1990) +Djibouti (French Territory of the Afars and the Issas) (-1977) = Djibouti (French Territory of the Afars and the Issas) (-1977) +Benin (Dahomey) (-1977) = Benin (Dahomey) (-1977) +Burkina Faso (Upper Volta) (-1984) = Burkina Faso (Upper Volta) (-1984) +Zimbabwe (Southern Rhodesia) (-1980) = Zimbabwe (Southern Rhodesia) (-1980) +Congo, Democratic Republic of the (Zaire) (-1997) = Congo, Democratic Republic of the (Zaire) (-1997) +Netherlands Antilles (1974-2011) = Netherlands Antilles (1974-2011) +Panama Canal Zone (-1980) = Panama Canal Zone (-1980) +Canton and Enderbury Islands (-1984) = Canton and Enderbury Islands (-1984) +Gilbert and Ellice Islands (-1979) = Gilbert and Ellice Islands (-1979) +Vanuatu (New Hebrides) (-1980) = Vanuatu (New Hebrides) (-1980) +Pacific Islands, Trust Territory of the (-1986) = Pacific Islands, Trust Territory of the (-1986) +British Antarctic Territory (-1979) = British Antarctic Territory (-1979) +French Southern and Antarctic Lands (-1979) = French Southern and Antarctic Lands (-1979) +Queen Maud Land (-1983) = Queen Maud Land (-1983) +Johnston Island (-1986) = Johnston Island (-1986) +Midway Islands (-1986) = Midway Islands (-1986) +United States Miscellaneous Pacific Islands (-1986) = United States Miscellaneous Pacific Islands (-1986) +Wake Island (-1986) = Wake Island (-1986) +Abkhazian = Abkhazian +Acoli = Acoli +Afrikaans = Afrikaans +Afro-Asiatic = Afro-Asiatic +Ainu = Ainu +Akkadian = Akkadian +Albanian = Albanian +Aleut = Aleut +Algonquian = Algonquian +Altaic = Altaic +Amharic = Amharic +Ancient Egyptian = Ancient Egyptian +Ancient Greek = Ancient Greek +Arabic = Arabic +Aragonese = Aragonese +Aramaic = Aramaic +Armenian = Armenian +Aromanian = Aromanian +Artificial = Artificial +Assamese = Assamese +Australian = Australian +Austronesian = Austronesian +Avestan = Avestan +Awadhi = Awadhi +Azerbaijani = Azerbaijani +Baltic = Baltic +Baluchi = Baluchi +Bambara = Bambara +Banda = Banda +Bantu = Bantu +Bashkir = Bashkir +Basque = Basque +Belarusian = Belarusian +Bemba = Bemba +Bengali = Bengali +Berber = Berber +Bhojpuri = Bhojpuri +Bihari = Bihari +Bini = Bini +Bosnian = Bosnian +Braj = Braj +# Brazilian Portugese = # Brazilian Portugese +Brazilian Portugese = Brazilian Portugese +Breton = Breton +Buginese = Buginese +Bulgarian = Bulgarian +Buriat = Buriat +Burmese = Burmese +Catalan = Catalan +Caucasian = Caucasian +Central American Indian = Central American Indian +Central Khmer = Central Khmer +Chagatai = Chagatai +Chamic = Chamic +Chechen = Chechen +Cherokee = Cherokee +Chichewa = Chichewa +Chinook = Chinook +Church Slavic = Church Slavic +Chuvash = Chuvash +Classical Syriac = Classical Syriac +Coptic = Coptic +Cornish = Cornish +Cree = Cree +Creole = Creole +Creoles = Creoles +Croatian = Croatian +Cushitic = Cushitic +Czech = Czech +Dakota = Dakota +Danish = Danish +Delaware = Delaware +Divehi = Divehi +Dogri = Dogri +Dravidian = Dravidian +Duala = Duala +Dzongkha = Dzongkha +Esperanto = Esperanto +Estonian = Estonian +Ewe = Ewe +Fanti = Fanti +Faroese = Faroese +Finnish = Finnish +Finno-Ugrian = Finno-Ugrian +Fulah = Fulah +Ga = Ga +Gaelic = Gaelic +Galician = Galician +Ganda = Ganda +Geez = Geez +Georgian = Georgian +Germanic = Germanic +Gothic = Gothic +Gujarati = Gujarati +Haitian = Haitian +Hausa = Hausa +Hawaiian = Hawaiian +Herero = Herero +Hittite = Hittite +Hungarian = Hungarian +Icelandic = Icelandic +Ido = Ido +Igbo = Igbo +Ijo = Ijo +Indic = Indic +Indo-European = Indo-European +Indonesian = Indonesian +Ingush = Ingush +Interlingua = Interlingua +Inuktitut = Inuktitut +Inupiaq = Inupiaq +Iranian = Iranian +Iroquoian = Iroquoian +Javanese = Javanese +Judeo-Arabic = Judeo-Arabic +Judeo-Persian = Judeo-Persian +Kabardian = Kabardian +Kabyle = Kabyle +Kachin = Kachin +Kalaallisut = Kalaallisut +Kalmyk = Kalmyk +Kamba = Kamba +Kannada = Kannada +Kanuri = Kanuri +Kara-Kalpak = Kara-Kalpak +Karelian = Karelian +Kashmiri = Kashmiri +Kashubian = Kashubian +Kazakh = Kazakh +Khasi = Khasi +Khoisan = Khoisan +Khotanese = Khotanese +Kikuyu = Kikuyu +Kinyarwanda = Kinyarwanda +Kirghiz = Kirghiz +Komi = Komi +Kongo = Kongo +Konkani = Konkani +Korean = Korean +Kpelle = Kpelle +Kru languages = Kru languages +Kuanyama = Kuanyama +Kumyk = Kumyk +Kurdish = Kurdish +Kurukh = Kurukh +Ladino = Ladino +Lamba = Lamba +Land Dayak = Land Dayak +Lao = Lao +Latin = Latin +Latvian = Latvian +Lezghian = Lezghian +Lingala = Lingala +Lithuanian = Lithuanian +Low German = Low German +Lower Sorbian = Lower Sorbian +Luba-Katanga = Luba-Katanga +Luba-Lulua = Luba-Lulua +Lule Sami = Lule Sami +Luo = Luo +Luxembourgish = Luxembourgish +Macedonian = Macedonian +Maithili = Maithili +Makasar = Makasar +Malagasy = Malagasy +Malay = Malay +Malayalam = Malayalam +Maltese = Maltese +Manchu = Manchu +Mandar = Mandar +Mandingo = Mandingo +Manipuri = Manipuri +Manx = Manx +Maori = Maori +Marathi = Marathi +Mari = Mari +Marwari = Marwari +Masai = Masai +Mayan = Mayan +Mende = Mende +Middle English = Middle English +Middle French = Middle French +Middle High German = Middle High German +Mi-kmaq = Mi-kmaq +Mohawk = Mohawk +Mongo = Mongo +Mongolian = Mongolian +Mon-Khmer = Mon-Khmer +Mossi = Mossi +Multiple = Multiple +Munda = Munda +Music = Music +Nahuatl = Nahuatl +Navajo = Navajo +Ndonga = Ndonga +Nepal Bhasa = Nepal Bhasa +Nepali = Nepali +Niger-Kordofanian = Niger-Kordofanian +Nilo-Saharan = Nilo-Saharan +Nogai = Nogai +North American Indian = North American Indian +North Ndebele = North Ndebele +Northern Frisian = Northern Frisian +Norwegian = Norwegian +Norwegian BokmÃ¥l = Norwegian BokmÃ¥l +Nubian = Nubian +Nyamwezi = Nyamwezi +Occitan = Occitan +Ojibwa = Ojibwa +Old English = Old English +Old French = Old French +Old High German = Old High German +Old Norse = Old Norse +Old Persian = Old Persian +Old Provençal = Old Provençal +Oriya = Oriya +Oromo = Oromo +Ossetian = Ossetian +Otomian = Otomian +Ottoman Turkish = Ottoman Turkish +Pahlavi = Pahlavi +Pali = Pali +Panjabi = Panjabi +Papiamento = Papiamento +Papuan = Papuan +Pedi = Pedi +Persian = Persian +Phoenician = Phoenician +Polish = Polish +Prakrit languages = Prakrit languages +Pushto = Pushto +Quechua = Quechua +Rajasthani = Rajasthani +Romance = Romance +Romanian = Romanian +Romansh = Romansh +Romany = Romany +Rundi = Rundi +Russian = Russian +Salishan = Salishan +Samaritan Aramaic = Samaritan Aramaic +Sami languages = Sami languages +Samoan = Samoan +Sanskrit = Sanskrit +Santali = Santali +Sardinian = Sardinian +Scots = Scots +Semitic = Semitic +Serbian = Serbian +Serer = Serer +Shona = Shona +Sidamo = Sidamo +Sindhi = Sindhi +Sinhala = Sinhala +Sino-Tibetan = Sino-Tibetan +Slave = Slave +Slavic = Slavic +Slovak = Slovak +Slovenian = Slovenian +Sogdian = Sogdian +Somali = Somali +Songhai = Songhai +Sorbian = Sorbian +Southern Altai = Southern Altai +Southern Sotho = Southern Sotho +Sumerian = Sumerian +Swahili = Swahili +Swedish = Swedish +Swiss German = Swiss German +Syriac = Syriac +Tagalog = Tagalog +Tahitian = Tahitian +Tai = Tai +Tajik = Tajik +Tamashek = Tamashek +Tamil = Tamil +Tatar = Tatar +Telugu = Telugu +Tetum = Tetum +Thai = Thai +Tibetan = Tibetan +Tigre = Tigre +Tigrinya = Tigrinya +Timne = Timne +Tiv = Tiv +Tok = Tok +Tsonga = Tsonga +Tswana = Tswana +Tupi = Tupi +Turkmen = Turkmen +Tuvinian = Tuvinian +Twi = Twi +Udmurt = Udmurt +Ugaritic = Ugaritic +Uighur = Uighur +Ukrainian = Ukrainian +Umbundu = Umbundu +Undetermined = Undetermined +Upper Sorbian = Upper Sorbian +Urdu = Urdu +Uzbek = Uzbek +Venda = Venda +Vietnamese = Vietnamese +Volapük = Volapük +Votic = Votic +Wakashan = Wakashan +Walamo = Walamo +Walloon = Walloon +Western Frisian = Western Frisian +Wolof = Wolof +Xhosa = Xhosa +Yakut = Yakut +Yao = Yao +Yiddish = Yiddish +Yoruba = Yoruba +Yupik = Yupik +Zande = Zande +Zapotec = Zapotec +Zhuang = Zhuang +Zulu = Zulu +Zuni = Zuni +Check interlibrary loans = Check interlibrary loans +Check availability via interlibrary loans = Check availability via interlibrary loans +Initiate purchase order = Initiate purchase order +Please purchase = Please purchase + + +################### finc-spezifisch hinzugefuegt ########################## +p. = p. \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php b/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php index 36502f9cf8590a8b89e2a5eda93adab3daaba3d3..1620d1a8c75b78b342b46201579d073d40e1a351 100644 --- a/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/MultiIndexListener.php @@ -124,7 +124,11 @@ class MultiIndexListener } else { // In any other context, we should make sure our field values are // all legal. - $shards = explode(',', implode(',', $params->get('shards'))); + + // Check if $params->get('shards') returns an array to prevent + // invalid argument warnings. + $shards = explode(',', implode(',', + (is_array($params->get('shards')) ? $params->get('shards') : array()))); $fields = $this->getFields($shards); $specs = $this->getSearchSpecs($fields); $backend->getQueryBuilder()->setSpecs($specs); diff --git a/module/VuFind/src/VuFind/Search/Solr/Params.php b/module/VuFind/src/VuFind/Search/Solr/Params.php index f08961a0e9ecea12fe095ce1e582645875bd298a..861d6df6b70a09341d3e59de2b27afbc1dd63e1d 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Params.php +++ b/module/VuFind/src/VuFind/Search/Solr/Params.php @@ -459,7 +459,7 @@ class Params extends \VuFind\Search\Base\Params // Shards $allShards = $this->getOptions()->getShards(); $shards = $this->getSelectedShards(); - if (is_null($shards)) { + if (empty($shards)) { $shards = array_keys($allShards); } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php index 196f4ccaacf10504f668d1d019e7f4d9ddb7611f..06a75ee70d60cbbd521ab53df18b7bd57cd43f5c 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php @@ -27,6 +27,7 @@ */ namespace VuFindConsole\Controller; use File_MARC, File_MARCXML, VuFind\Sitemap, Zend\Console\Console; +use VuFind\Log\Logger; use VuFindSearch\Backend\Solr\Document\UpdateDocument; use VuFindSearch\Backend\Solr\Record\SerializableRecord; @@ -433,6 +434,152 @@ class UtilController extends AbstractBase return $this->getSuccessResponse(); } + /** + * Tool to synchronize language files + * + * @return \Zend\Console\Response + */ + public function synclangfilesAction() + { + $this->checkLocalSetting(); + + $this->consoleOpts->addRules( + [ + 'h|help' => 'Get help', + 'enable-comments' => 'Write output as ini comments', + 'enable-fallback' => 'Enable fallback languages', + 'ignore-local-dir' => 'ignore VUFIND_LOCAL_DIR', + 'write-keys' => 'Write keys of language files as translation', + 'write-to-ini' => 'Write missing lines directly to .ini files', + ] + ); + + if ($this->consoleOpts->getOption('h') + || $this->consoleOpts->getOption('help') + ) { + Console::writeLine('Synchronize language files.'); + Console::writeLine(''); + Console::writeLine( + 'Options:' + ); + Console::writeLine( + '--help this output.' + ); + Console::writeLine( + '--enable-comments write output as ini comments (disabled by default).' + ); + Console::writeLine( + '--enable-fallback fallback languages are enabled (by default, fallback logic is overriden).' + ); + Console::writeLine( + '--ignore-local-dir ignore VUFIND_LOCAL_DIR if set and only sync language files in VUFIND_HOME/languages.' + ); + Console::writeLine( + '--write-keys write language file keys as translation (write translation by default).' + ); + Console::writeLine( + '--write-to-ini write missing lines directly into .ini files (writes to stdout by default).' + ); + return $this->getFailureResponse(); + } + + $config = $this->getServiceLocator()->get('VuFind\Config')->get('config'); + + $pathStack = [ + APPLICATION_PATH . '/languages' + ]; + if (!$this->consoleOpts->getOption('ignore-local-dir') && LOCAL_OVERRIDE_DIR != '') + $pathStack[] = LOCAL_OVERRIDE_DIR . '/languages'; + + $fallbackLocales = $config->Site->language == 'en' + ? 'en' + : [$config->Site->language, 'en']; + + $languageFile = new \VuFind\I18n\Translator\Loader\ExtendedIni( + $pathStack, + ($this->consoleOpts->getOption('enable-fallback') + ? $fallbackLocales : []) + ); + + $languages = $config->Languages->toArray(); + + $diffArray = []; + + foreach ($languages as $lang => $value) { + if (is_array($fallbackLocales)) { + foreach ($fallbackLocales as $fallback) { + if ($fallback != $lang) { + $dataLang = $languageFile->load($lang, null); + $dataFallback = $languageFile->load($fallback, null); + $diffArray[$fallback][$lang] = array_diff_key($dataFallback->getArrayCopy(), $dataLang->getArrayCopy()); + } + } + } else { + if ($fallbackLocales != $lang) { + $dataLang = $languageFile->load($lang, null); + $dataFallback = $languageFile->load($fallbackLocales, null); + $diffArray[$fallbackLocales][$lang] = array_diff_key($dataFallback->getArrayCopy(), $dataLang->getArrayCopy()); + } + } + } + + if (!empty ($diffArray)) { + foreach ($diffArray as $referenceLang => $targetLang) { + foreach ($targetLang as $lang => $messages) { + if (count($messages)) { + $counter = 0; + + if ($this->consoleOpts->getOption('write-to-ini')) { + if ($this->consoleOpts->getOption('ignore-local-dir') && + file_exists($pathStack[0] . "/" . $lang . ".ini")) { + $writerPath = "file://" . $pathStack[0] . "/" . $lang . ".ini"; + } elseif (!$this->consoleOpts->getOption('ignore-local-dir') && + file_exists($pathStack[1] . "/" . $lang . ".ini")) { + $writerPath = "file://" . $pathStack[1] . "/" . $lang . ".ini"; + } else { + $writerPath = "php://stdout"; + } + } else { + $writerPath = "php://stdout"; + } + $writer = new \Zend\Log\Writer\Stream($writerPath); + $writer->setFormatter( + new \Zend\Log\Formatter\Simple('%message%') + ); + + $logger = new Logger(); + $logger->addWriter($writer); + $logger->info(";------"); + $logger->info(";missing lines from " . $referenceLang . ".ini (compiled from files in " . implode(', ', $pathStack) . ")" + . ($this->consoleOpts->getOption('write-to-ini') ? '' : ' in ' . $lang . '.ini')); + $logger->info(";------"); + + foreach($messages as $key => $message) { + if ($key != "@parent_ini") { + $counter++; + if ($this->consoleOpts->getOption('write-keys')) { + $logger->info( + ($this->consoleOpts->getOption('enable-comments') ? ";" : "") . + $key. " = " . $key); + } else { + $logger->info( + ($this->consoleOpts->getOption('enable-comments') ? ";" : "") . + $key. " = " . $message); + } + } + } + + if ($this->consoleOpts->getOption('write-to-ini')) { + Console::writeLine('Added ' . $counter . ' lines from ' . $referenceLang . '.ini (compiled from files in ' . implode(', ', $pathStack) . ') to ' . $writerPath); + } + unset($logger); + unset($writer); + } + } + } + } + } + /** * Tool to auto-fill hierarchy cache. * diff --git a/module/VuFindHttp/src/VuFindHttp/HttpService.php b/module/VuFindHttp/src/VuFindHttp/HttpService.php index 553c023125fa3b1083fa32ae78b527e12ed39cac..48e6eefc44922337b24dfc2c6482838ef6c214e0 100644 --- a/module/VuFindHttp/src/VuFindHttp/HttpService.php +++ b/module/VuFindHttp/src/VuFindHttp/HttpService.php @@ -118,8 +118,9 @@ class HttpService implements HttpServiceInterface * * @return \Zend\Http\Response */ - public function get($url, array $params = array(), $timeout = null) - { + public function get($url, array $params = array(), $timeout = null, + array $headers = array() + ) { if ($params) { $query = $this->createQueryString($params); if (strpos($url, '?') !== false) { @@ -130,6 +131,9 @@ class HttpService implements HttpServiceInterface } $client = $this->createClient($url, \Zend\Http\Request::METHOD_GET, $timeout); + if ($headers) { + $client->setHeaders($headers); + } return $this->send($client); } @@ -144,13 +148,16 @@ class HttpService implements HttpServiceInterface * @return \Zend\Http\Response */ public function post($url, $body = null, $type = 'application/octet-stream', - $timeout = null + $timeout = null, array $headers = array() ) { $client = $this->createClient($url, \Zend\Http\Request::METHOD_POST, $timeout); $client->setRawBody($body); $client->setHeaders( - array('Content-Type' => $type, 'Content-Length' => strlen($body)) + array_merge( + array('Content-Type' => $type, 'Content-Length' => strlen($body)), + $headers + ) ); return $this->send($client); } diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php index 6f7408c403302fb0474a9dd4fb6a5075a6a8045e..1932dc217f59050327e84d287177a2e12ac537e5 100644 --- a/module/finc/config/module.config.php +++ b/module/finc/config/module.config.php @@ -6,7 +6,7 @@ $config = array( 'plugin_managers' => array( 'ils_driver' => array( 'factories' => array( - 'fincdaia' => 'finc\ILS\Driver\Factory::getFincDAIA', + 'fincils' => 'finc\ILS\Driver\Factory::getFincILS', ), 'invokables' => array( 'daia' => 'finc\ILS\Driver\DAIA', @@ -15,11 +15,40 @@ $config = array( ), 'recorddriver' => array( 'factories' => array( - 'solrmarcremote' => 'finc\RecordDriver\Factory::getSolrMarcRemote' + 'solrdefault' => 'finc\RecordDriver\Factory::getSolrDefault', + 'solrmarc' => 'finc\RecordDriver\Factory::getSolrMarc', + 'solrmarcremote' => 'finc\RecordDriver\Factory::getSolrMarcRemote', + 'solrmarcremotefinc' => 'finc\RecordDriver\Factory::getSolrMarcRemoteFinc', + 'solrai' => 'finc\RecordDriver\Factory::getSolrAI', + ), + ), + 'resolver_driver' => array( + 'factories' => array( + 'redi' => 'finc\Resolver\Driver\Factory::getRedi', ), ), ), 'recorddriver_tabs' => array( + 'finc\RecordDriver\SolrDefault' => array( + 'tabs' => array ( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewArray', + ), + 'defaultTab' => null, + ), + 'finc\RecordDriver\SolrMarc' => array( + 'tabs' => array( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewMARC', + ), + 'defaultTab' => null, + ), 'finc\RecordDriver\SolrMarcRemote' => array( 'tabs' => array( 'Holdings' => 'HoldingsILS', 'Description' => 'Description', @@ -31,6 +60,27 @@ $config = array( ), 'defaultTab' => null, ), + 'finc\RecordDriver\SolrMarcRemoteFinc' => array( + 'tabs' => array( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'Preview' => 'preview', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewMARC', + ), + 'defaultTab' => null, + ), + 'finc\RecordDriver\SolrAI' => array( + 'tabs' => array ( + 'Holdings' => 'HoldingsILS', 'Description' => 'Description', + 'TOC' => 'TOC', 'UserComments' => 'UserComments', + 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', + 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', + 'Details' => 'StaffViewArray', + ), + 'defaultTab' => null, + ), ), ), ); diff --git a/module/finc/src/finc/ILS/Driver/DAIA.php b/module/finc/src/finc/ILS/Driver/DAIA.php index 99894919879c70e1ea4ba662714dbd5e978d4ff4..54bccd83b114122f3a7bc15b15b2a325ab6151cb 100644 --- a/module/finc/src/finc/ILS/Driver/DAIA.php +++ b/module/finc/src/finc/ILS/Driver/DAIA.php @@ -70,10 +70,18 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn */ protected $daiaResponseFormat; - /** + /** + * Flag to enable multiple DAIA-queries + * + * @var boolean + */ + protected $multiQuery = false; + + /** * DAIA legacySupport flag * * @var boolean + * @deprecated Will be removed in the next driver version */ protected $legacySupport = false; @@ -128,6 +136,11 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn $this->debug("No daiaIdPrefix setting found, using default: ppn:"); $this->daiaIdPrefix = "ppn:"; } + if (isset($this->config['DAIA']['multiQuery'])) { + $this->multiQuery = $this->config['DAIA']['multiQuery']; + } else { + $this->debug("No multiQuery setting found, using default: false"); + } } /** @@ -170,19 +183,27 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn * * @param string $id The record id to retrieve the holdings for * - * @throws ILSException * @return mixed On success, an associative array with the following keys: * id, availability (boolean), status, location, reserve, callnumber. */ public function getStatus($id) { - if ($this->daiaResponseFormat == 'xml') { + if ($this->legacySupport) { + // we are in legacySupport mode, so use the deprecated + // getXMLStatus() method return $this->getXMLStatus($id); - } elseif ($this->daiaResponseFormat == 'json') { - return $this->getJSONStatus($id); } else { - throw new ILSException('No matching format found for status retrieval.'); + // let's retrieve the DAIA document by URI + $rawResult = $this->doHTTPRequest($this->generateURI($id)); + // extract the DAIA document for the current id from the + // HTTPRequest's result + $doc = $this->extractDaiaDoc($id, $rawResult); + if (!is_null($doc)) { + // parse the extracted DAIA document and return the status info + return $this->parseDaiaDoc($id, $doc); + } } + return []; } /** @@ -190,29 +211,58 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn * * This is responsible for retrieving the status information for a * collection of records. + * As the DAIA Query API supports querying multiple ids simultaneously + * (all ids divided by "|") getStatuses(ids) would call getStatus(id) only + * once, id containing the list of ids to be retrieved. Apart from the + * legacySupport this would cause some trouble as the list of ids does not + * necessarily correspond to the VuFind Record-id. Therefore getStatuses(ids) + * has its own logic for multiQuery-support and performs the HTTPRequest + * itself, retrieving one DAIA response for all ids and uses helper + * functions to split this one response into documents corresponding to the + * queried ids. + * If multiQueries are not supported, getStatus(id) is used. * - * @param array $ids The array of record ids to retrieve the status for + * @param array $ids The array of record ids to retrieve the status for * - * @throws ILSException - * @return array An array of getStatus() return values on success. + * @return array An array of status information values on success. */ public function getStatuses($ids) { - $items = []; + $status = []; - if ($this->daiaResponseFormat == 'xml') { + if ($this->legacySupport) { + // we are in legacySupport mode, so use the deprecated + // getXMLStatus() method for each id foreach ($ids as $id) { - $items[] = $this->getXMLShortStatus($id); - } - } elseif ($this->daiaResponseFormat == 'json') { - foreach ($ids as $id) { - $items[] = $this->getJSONStatus($id); + $status[] = $this->getXMLShortStatus($id); } } else { - throw new ILSException('No matching format found for status retrieval.'); + if ($this->multiQuery) { + // perform one DAIA query with multiple URIs + $rawResult = $this->doHTTPRequest($this->generateMultiURIs($ids)); + // now we need to reestablish the key-value pair id=>document as + // the id used in VuFind can differ from the document-URI + // (depending on how the URI is generated) + foreach ($ids as $id) { + // it is assumed that each DAIA document has a unique URI, + // so get the document with the corresponding id + $doc = $this->extractDaiaDoc($id, $rawResult); + if (!is_null($doc)) { + // a document with the corresponding id exists, which + // means we got status information for that record + $status[] = $this->parseDaiaDoc($id, $doc); + } + unset($doc); + } + } else { + // multiQuery is not supported, so retrieve DAIA documents by + // performing getStatus(id) for all ids + foreach ($ids as $id) { + $status[] = $this->getStatus($id); + } + } } - - return $items; + return $status; } /** @@ -224,8 +274,6 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data * - * @throws \VuFind\Exception\Date - * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. @@ -280,11 +328,11 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn $http_headers = [ "Content-type: " . $contentTypes[$this->daiaResponseFormat], - "Accept: " . $contentTypes[$this->daiaResponseFormat] + "Accept: " . $contentTypes[$this->daiaResponseFormat], ]; $params = [ - "id" => $this->daiaIdPrefix . $id, + "id" => $id, "format" => $this->daiaResponseFormat, ]; @@ -319,99 +367,494 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn // return false as DAIA request failed return false; } + + // check if result matches daiaResponseFormat + if (!preg_match( + "/^" . + str_replace("/", "\/", $contentTypes[$this->daiaResponseFormat]) . + "(\s*)(\;.*)?/", + strtolower($result->getHeaders()->get("ContentType")->getFieldValue()) + )) { + throw new ILSException( + "DAIA-ResponseFormat not supported. Received: " . + $result->getHeaders()->get("ContentType")->getFieldValue() . " - " . + "Expected: " . $contentTypes[$this->daiaResponseFormat] + ); + } + return ($result->getBody()); + } + /** + * Generate a DAIA URI necessary for the query + * + * @param string $id Id of the record whose DAIA document should be queried + * + * @return string URI of the DAIA document + * + * @see http://gbv.github.io/daiaspec/daia.html#query-api + */ + protected function generateURI($id) + { + if ($this->legacySupport) { + return $id; + } else { + return $this->daiaIdPrefix . $id; + } } /** - * Get Status of JSON Result + * Combine several ids to DAIA Query API conform URIs * - * This method gets a json result from the DAIA server and - * analyses it. Than a vufind result is build. + * @param array $ids Array of ids which shall be converted into URIs and + * combined for querying multiple DAIA documents. + * + * @return string Combined URIs (delimited by "|") * - * @param string $id The id of the bib record + * @see http://gbv.github.io/daiaspec/daia.html#query-api + */ + protected function generateMultiURIs($ids) + { + $multiURI = ''; + foreach ($ids as $id) { + $multiURI .= $this->generateURI($id) . "|"; + } + return rtrim($multiURI, "|"); + } + + /** + * Parse a DAIA document depending on its type. * - * @return array() of items + * Parse a DAIA document depending on its type and return a VuFind + * compatible array of status information. + * Supported types are: + * - array (for JSON results) + * - DOMNode (for XML results) + * + * @param string $id Record Id corresponding to the DAIA document + * @param mixed $daiaDoc The DAIA document, supported types are array and + * DOMNode + * + * @return array An array with status information for the record + * @throws ILSException */ - protected function getJSONStatus($id) + protected function parseDaiaDoc($id, $daiaDoc) { - // get daia json request for id and decode it - $daia = json_decode($this->doHTTPRequest($id), true); - $result = []; - if (array_key_exists("message", $daia)) { - // analyse the message for the error handling and debugging + if (is_array($daiaDoc)) { + return $this->parseDaiaArray($id, $daiaDoc); + } elseif (is_subclass_of($daiaDoc, "DOMNode")) { + return $this->parseDaiaDom($id, $daiaDoc); + } else { + throw new ILSException( + 'Unsupported document type (did not match Array or DOMNode).' + ); } - if (array_key_exists("instituion", $daia)) { - // information about the institution that grants or - // knows about services and their availability - // this fields could be analyzed: href, content, id + } + + /** + * Extract a DAIA document identified by an id + * + * This method loops through all the existing DAIA document-elements in + * the given DAIA response and returns the first document whose id matches + * the given id. + * + * @param string $id Record Id of the DAIA document in question. + * @param string $daiaResponse Raw response from DAIA request. + * + * @return Array|DOMNode|null The DAIA document identified by id and + * type depending on daiaResponseFormat. + * @throws ILSException + */ + protected function extractDaiaDoc($id, $daiaResponse) + { + + if ($this->daiaResponseFormat == 'xml') { + try { + $docs = new DOMDocument(); + $docs->loadXML($daiaResponse); + // get all the DAIA documents + $doc = $docs->getElementsByTagName("document"); + if (!is_null($doc) && $this->multiQuery) { + // now loop through the found DAIA documents + for ($i = 0; $i < $doc->length; $i++) { + $attr = $doc->item($i)->attributes; + // DAIA documents should use URIs as value for id + if ($attr->getNamedItem("id")->nodeValue == $this->generateURI($id) + ) { + // we've found the document element with the + // matching URI + return $doc->item($i); + } + } + } elseif (!is_null($doc)) { + // as multiQuery is not enabled we can be sure that the + // DAIA response only contains one document. + return $doc->item(0); + } + // no (id matching) document element found + return null; + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } + + } elseif ($this->daiaResponseFormat == 'json') { + $docs = json_decode($daiaResponse, true); + // do DAIA documents exist? + if (array_key_exists("document", $docs) && $this->multiQuery) { + // now loop through the found DAIA documents + foreach ($docs["document"] as $doc) { + // DAIA documents should use URIs as value for id + if (isset($doc["id"]) + && $doc["id"] == $this->generateURI($id) + ) { + // we've found the document element with the matching URI + return $doc; + } + } + } elseif (array_key_exists("document", $docs)) { + // since a document exists but multiQuery is disabled, the first + // document is returned + return array_shift($docs['document']); + } + // no (id matching) document element found + return null; + } else { + throw new ILSException('Unsupported document format.'); } - if (array_key_exists("document", $daia)) { - // analyse the items - $dummy_item = ["id" => "0815", - "availability" => true, - "status" => "Available", - "location" => "physical location no HTML", - "reserve" => "N", - "callnumber" => "007", - "number" => "1", - "item_id" => "0815", - "barcode" => "1"]; - // each document may contain: id, href, message, item - foreach ($daia["document"] as $document) { - $doc_id = null; - $doc_href = null; - $doc_message = null; - if (array_key_exists("id", $document)) { - $doc_id = $document["id"]; - } - if (array_key_exists("href", $document)) { - // url of the document - $doc_href = $document["href"]; - } - if (array_key_exists("message", $document)) { - // array of messages with language code and content - $doc_message = $document["message"]; - } - // if one or more items exist, iterate and build result-item - if (array_key_exists("item", $document)) { - $number = 0; - foreach ($document["item"] as $item) { - $result_item = []; - $result_item["id"] = $id; - $result_item["item_id"] = $id; - $number++; // count items - $result_item["number"] = $number; - // set default value for barcode - $result_item["barcode"] = "1"; - // set default value for reserve - $result_item["reserve"] = "N"; - // get callnumber - if (isset($item["label"])) { - $result_item["callnumber"] = $item["label"]; - } else { - $result_item["callnumber"] = "Unknown"; + } + + /** + * Parse an array with DAIA status information. + * + * @param string $id Record id for the DAIA array. + * @param array $daiaArray Array with raw DAIA status information. + * + * @return array Array with VuFind compatible status information. + */ + protected function parseDaiaArray($id, $daiaArray) + { + $doc_id = null; + $doc_href = null; + $doc_message = null; + if (array_key_exists("id", $daiaArray)) { + $doc_id = $daiaArray["id"]; + } + if (array_key_exists("href", $daiaArray)) { + // url of the document + $doc_href = $daiaArray["href"]; + } + if (array_key_exists("message", $daiaArray)) { + // array of messages with language code and content + $doc_message = $daiaArray["message"]; + } + // if one or more items exist, iterate and build result-item + if (array_key_exists("item", $daiaArray)) { + $number = 0; + foreach ($daiaArray["item"] as $item) { + $result_item = []; + $result_item["id"] = $id; + $result_item["item_id"] = $id; + $result_item["ilslink"] = $doc_href; + $number++; // count items + $result_item["number"] = $number; + // set default value for barcode + $result_item["barcode"] = "1"; + // set default value for reserve + $result_item["reserve"] = "N"; + // get callnumber + if (isset($item["label"])) { + $result_item["callnumber"] = $item["label"]; + } else { + $result_item["callnumber"] = "Unknown"; + } + // get location + if (isset($item["storage"]["content"])) { + $result_item["location"] = $item["storage"]["content"]; + } else { + $result_item["location"] = "Unknown"; + } + // status and availability will be calculated in own function + $result_item = $this->calculateStatus($item)+$result_item; + // add result_item to the result array + $result[] = $result_item; + } // end iteration on item + } + + return $result; + } + + /** + * Parse a DOMNode Object with DAIA status information. + * + * @param string $id Record id for the DAIA array. + * @param DOMNode $daiaDom DOMNode object with raw DAIA status information. + * + * @return array Array with VuFind compatible status information. + */ + protected function parseDaiaDom($id, $daiaDom) + { + $itemlist = $daiaDom->getElementsByTagName('item'); + $ilslink = ''; + if ($daiaDom->attributes->getNamedItem('href') !== null) { + $ilslink = $daiaDom->attributes + ->getNamedItem('href')->nodeValue; + } + $emptyResult = [ + 'callnumber' => '-', + 'availability' => '0', + 'number' => 1, + 'reserve' => 'No', + 'duedate' => '', + 'queue' => '', + 'delay' => '', + 'barcode' => 'No samples', + 'status' => '', + 'id' => $id, + 'location' => '', + 'ilslink' => $ilslink, + 'label' => 'No samples' + ]; + for ($c = 0; $itemlist->item($c) !== null; $c++) { + $result = [ + 'callnumber' => '', + 'availability' => '0', + 'number' => ($c+1), + 'reserve' => 'No', + 'duedate' => '', + 'queue' => '', + 'delay' => '', + 'barcode' => 1, + 'status' => '', + 'id' => $id, + 'item_id' => '', + 'recallhref' => '', + 'location' => '', + 'location.id' => '', + 'location.href' => '', + 'label' => '', + 'notes' => [], + ]; + if ($itemlist->item($c)->attributes->getNamedItem('id') !== null) { + $result['item_id'] = $itemlist->item($c)->attributes + ->getNamedItem('id')->nodeValue; + } + if ($itemlist->item($c)->attributes->getNamedItem('href') !== null) { + $result['recallhref'] = $itemlist->item($c)->attributes + ->getNamedItem('href')->nodeValue; + } + $departmentElements = $itemlist->item($c) + ->getElementsByTagName('department'); + if ($departmentElements->length > 0) { + if ($departmentElements->item(0)->nodeValue) { + $result['location'] + = $departmentElements->item(0)->nodeValue; + $result['location.id'] = $departmentElements + ->item(0)->attributes->getNamedItem('id')->nodeValue; + $result['location.href'] = $departmentElements + ->item(0)->attributes->getNamedItem('href')->nodeValue; + } + } + $storageElements + = $itemlist->item($c)->getElementsByTagName('storage'); + if ($storageElements->length > 0) { + if ($storageElements->item(0)->nodeValue) { + $result['location'] = $storageElements->item(0)->nodeValue; + //$result['location.id'] = $storageElements->item(0) + // ->attributes->getNamedItem('id')->nodeValue; + $href = $storageElements->item(0)->attributes + ->getNamedItem('href'); + if ($href !== null) { + //href attribute is recommended but not mandatory + $result['location.href'] = $storageElements->item(0) + ->attributes->getNamedItem('href')->nodeValue; + } + //$result['barcode'] = $result['location.id']; + } + } + $barcodeElements + = $itemlist->item($c)->getElementsByTagName('identifier'); + if ($barcodeElements->length > 0) { + if ($barcodeElements->item(0)->nodeValue) { + $result['barcode'] = $barcodeElements->item(0)->nodeValue; + } + } + $labelElements = $itemlist->item($c)->getElementsByTagName('label'); + if ($labelElements->length > 0) { + if ($labelElements->item(0)->nodeValue) { + $result['label'] = $labelElements->item(0)->nodeValue; + $result['callnumber'] + = urldecode($labelElements->item(0)->nodeValue); + } + } + $messageElements + = $itemlist->item($c)->getElementsByTagName('message'); + if ($messageElements->length > 0) { + for ($m = 0; $messageElements->item($m) !== null; $m++) { + $errno = $messageElements->item($m)->attributes + ->getNamedItem('errno')->nodeValue; + if ($errno === '404') { + $result['status'] = 'missing'; + } else if ($this->logger) { + $lang = $messageElements->item($m)->attributes + ->getNamedItem('lang')->nodeValue; + $logString = "[DAIA] message for {$lang}: " + . $messageElements->item($m)->nodeValue; + $this->debug($logString); + } + } + } + + //$loanAvail = 0; + //$loanExp = 0; + //$presAvail = 0; + //$presExp = 0; + + $unavailableElements = $itemlist->item($c) + ->getElementsByTagName('unavailable'); + if ($unavailableElements->item(0) !== null) { + for ($n = 0; $unavailableElements->item($n) !== null; $n++) { + $service = $unavailableElements->item($n)->attributes + ->getNamedItem('service'); + $expectedNode = $unavailableElements->item($n)->attributes + ->getNamedItem('expected'); + $queueNode = $unavailableElements->item($n)->attributes + ->getNamedItem('queue'); + if ($service !== null) { + $service = $service->nodeValue; + if ($service === 'presentation') { + $result['presentation.availability'] = '0'; + $result['presentation_availability'] = '0'; + if ($expectedNode !== null) { + $result['presentation.duedate'] + = $expectedNode->nodeValue; + } + if ($queueNode !== null) { + $result['presentation.queue'] + = $queueNode->nodeValue; + } + $result['availability'] = '0'; + } elseif ($service === 'loan') { + $result['loan.availability'] = '0'; + $result['loan_availability'] = '0'; + if ($expectedNode !== null) { + $result['loan.duedate'] + = $expectedNode->nodeValue; + } + if ($queueNode !== null) { + $result['loan.queue'] = $queueNode->nodeValue; + } + $result['availability'] = '0'; + } elseif ($service === 'interloan') { + $result['interloan.availability'] = '0'; + if ($expectedNode !== null) { + $result['interloan.duedate'] + = $expectedNode->nodeValue; + } + if ($queueNode !== null) { + $result['interloan.queue'] + = $queueNode->nodeValue; + } + $result['availability'] = '0'; + } elseif ($service === 'openaccess') { + $result['openaccess.availability'] = '0'; + if ($expectedNode !== null) { + $result['openaccess.duedate'] + = $expectedNode->nodeValue; + } + if ($queueNode !== null) { + $result['openaccess.queue'] + = $queueNode->nodeValue; + } + $result['availability'] = '0'; } - // get location - if (isset($item["storage"])) { - $result_item["location"] = $item["storage"]["content"]; - } else { - $result_item["location"] = "Unknown"; + } + // TODO: message/limitation + if ($expectedNode !== null) { + $result['duedate'] = $expectedNode->nodeValue; + } + if ($queueNode !== null) { + $result['queue'] = $queueNode->nodeValue; + } + } + } + + $availableElements = $itemlist->item($c) + ->getElementsByTagName('available'); + if ($availableElements->item(0) !== null) { + for ($n = 0; $availableElements->item($n) !== null; $n++) { + $service = $availableElements->item($n)->attributes + ->getNamedItem('service'); + $delayNode = $availableElements->item($n)->attributes + ->getNamedItem('delay'); + if ($service !== null) { + $service = $service->nodeValue; + if ($service === 'presentation') { + $result['presentation.availability'] = '1'; + $result['presentation_availability'] = '1'; + if ($delayNode !== null) { + $result['presentation.delay'] + = $delayNode->nodeValue; + } + $result['availability'] = '1'; + } elseif ($service === 'loan') { + $result['loan.availability'] = '1'; + $result['loan_availability'] = '1'; + if ($delayNode !== null) { + $result['loan.delay'] = $delayNode->nodeValue; + } + $result['availability'] = '1'; + } elseif ($service === 'interloan') { + $result['interloan.availability'] = '1'; + if ($delayNode !== null) { + $result['interloan.delay'] + = $delayNode->nodeValue; + } + $result['availability'] = '1'; + } elseif ($service === 'openaccess') { + $result['openaccess.availability'] = '1'; + if ($delayNode !== null) { + $result['openaccess.delay'] + = $delayNode->nodeValue; + } + $result['availability'] = '1'; } - // status and availability will be calculated in own function - $result_item = $this->calculateStatus($item)+$result_item; - // add result_item to the result array - $result[] = $result_item; - } // end iteration on item - } - } // end iteration on document - // $result[]=$dummy_item; + } + // TODO: message/limitation + if ($delayNode !== null) { + $result['delay'] = $delayNode->nodeValue; + } + } + } + // document has no availability elements, so set availability + // and barcode to -1 + if ($availableElements->item(0) === null + && $unavailableElements->item(0) === null + ) { + $result['availability'] = '-1'; + $result['barcode'] = '-1'; + } + $result['ilslink'] = $ilslink; + $status[] = $result; + /* $status = "available"; + if (loanAvail) return 0; + if (presAvail) { + if (loanExp) return 1; + return 2; + } + if (loanExp) return 3; + if (presExp) return 4; + return 5; + */ } - return $result; + if (count($status) === 0) { + $status[] = $emptyResult; + } + + return $status; } /** - * Calaculate Status and Availability of an item + * Calculate Status and Availability of an item * * If availability is false the string of status will be shown in vufind * @@ -422,26 +865,33 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn protected function calculateStatus($item) { $availability = false; - $status = null; + $status = ''; // status cannot be null as this will crash the translator $duedate = null; if (array_key_exists("available", $item)) { // check if item is loanable or presentation foreach ($item["available"] as $available) { - if ($available["service"] == "loan") { - $availability = true; - } - if ($available["service"] == "presentation") { - $availability = true; + // attribute service can be set once or not + if (isset($available["service"])) { + if ($available["service"] == "loan") { + $availability = true; + } + if ($available["service"] == "presentation") { + $availability = true; + } } } } if (array_key_exists("unavailable", $item)) { foreach ($item["unavailable"] as $unavailable) { - if ($unavailable["service"] == "loan") { - if (isset($unavailable["expected"])) { - $duedate = $unavailable["expected"]; + // attribute service can be set once or not + if (isset($unavailable["service"])) { + if ($unavailable["service"] == "loan") { + $status = "dummy text"; } - $status = "dummy text"; + } + // attribute expected is mandatory for unavailable element + if (isset($unavailable["expected"])) { + $duedate = $unavailable["expected"]; } } } @@ -456,6 +906,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn * @param string $id Document to look up. * * @return array + * + * @deprecated Only kept for legacySupport */ protected function getXMLStatus($id) { @@ -744,6 +1196,8 @@ class DAIA extends \VuFind\ILS\Driver\AbstractBase implements HttpServiceAwareIn * @return mixed On success, an associative array with the following keys: * id, availability (boolean), status, location, reserve, callnumber, duedate, * number + * + * @deprecated Only kept for legacySupport */ public function getXMLShortStatus($id) { diff --git a/module/finc/src/finc/ILS/Driver/Factory.php b/module/finc/src/finc/ILS/Driver/Factory.php index 3f22e073b577d4412c06a5df230bd002b6c41d78..a9c36c434de7fda4ca3382138df3bf943b8f7f15 100644 --- a/module/finc/src/finc/ILS/Driver/Factory.php +++ b/module/finc/src/finc/ILS/Driver/Factory.php @@ -37,19 +37,22 @@ use Zend\ServiceManager\ServiceManager; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki */ -class Factory extends \VuFind\ILS\Driver\Factory +class Factory { /** - * Factory for NoILS driver. + * Factory for FincILS driver. * * @param ServiceManager $sm Service manager. * - * @return NoILS + * @return FincILS */ - public static function getFincDAIA(ServiceManager $sm) + public static function getFincILS(ServiceManager $sm) { - return new FincDAIA($sm->getServiceLocator()->get('VuFind\RecordLoader')); + return new FincILS( + $sm->getServiceLocator()->get('VuFind\RecordLoader'), + $sm->getServiceLocator()->get('VuFind\Config')->get('config') + ); } } \ No newline at end of file diff --git a/module/finc/src/finc/ILS/Driver/FincDAIA.php b/module/finc/src/finc/ILS/Driver/FincDAIA.php deleted file mode 100644 index 52e322738281c2f59402cc5693d69bafb11d2c9a..0000000000000000000000000000000000000000 --- a/module/finc/src/finc/ILS/Driver/FincDAIA.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php -/** - * ILS Driver for VuFind to query availability information via DAIA. - * - * Based on the proof-of-concept-driver by Till Kinstler, GBV. - * - * PHP version 5 - * - * Copyright (C) Oliver Goldschmidt 2010. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * @category VuFind2 - * @package ILS_Drivers - * @author Oliver Goldschmidt <o.goldschmidt@tu-harburg.de> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki - */ -namespace finc\ILS\Driver; -use DOMDocument, VuFind\Exception\ILS as ILSException; - -/** - * ILS Driver for VuFind to query availability information via DAIA. - * - * Based on the proof-of-concept-driver by Till Kinstler, GBV. - * - * @category VuFind2 - * @package ILS_Drivers - * @author Oliver Goldschmidt <o.goldschmidt@tu-harburg.de> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki - */ -class FincDAIA extends PAIA implements \Zend\Log\LoggerAwareInterface -{ - /** - * Identifier used for interaction with ILS - * - * @var string - */ - protected $ilsIdentifier; - - /** - * ISIL used for identifying the correct ILS-identifier if array is returned - * - * @var string - */ - protected $isil; - - /** - * Record loader - * - * @var \VuFind\Record\Loader - */ - protected $recordLoader; - - /** - * Constructor - * - * @param \VuFind\Record\Loader $loader Record loader - */ - public function __construct(\VuFind\Record\Loader $loader) - { - $this->recordLoader = $loader; - } - - /** - * Initialize the driver. - * - * Validate configuration and perform all resource-intensive tasks needed to - * make the driver active. - * - * @throws ILSException - * @return void - */ - public function init() - { - parent::init(); - - // due to section naming changes in DAIA.ini - // switch legacySupport - if ($this->legacySupport) { - // set the ILS-specific recordId for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['Global']['ilsIdentifier'])) { - $this->debug("No ILS-specific identifier configured, setting ilsIdentifier=default."); - $this->ilsIdentifier = "default"; - } else { - $this->ilsIdentifier = $this->config['Global']['ilsIdentifier']; - } - - // get ISIL from config if ILS-specific recordId is barcode for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['Global']['ISIL'])) { - $this->debug("No ISIL for ILS-driver configured."); - $this->isil = ''; - } else { - $this->isil = $this->config['Global']['ISIL']; - } - } else { - // set the ILS-specific recordId for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['DAIA']['ilsIdentifier'])) { - $this->debug("No ILS-specific identifier configured, setting ilsIdentifier=default."); - $this->ilsIdentifier = "default"; - } else { - $this->ilsIdentifier = $this->config['DAIA']['ilsIdentifier']; - } - - // get ISIL from config if ILS-specific recordId is barcode for interaction with ILS - // get the ILS-specific identifier - if (!isset($this->config['DAIA']['ISIL'])) { - $this->debug("No ISIL for ILS-driver configured."); - $this->isil = ''; - } else { - $this->isil = $this->config['DAIA']['ISIL']; - } - } - - } - - /** - * Wrapper implementation of @queryDAIAXML($id) to perform the query - * with the ILS-specific identifier. - * - * @param string $id Document to look up. - * - * @return DOMDocument Object representation of an XML document containing - * content as described in the DAIA format specification. - */ - protected function doHTTPRequest($id) - { - return parent::doHTTPRequest($this->getILSRecordId($id)); - } - - /** - * Get the Record-Object from the RecordDriver. - * - * @param string $id ID of record to retrieve - * - * @return \VuFind\RecordDriver\AbstractBase - */ - protected function getRecord($id) - { - return $this->recordLoader->load($id); - } - - /** - * Get the identifier for the record which will be used for ILS interaction - * - * @param string $id Document to look up. - * - * @return string $ilsRecordId - */ - protected function getILSRecordId($id) - { - //get the ILS-specific recordId - if ($this->ilsIdentifier != "default") { - $ilsRecordId = $this->getRecord($id)->getILSIdentifier($this->ilsIdentifier); - if ($ilsRecordId == '') { - return $id; - } else { - if (is_array($ilsRecordId)) { - // use ISIL for identifying the correct ILS-identifier if array is returned - foreach ($ilsRecordId as $recordId) { - if (preg_match("/^(\(".$this->isil."\)).*$/", $recordId)) { - return substr($recordId, strpos($recordId, "(".$this->isil.")")+strlen("(".$this->isil.")")); - } - } - } - return $ilsRecordId; - } - } - return $id; - } - -} \ No newline at end of file diff --git a/module/finc/src/finc/ILS/Driver/FincILS.php b/module/finc/src/finc/ILS/Driver/FincILS.php new file mode 100644 index 0000000000000000000000000000000000000000..3ebcad7948fab308b77c917d4c48868dc36bcd62 --- /dev/null +++ b/module/finc/src/finc/ILS/Driver/FincILS.php @@ -0,0 +1,338 @@ +<?php +/** + * Finc specific ILS Driver for VuFind, using PAIA and DAIA services. + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki + */ +namespace finc\ILS\Driver; +use VuFind\Exception\ILS as ILSException; + +/** + * Finc specific ILS Driver for VuFind, using PAIA and DAIA services. + * + * @category VuFind2 + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki + */ +class FincILS extends PAIA implements \Zend\Log\LoggerAwareInterface +{ + + /** + * Array that stores the mapping of VuFind record_id to the ILS-specific + * identifier retrieved by @_getILSRecordId() + * + * @var array + */ + private $_idMapper = []; + + /** + * Identifier used for interaction with ILS + * + * @var string + */ + protected $ilsIdentifier; + + /** + * ISIL used for identifying the correct ILS-identifier if array is returned + * + * @var string + */ + protected $isil; + + /** + * Record loader + * + * @var \VuFind\Record\Loader + */ + protected $recordLoader; + + /** + * Main Config + * + * @var null|\Zend\Config\Config + */ + protected $mainConfig; + + /** + * Constructor + * + * @param \VuFind\Record\Loader $loader Record loader + * @param \Zend\Config\Config $mainConfig VuFind main configuration (omit for + * built-in defaults) + */ + public function __construct(\VuFind\Record\Loader $loader, $mainConfig = null) + { + $this->recordLoader = $loader; + $this->mainConfig = $mainConfig; + } + + /** + * Initialize the driver. + * + * Validate configuration and perform all resource-intensive tasks needed to + * make the driver active. + * + * @throws ILSException + * @return void + */ + public function init() + { + parent::init(); + + // due to section naming changes in DAIA.ini switch legacySupport + if ($this->legacySupport) { + // set the ILS-specific recordId for interaction with ILS + + // get the ILS-specific identifier + if (!isset($this->config['Global']['ilsIdentifier'])) { + $this->debug( + "No ILS-specific identifier configured, setting ilsIdentifier=default." + ); + $this->ilsIdentifier = "default"; + } else { + $this->ilsIdentifier = $this->config['Global']['ilsIdentifier']; + } + + // get ISIL from config if ILS-specific recordId is barcode for + // interaction with ILS + if (!isset($this->mainConfig['InstitutionInfo']['isil'])) { + $this->debug("No ISIL defined in section InstitutionInfo in config.ini."); + $this->isil = ''; + } else { + $this->isil = $this->mainConfig['InstitutionInfo']['isil']; + } + } else { + // set the ILS-specific recordId for interaction with ILS + + // get the ILS-specific identifier + if (!isset($this->config['DAIA']['ilsIdentifier'])) { + $this->debug( + "No ILS-specific identifier configured, setting ilsIdentifier=default." + ); + $this->ilsIdentifier = "default"; + } else { + $this->ilsIdentifier = $this->config['DAIA']['ilsIdentifier']; + } + + // get ISIL from config if ILS-specific recordId is barcode for + // interaction with ILS + if (!isset($this->mainConfig['InstitutionInfo']['isil'])) { + $this->debug("No ISIL defined in section InstitutionInfo in config.ini."); + $this->isil = ''; + } else { + $this->isil = $this->mainConfig['InstitutionInfo']['isil']; + } + } + } + + /** + * Get Status + * + * Wrapper implementation of @getStatus($id) to retrieve the status + * information of a certain record by using ILS-specific identifier. + * + * @param string $id The record id to retrieve the holdings for + * + * @return mixed On success, an associative array with the following keys: + * id, availability (boolean), status, location, reserve, callnumber. + */ + public function getStatus($id) + { + return $this->_replaceILSId( + parent::getStatus($this->_getILSRecordId($id)), $id + ); + } + + /** + * Get Statuses + * + * Wrapper implementation of @getStatuses($id) to retrieve status + * information for several records by using ILS-specific identifier. + * + * @param array $ids The array of record ids to retrieve the status for + * + * @return array An array of status information values on success. + */ + public function getStatuses($ids) + { + return $this->_replaceILSIds( + parent::getStatuses($this->_getILSRecordIds($ids)), $ids + ); + } + + /** + * Get the Record-Object from the RecordDriver. + * + * @param string $id ID of record to retrieve + * + * @return \VuFind\RecordDriver\AbstractBase + */ + private function _getRecord($id) + { + return $this->recordLoader->load($id); + } + + /** + * Function to replace the custom ILS specific identifier + * with the VuFind record_id, provided by the mapping array + * $idMapper. + * + * @param array $array Array with status information. + * @param string $id VuFind record_id. + * + * @return mixed + */ + private function _replaceILSId($array, $id) + { + $statuses = []; + foreach ($array as $status) { + if ($status['item_id'] == $this->_idMapper[$id]) { + $status['item_id'] = $id; + } + if ($status['id'] == $this->_idMapper[$id]) { + $status['id'] = $id; + } + $statuses[] = $status; + } + + return $statuses; + } + + /** + * Function to replace the custom ILS specific identifier + * with the VuFind record_id in several status information + * arrays. + * + * @param array $array Array with status information from several records. + * records. + * @param array $ids Array with VuFind record_ids. + * + * @return array + */ + private function _replaceILSIds($array, $ids) + { + $results = []; + foreach ($array as $statuses) { + foreach ($ids as $id) { + if ($this->_containsILSid($statuses, $id)) { + // save the result if _replaceILSId had some effect + $results[] = $this->_replaceILSId($statuses, $id); + } + } + } + + return $results; + } + + /** + * Function to check whether the given array with status information + * contains an ILS-specific identifier, provided by idMapper($id) + * + * @param array $array Array with status information. + * @param string $id VuFind record_id. + * + * @return bool + */ + private function _containsILSid($array, $id) + { + foreach ($array as $status) { + if ($status['item_id'] == $this->_idMapper[$id] + || $status['id'] == $this->_idMapper[$id] + ) { + + return true; + } + } + + return false; + } + + /** + * Get the identifier for the record which will be used for ILS interaction + * + * @param string $id Document to look up. + * + * @return string $ilsRecordId + */ + private function _getILSRecordId($id) + { + //get the ILS-specific recordId + if ($this->ilsIdentifier != "default") { + $ilsRecordId = $this->_getRecord($id) + ->getILSIdentifier($this->ilsIdentifier); + if ($ilsRecordId == '') { + $this->_idMapper[$id] = $id; + + return $id; + } else { + if (is_array($ilsRecordId)) { + // use ISIL for identifying the correct ILS-identifier if + // array is returned + foreach ($ilsRecordId as $recordId) { + if (preg_match("/^(\(".$this->isil."\)).*$/", $recordId)) { + $recordId = substr( + $recordId, + strpos($recordId, "(".$this->isil.")")+strlen("(".$this->isil.")") + ); + $this->_idMapper[$id] = $recordId; + + return $recordId; + } + } + } + $this->_idMapper[$id] = $ilsRecordId; + + return $ilsRecordId; + } + } + $this->_idMapper[$id] = $id; + + return $id; + } + + /** + * Get the identifiers for multiple records + * + * @param array $ids Documents to look up. + * + * @return array $ilsRecordIds + */ + private function _getILSRecordIds($ids) + { + $ilsRecordIds = []; + + if (is_array($ids)) { + foreach ($ids as $id) { + $ilsRecordIds[] = $this->_getILSRecordId($id); + } + + return $ilsRecordIds; + } + + return $ids; + } + +} \ No newline at end of file diff --git a/module/finc/src/finc/ILS/Driver/PAIA.php b/module/finc/src/finc/ILS/Driver/PAIA.php index b504153eae2ef24d43130b05f561d1e3f54fce7e..6df25c92ebf39939bf18f6348c38c7d47c264761 100644 --- a/module/finc/src/finc/ILS/Driver/PAIA.php +++ b/module/finc/src/finc/ILS/Driver/PAIA.php @@ -1,10 +1,6 @@ <?php /** - * ILS Driver for VuFind to get information from PAIA - * - * Authentication in this driver is handled via LDAP, not via normal PICA! - * First check local vufind database, and if no user is found, check LDAP. - * LDAP configuration settings are taken from vufinds config.ini + * PAIA ILS Driver for VuFind to get patron information * * PHP version 5 * @@ -28,16 +24,19 @@ * @author Oliver Goldschmidt <o.goldschmidt@tuhh.de> * @author Magdalena Roos <roos@gbv.de> * @author Till Kinstler <kinstler@gbv.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/building_an_ils_driver Wiki */ namespace finc\ILS\Driver; -use DOMDocument, VuFind\Exception\ILS as ILSException; -//use VuFind\ILS\Driver\DAIA as DAIA; +use VuFind\Exception\ILS as ILSException, + VuFindHttp\HttpServiceAwareInterface as HttpServiceAwareInterface, + Zend\Log\LoggerAwareInterface as LoggerAwareInterface, + Zend\Log\LoggerInterface as LoggerInterface; /** - * ILS Driver for VuFind to get information from PICA + * PAIA ILS Driver for VuFind to get patron information * * Holding information is obtained by DAIA, so it's not necessary to implement those * functions here; we just need to extend the DAIA driver. @@ -47,11 +46,27 @@ use DOMDocument, VuFind\Exception\ILS as ILSException; * @author Oliver Goldschmidt <o.goldschmidt@tuhh.de> * @author Magdalena Roos <roos@gbv.de> * @author Till Kinstler <kinstler@gbv.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/building_an_ils_driver Wiki */ -class PAIA extends DAIA +class PAIA extends DAIA implements HttpServiceAwareInterface, LoggerAwareInterface { + + /** + * Logger (or false for none) + * + * @var LoggerInterface|bool + */ + protected $logger = false; + + /** + * HTTP service + * + * @var \VuFindHttp\HttpServiceInterface + */ + protected $httpService = null; + private $_username; private $_password; @@ -62,6 +77,8 @@ class PAIA extends DAIA * Constructor * * @access public + * @return void + * @throws ILSException */ public function init() { @@ -183,10 +200,13 @@ class PAIA extends DAIA */ public function getMyTransactions($patron) { - $loans_response = $this->_getAsArray('/core/'.$patron['cat_username'].'/items'); + $loans_response = $this->_getAsArray( + '/core/'.$patron['cat_username'].'/items' + ); $holds = count($loans_response['doc']); for ($i = 0; $i < $holds; $i++) { - if ($loans_response['doc'][$i]['status'] == '3') { //status: held (the document is on loan by the patron) + if ($loans_response['doc'][$i]['status'] == '3') { + // status: held (the document is on loan by the patron) // TODO: set renewable dynamically (not yet supported by PAIA) $renewable = true; $renew_details = $loans_response['doc'][$i]['item']; @@ -198,13 +218,17 @@ class PAIA extends DAIA // hook for retrieving alternative ItemId in case PAIA does not // the needed id - $alternativeItemId = $this->_getAlternativeItemId($loans_response['doc'][$i]['item']); + $alternativeItemId = $this->_getAlternativeItemId( + $loans_response['doc'][$i]['item'] + ); - if ($loans_response['doc'][$i]['status'] == '4') { //status: provided (the document is ready to be used by the patron) + if ($loans_response['doc'][$i]['status'] == '4') { + // status: provided (the document is ready to be used by the + // patron) $message = "hold_available"; } - $transList[] = array( + $transList[] = [ 'id' => $alternativeItemId ? $alternativeItemId : $loans_response['doc'][$i]['item'], 'duedate' => $loans_response['doc'][$i]['endtime'], 'dueTime' => null, @@ -227,7 +251,7 @@ class PAIA extends DAIA 'upc' => null, 'callnumber' => $loans_response['doc'][$i]['label'], //non-standard 'borrowingLocation' => $loans_response['doc'][$i]['storage'], - ); + ]; } } return $transList; @@ -256,48 +280,50 @@ class PAIA extends DAIA public function renewMyItems($details) { $it = $details['details']; - $items = array(); + $items = []; foreach ($it as $item) { - $items[] = array('item' => stripslashes($item)); + $items[] = ['item' => stripslashes($item)]; } $patron = $details['patron']; - $post_data = array("doc" => $items); - $array_response = $this->_postAsArray('/core/'.$patron['cat_username'].'/renew', $post_data); + $post_data = ["doc" => $items]; + $array_response = $this->_postAsArray( + '/core/'.$patron['cat_username'].'/renew', $post_data + ); - $details = array(); + $details = []; if (array_key_exists('error', $array_response)) { - $details[] = array('success' => false, 'sysMessage' => $array_response['error_description']); - } - else { + $details[] = [ + 'success' => false, + 'sysMessage' => $array_response['error_description'] + ]; + } else { $elements = $array_response['doc']; foreach ($elements as $element) { $item_id = $element['item']; if (array_key_exists('error', $element)) { - $details[$item_id] = array( + $details[$item_id] = [ 'success' => false, 'sysMessage' => $element['error'] - ); - } - elseif ($element['status'] == '3') { - $details[$item_id] = array( + ]; + } elseif ($element['status'] == '3') { + $details[$item_id] = [ 'success' => true, 'new_date' => $element['endtime'], 'item_id' => 0, 'sysMessage' => 'Successfully renewed' - ); - } - else { - $details[$item_id] = array( + ]; + } else { + $details[$item_id] = [ 'success' => false, 'new_date' => $element['endtime'], 'item_id' => 0, 'sysMessage' => 'Request rejected' - ); + ]; } } } - $returnArray = array('blocks' => false, 'details' => $details); + $returnArray = ['blocks' => false, 'details' => $details]; return $returnArray; } @@ -306,80 +332,82 @@ class PAIA extends DAIA * each hold item. (optional, but required if you implement the * renewMyItems method) Not supported prior to VuFind 1.2 * - * @param $checkOutDetails - One of the individual item arrays returned by - * the getMyTransactions method - * @return string - A string to use as the input form value for renewing - * each item; you can pass any data that is needed by your - * ILS to identify the transaction to renew – the output - * of this method will be used as part of the input to the - * renewMyItems method. + * @param array $checkOutDetails One of the individual item arrays returned by + * the getMyTransactions method + * + * @return string A string to use as the input form value for renewing + * each item; you can pass any data that is needed by your + * ILS to identify the transaction to renew – the output + * of this method will be used as part of the input to the + * renewMyItems method. */ - public function getRenewDetails($checkOutDetails) { + public function getRenewDetails($checkOutDetails) + { return($checkOutDetails['renew_details']); } /** * This method cancels a list of holds for a specific patron. * - * @param array $cancelDetails - An associative array with two keys: - * patron - array returned by the driver's patronLogin method - * details - an array of strings returned by the driver's - * getCancelHoldDetails method + * @param array $cancelDetails An associative array with two keys: + * patron array returned by the driver's patronLogin method + * details an array of strings returned by the driver's + * getCancelHoldDetails method * - * @return array - Associative array containing: - * count – The number of items successfully cancelled - * items – Associative array where key matches one of the item_id + * @return array Associative array containing: + * count The number of items successfully cancelled + * items Associative array where key matches one of the item_id * values returned by getMyHolds and the value is an * associative array with these keys: - * success – Boolean true or false - * status – A status message from the language file - * (required – VuFind-specific message, - * subject to translation) - * sysMessage - A system supplied failure message + * success Boolean true or false + * status A status message from the language file + * (required – VuFind-specific message, + * subject to translation) + * sysMessage A system supplied failure message */ public function cancelHolds($cancelDetails) { $it = $cancelDetails['details']; - $items = array(); + $items = []; foreach ($it as $item) { - $items[] = array('item' => stripslashes($item)); + $items[] = ['item' => stripslashes($item)]; } $patron = $cancelDetails['patron']; - $post_data = array("doc" => $items); + $post_data = ["doc" => $items]; - $array_response = $this->_postAsArray('/core/'.$patron['cat_username'].'/cancel', $post_data); - $details = array(); + $array_response = $this->_postAsArray( + '/core/'.$patron['cat_username'].'/cancel', $post_data + ); + $details = []; if (array_key_exists('error', $array_response)) { - $details[] = array( + $details[] = [ 'success' => false, 'status' => $array_response['error_description'], 'sysMessage' => $array_response['error'] - ); - } - else { + ]; + } else { $count = 0; $elements = $array_response['doc']; foreach ($elements as $element) { $item_id = $element['item']; if ($element['error']) { - $details[$item_id] = array( + $details[$item_id] = [ 'success' => false, 'status' => $element['error'], 'sysMessage' => 'Cancel request rejected' - ); - } - else { - $details[$item_id] = array( + ]; + } else { + $details[$item_id] = [ 'success' => true, 'status' => 'Success', 'sysMessage' => 'Successfully cancelled' - ); + ]; $count++; } } } - $returnArray = array('count' => $count, 'items' => $details); + $returnArray = ['count' => $count, 'items' => $details]; return $returnArray; } @@ -389,15 +417,17 @@ class PAIA extends DAIA * cancelling each hold item. (optional, but required if you * implement cancelHolds). Not supported prior to VuFind 1.2 * - * @param $checkOutDetails - One of the individual item arrays returned by - * the getMyHolds method - * @return string - A string to use as the input form value for cancelling - * each hold item; you can pass any data that is needed - * by your ILS to identify the hold – the output of this - * method will be used as part of the input to the - * cancelHolds method. + * @param array $checkOutDetails One of the individual item arrays returned by + * the getMyHolds method + * + * @return string A string to use as the input form value for cancelling + * each hold item; you can pass any data that is needed + * by your ILS to identify the hold – the output of this + * method will be used as part of the input to the + * cancelHolds method. */ - public function getCancelHoldDetails($checkOutDetails) { + public function getCancelHoldDetails($checkOutDetails) + { return($checkOutDetails['cancel_details']); } @@ -414,12 +444,14 @@ class PAIA extends DAIA */ public function getMyFines($patron) { - $fees_response = $this->_getAsArray('/core/'.$patron['cat_username'].'/fees'); + $fees_response = $this->_getAsArray( + '/core/'.$patron['cat_username'].'/fees' + ); - $fineList = array(); + $fineList = []; foreach ($fees_response['fee'] as $fine) { $alternativeItemId = $this->_getAlternativeItemId($fine['item']); - $fineList[] = array( + $fineList[] = [ "id" => $alternativeItemId ? $alternativeItemId : $fine['item'], "amount" => $fine['amount'], "checkout" => "", @@ -428,11 +460,11 @@ class PAIA extends DAIA "duedate" => "", "fine" => $fine['feetype'], //"balance" => "", - ); + ]; } - $fineList[] = array( + $fineList[] = [ "balance" => $fees_response['amount'] - ); + ]; return $fineList; } @@ -450,22 +482,28 @@ class PAIA extends DAIA */ public function getMyHolds($patron) { - $loans_response = $this->_getAsArray('/core/'.$patron['cat_username'].'/items'); + $loans_response = $this->_getAsArray( + '/core/'.$patron['cat_username'].'/items' + ); $holds = count($loans_response['doc']); for ($i = 0; $i < $holds; $i++) { // TODO: get date of creation from a reservation // this is not yet supported by PAIA - if ($loans_response['doc'][$i]['status'] == '1' || $loans_response['doc'][$i]['status'] == '2') { - // get PPN from PICA catalog since it is not part of PAIA - $alternativeItemId = $this->_getAlternativeItemId($loans_response['doc'][$i]['label']); + if ($loans_response['doc'][$i]['status'] == '1' + || $loans_response['doc'][$i]['status'] == '2' + ) { + $alternativeItemId = $this->_getAlternativeItemId( + $loans_response['doc'][$i]['label'] + ); $cancel_details = false; if ($loans_response['doc'][$i]['cancancel'] == 1) { $cancel_details = $loans_response['doc'][$i]['item']; } - // As long as PAIA-Server does not set cancancel, always populate $cancel_details + // As long as PAIA-Server does not set cancancel, always populate + // $cancel_details $cancel_details = $loans_response['doc'][$i]['item']; - $transList[] = array( + $transList[] = [ 'type' => $loans_response['doc'][$i]['status'], 'id' => $alternativeItemId ? $alternativeItemId : $loans_response['doc'][$i]['item'], 'location' => $loans_response['doc'][$i]['storage'], @@ -485,7 +523,7 @@ class PAIA extends DAIA 'message' => $loans_response['doc'][$i]['label'], 'callnumber' => $loans_response['doc'][$i]['label'], 'cancel_details' => $cancel_details, - ); + ]; } } return $transList; @@ -510,33 +548,33 @@ class PAIA extends DAIA { $item = $holdDetails['item_id']; - $items = array(); - $items[] = array('item' => stripslashes($item)); + $items = []; + $items[] = ['item' => stripslashes($item)]; $patron = $holdDetails['patron']; - $post_data = array("doc" => $items); - $array_response = $this->_postAsArray('/core/'.$patron['cat_username'].'/request', $post_data); - $details = array(); + $post_data = ["doc" => $items]; + $array_response = $this->_postAsArray( + '/core/'.$patron['cat_username'].'/request', $post_data + ); + $details = []; if (array_key_exists('error', $array_response)) { - $details = array( + $details = [ 'success' => false, 'sysMessage' => $array_response['error_description'] - ); - } - else { + ]; + } else { $elements = $array_response['doc']; foreach ($elements as $element) { if (array_key_exists('error', $element)) { - $details = array( + $details = [ 'success' => false, 'sysMessage' => $element['error'] - ); - } - else { - $details = array( + ]; + } else { + $details = [ 'success' => true, 'sysMessage' => 'Successfully requested' - ); + ]; } } } @@ -556,51 +594,50 @@ class PAIA extends DAIA // If you do not want or support such limits, just return an empty // array here and the limit control on the new item search screen // will disappear. - return array(); + return []; } /** * Public Function which changes the password in the library system * (not supported prior to VuFind 2.4) * - * @param string $function The name of the feature to be checked + * @param array $patron Array with patron information. + * @param string $oldPassword Old Password. + * @param string $newPassword New Password. * * @return array An array with patron information. - * @access public */ public function changePassword($patron, $oldPassword, $newPassword) { - $post_data = array( + $post_data = [ "patron" => $patron['username'], "username" => $patron['firstname']." ".$patron['lastname'], "old_password" => $oldPassword, - "new_password" => $newPassword); + "new_password" => $newPassword]; $array_response = $this->_postAsArray('/auth/change', $post_data); - $details = array(); + $details = []; if (array_key_exists('error', $array_response)) { - $details = array( + $details = [ 'success' => false, 'status' => $array_response['error'], 'sysMessage' => $array_response['error_description'] - ); - } - else { + ]; + } else { $element = $array_response['patron']; if (array_key_exists('error', $element)) { - $details = array( + $details = [ 'success' => false, 'status' => 'Failure changing password', 'sysMessage' => $element['error'] - ); - } - else { - $details = array( + ]; + } else { + $details = [ 'success' => true, 'status' => 'Successfully changed' - ); + ]; } } return $details; @@ -613,7 +650,7 @@ class PAIA extends DAIA * holds / recall retrieval * * @param array $patron Patron information returned by the patronLogin - * method. + * method. * @param array $holdDetails Optional array, only passed in when getting a list * in the context of placing a hold; contains most of the same values passed to * placeHold, minus the patron data. May be used to limit the pickup options @@ -628,7 +665,7 @@ class PAIA extends DAIA public function getPickUpLocations($patron = false, $holdDetails = null) { // How to get valid PickupLocations for a PICA LBS? - return array(); + return []; } /** @@ -649,51 +686,98 @@ class PAIA extends DAIA return false; } - // private functions to connect to PAIA /** - * post something to a foreign host + * Post something to a foreign host * * @param string $file POST target URL * @param string $data_to_send POST data + * @param string $access_token PAIA access token for current session * - * @return string POST response + * @return string POST response * @access private + * @throws \Exception */ private function _postit($file, $data_to_send, $access_token = null) { // json-encoding $postData = stripslashes(json_encode($data_to_send)); - $http = curl_init(); - curl_setopt($http, CURLOPT_URL, $this->paiaURL . $file); - curl_setopt($http, CURLOPT_POST, true); - curl_setopt($http, CURLOPT_POSTFIELDS, $postData); + $http_headers = []; if (isset($access_token)) { - curl_setopt($http, CURLOPT_HTTPHEADER, array('Content-type: application/json; charset=UTF-8', 'Authorization: Bearer ' .$access_token)); - } else { - curl_setopt($http, CURLOPT_HTTPHEADER, array('Content-type: application/json; charset=UTF-8')); + $http_headers['Authorization'] = 'Bearer ' .$access_token; } - curl_setopt($http, CURLOPT_RETURNTRANSFER, true); - $data = curl_exec($http); - curl_close($http); + try { + $result = $this->httpService->post( + $this->paiaURL . $file, + $postData, + 'application/json; charset=UTF-8', + null, + $http_headers + ); + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } - return $data; + if (!$result->isSuccess()) { + $this->debug( + 'HTTP status ' . $result->getStatusCode() . + ' received' + ); + + // return false as request failed + return false; + } + return ($result->getBody()); } + /** + * GET data from foreign host + * + * @param string $file GET target URL + * @param string $access_token PAIA access token for current session + * + * @return bool|string + * @throws ILSException + */ private function _getit($file, $access_token) { - $http = curl_init(); - curl_setopt($http, CURLOPT_URL, $this->paiaURL . $file); - curl_setopt($http, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' .$access_token, 'Content-type: application/json; charset=UTF-8')); - curl_setopt($http, CURLOPT_RETURNTRANSFER, true); - $data = curl_exec($http); - curl_close($http); - return $data; + $http_headers = [ + 'Authorization' => 'Bearer ' .$access_token, + 'Content-type' => 'application/json; charset=UTF-8', + ]; + + try { + $result = $this->httpService->get( + $this->paiaURL . $file, + [], null, $http_headers + ); + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } + + if (!$result->isSuccess()) { + $this->debug( + 'HTTP status ' . $result->getStatusCode() . + ' received' + ); + + // return false as request failed + return false; + } + return ($result->getBody()); } + /** + * Retrieve file at given URL and return it as json_decoded array + * + * @param string $file GET target URL + * + * @return array|mixed + * @throws ILSException + */ private function _getAsArray($file) { $pure_response = $this->_getit($file, $_SESSION['paiaToken']); @@ -701,16 +785,26 @@ class PAIA extends DAIA $json_response = substr($pure_response, $json_start); $loans_response = json_decode($json_response, true); - // if the login auth token is invalid, renew it (this is possible unless the session is expired) + // if the login auth token is invalid, renew it (this is possible unless the + // session is expired) if (isset($loans_response['error']) && $loans_response['code'] == '401') { //TODO: handling of expired auth token $this->debug("Auth token invalid - returning empty array"); - return array(); + return []; } return $loans_response; } + /** + * Post something at given URL and return it as json_decoded array + * + * @param string $file POST target URL + * @param array $data POST data + * + * @return array|mixed + * @throws ILSException + */ private function _postAsArray($file, $data) { $pure_response = $this->_postit($file, $data, $_SESSION['paiaToken']); @@ -718,32 +812,36 @@ class PAIA extends DAIA $json_response = substr($pure_response, $json_start); $loans_response = json_decode($json_response, true); - // if the login auth token is invalid, renew it (this is possible unless the session is expired) + // if the login auth token is invalid, renew it (this is possible unless the + // session is expired) if ($loans_response['error'] && $loans_response['code'] == '401') { //TODO: handling of expired auth token $this->debug("Auth token invalid - returning empty array"); - return array(); + return []; } return $loans_response; } /** - * private authentication function - * use PAIA for authentication + * Private authentication function - use PAIA for authentication + * + * @param string $username Username + * @param string $password Password * * @return mixed Associative array of patron info on successful login, * null on unsuccessful login, PEAR_Error on error. * @access private + * @throws ILSException */ private function _paiaLogin($username, $password) { - $post_data = array( - "username" => $username, - "password" => $password, - "grant_type" => "password", - "scope" => "read_patron read_fees read_items write_items change_password" - ); + $post_data = [ + "username" => $username, + "password" => $password, + "grant_type" => "password", + "scope" => "read_patron read_fees read_items write_items change_password" + ]; $login_response = $this->_postit('/auth/login', $post_data); $json_start = strpos($login_response, '{'); @@ -757,22 +855,25 @@ class PAIA extends DAIA $patron['cat_username'] = $array_response['patron']; $patron['cat_password'] = $password; return $patron; + } else { + throw new ILSException( + 'Login credentials accepted, but got no patron ID?!?' + ); } - else { - throw new ILSException('Login credentials accepted, but got no patron ID?!?'); - } - } - else if (array_key_exists('error', $array_response)) { - throw new ILSException($array_response['error'].": ".$array_response['error_description']); + } else if (array_key_exists('error', $array_response)) { + throw new ILSException( + $array_response['error'].": ".$array_response['error_description'] + ); + } else { + throw new ILSException('Unknown error! Access denied.'); } - else throw new ILSException('Unknown error! Access denied.'); } /** * Support method for _paiaLogin() -- load user details into session and return * array of basic user data. * - * @param array $patron patron ID + * @param array $patron patron ID * * @return array * @access private @@ -784,19 +885,30 @@ class PAIA extends DAIA $json_response = substr($pure_response, $json_start); $user_response = json_decode($json_response, true); - // if the login auth token is invalid, renew it (this is possible unless the session is expired) + // if the login auth token is invalid, renew it (this is possible unless the + // session is expired) if (isset($user_response['error']) && $user_response['code'] == '401') { //TODO: handling of expired auth token $this->debug("Auth token invalid - returning empty userdetails"); - return array(); + return []; } $username = $user_response['name']; - $nameArr = explode(',', $username); - $firstname = $nameArr[1]; - $lastname = $nameArr[0]; + if (count(explode(',', $username)) == 2) { + $nameArr = explode(',', $username); + $firstname = $nameArr[1]; + $lastname = $nameArr[0]; + } else { + $nameArr = explode(' ', $username); + $firstname = $nameArr[0]; + $lastname = ''; + array_shift($nameArr); + foreach ($nameArr as $value) { + $lastname .= $value; + } + } - $user = array(); + $user = []; $user['id'] = $patron; $user['firstname'] = $firstname; $user['lastname'] = $lastname; @@ -808,15 +920,16 @@ class PAIA extends DAIA } /** - * Support method to retrieve needed ItemId in case PAIA-resposne does not + * Support method to retrieve needed ItemId in case PAIA-response does not * contain it * - * @param string $id itemId + * @param string $id itemId * * @return string $id * @access private */ - private function _getAlternativeItemId($id) { + private function _getAlternativeItemId($id) + { return $id; } @@ -838,5 +951,43 @@ class PAIA extends DAIA } return $functionConfig; } -} -?> \ No newline at end of file + + /** + * Set the logger + * + * @param LoggerInterface $logger Logger to use. + * + * @return void + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Log a debug message. + * + * @param string $msg Message to log. + * + * @return void + */ + protected function debug($msg) + { + if ($this->logger) { + $this->logger->debug(get_class($this) . ": $msg"); + } + } + + /** + * Set the HTTP service to be used for HTTP requests. + * + * @param HttpServiceInterface $service HTTP service + * + * @return void + */ + public function setHttpService(\VuFindHttp\HttpServiceInterface $service) + { + $this->httpService = $service; + } + +} \ No newline at end of file diff --git a/module/finc/src/finc/RecordDriver/Factory.php b/module/finc/src/finc/RecordDriver/Factory.php index 5841f9e27d3e6ea2b166c9ab76b6f7a5d82a29be..1336afd0ad01d97550033354bdfe659a18edd675 100644 --- a/module/finc/src/finc/RecordDriver/Factory.php +++ b/module/finc/src/finc/RecordDriver/Factory.php @@ -38,7 +38,7 @@ use Zend\ServiceManager\ServiceManager; * @link http://vufind.org/wiki/vufind2 * @codeCoverageIgnore */ -class Factory extends \VuFind\RecordDriver\Factory +class Factory { /** @@ -62,4 +62,42 @@ class Factory extends \VuFind\RecordDriver\Factory ); return $driver; } + + /** + * Factory for SolrAI record driver. + * + * @param ServiceManager $sm Service manager. + * + * @return SolrAI + */ + public static function getSolrAI(ServiceManager $sm) + { + return new SolrAI( + $sm->getServiceLocator()->get('VuFind\Config')->get('config'), + $sm->getServiceLocator()->get('VuFind\Config')->get('SolrAI'), + null + ); + } + + /** + * Factory for SolrMarcRemoteFinc record driver. + * + * @param ServiceManager $sm Service manager. + * + * @return SolrMarc + */ + public static function getSolrMarcRemoteFinc(ServiceManager $sm) + { + $driver = new SolrMarcRemoteFinc( + $sm->getServiceLocator()->get('VuFind\Config')->get('config'), + $sm->getServiceLocator()->get('VuFind\Config')->get('SolrMarcRemoteFinc'), + $sm->getServiceLocator()->get('VuFind\Config')->get('searches') + ); + $driver->attachILS( + $sm->getServiceLocator()->get('VuFind\ILSConnection'), + $sm->getServiceLocator()->get('VuFind\ILSHoldLogic'), + $sm->getServiceLocator()->get('VuFind\ILSTitleHoldLogic') + ); + return $driver; + } } diff --git a/module/finc/src/finc/RecordDriver/SolrAI.php b/module/finc/src/finc/RecordDriver/SolrAI.php new file mode 100644 index 0000000000000000000000000000000000000000..193fb2414d4e1f584e101d662d44afadcac389e6 --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrAI.php @@ -0,0 +1,844 @@ +<?php +/** + * Recorddriver for Solr records from the aggregated index of Leipzig University + * Library + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +namespace finc\RecordDriver; + +/** + * Recorddriver for Solr records from the aggregated index of Leipzig University + * Library + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +class SolrAI extends SolrDefault +{ + /** + * Logger (or false for none) + * + * @var LoggerInterface|bool + */ + protected $logger = false; + + /** + * AI record + * + * @var array + */ + protected $aiRecord; + + /** + * holds config.ini data + * + * @var array + */ + protected $mainConfig; + + + /** + * gets the description of the record + * + * @return string description + */ + public function getDescriptions() + { + return $this->_getAIFullrecordArrayValue('abstract'); + } + + /** + * gets the edition key from the record + * + * @return string edition + */ + public function getEdition() + { + return $this->_getAIFullrecordStringValue('rft.edition'); + } + + /** + * gets the publication date of the record + * + * @return string publication date + */ + public function getDate() + { + return isset($this->fields['publishDateSort']) ? + $this->fields['publishDateSort'] : ''; + } + + /** + * gets an array of issues from record + * + * @return array of issues + */ + public function getIssues() + { + return $this->_getAIFullrecordStringValue('rft.issue'); + } + + + /** + * Get an array of publication detail of first entry combined from + * place, publisher and data. + * + * @return array + * @access protected + */ + /*public function getFirstPublicationDetails() + { + $place = $this->getPlacesOfPublication(); + $date = $this->getPublicationDates(); + $array = array(); + if (isset($this->fields['format']) && is_array($this->fields['format'])) { + $array['issue'] = (isset($this->fields['hierarchy_parent_title']) ? + 'In: '.$this->fields['hierarchy_parent_title'][0] : ''); + $array['date'] = ((is_array($date) && (count($date) > 0) ? + $date[0] : '')); + switch ((count($this->fields['format']) > 0) ? + $this->fields['format'][0] : '') { + case 'eBook': + $array['place'] = ((is_array($place) && (count($place) > 0) ? + $place[0] : '')); + break; + case 'ElectronicArticle': + $array['place'] = ''; + break; + default: + break; + } + } + + return $array; + }*/ + + /** + * Has FirstPublicationsDetails a Date in it + * + * @return boolean + * @access protected + */ + protected function getIsPublicationDetailsDate() + { + return true; + } + + /** + * Get the main author of the record. + * + * @return string + * @access protected + */ + public function getPrimaryAuthor() + { + return null; + } + + /** + * Get additional entries for personal names. + * + * @return array + * @access protected + * @link http://www.loc.gov/marc/bibliographic/bd700.html + */ + protected function getAdditionalAuthors() + { + if (isset($this->record['authors']) + && is_array($this->record['authors']) + && (count($this->record['authors']) > 0) + ) { + $retval = array(); + $i = 0; + $authors = $this->record['authors']; + foreach ($authors as $value) { + $retval[$i]['name'] = (isset($value['rft.aulast']) ? + $value['rft.aulast'].', ' : '') + .(isset($value['rft.aufirst']) ? $value['rft.aufirst'] : ''); + $i++; + } + return $retval; + } + return array(); + } + + /** + * Get the title of the item that contains this record (i.e. MARC 773s of a + * journal). + * + * @return string + */ + public function getContainerTitle() + { + return (isset($this->fields['hierarchy_parent_title']) ? + $this->fields['hierarchy_parent_title'][0] : ''); + } + + /** + * Get an array of publication detail lines combining information from + * getPublicationDates(), getPublishers() and getPlacesOfPublication(). + * + * @return array + * @access protected + */ + public function getPublicationDetails() + { + $names = $this->_getAIFullrecordArrayValue('rft.pub'); + $i = 0; + $retval = array(); + while (isset($names[$i])) { + // Build objects to represent each set of data; these will + // transform seamlessly into strings in the view layer. + $retval[] = new \VuFind\RecordDriver\Response\PublicationDetails( + null, + isset($names[$i]) ? $names[$i] : '', + null + ); + $i++; + } + return $retval; + } + + /** + * Get the publication dates of the record. See also getDateSpan(). + * + * @return array + */ + public function getPublicationDates() + { + return isset($this->fields['publishDateSort']) ? + $this->fields['publishDateSort'] : ''; + } + + /** + * Returns an array with the necessary information to create a detailed + * "Published in" line in RecordDriver core.phtml + * + * @return array + */ + public function getAIDataIn() + { + return array( + 'jtitle' => $this->getJTitle(), + 'volume' => $this->getVolume(), + 'date' => $this->getDate(), + 'issue' => $this->getIssues(), + 'issns' => $this->getISSNs(), + 'pages' => $this->getPages() + ); + } + + /** + * gets an array of series from record + * + * @return array of series + */ + public function getSeries() + { + return $this->_getAIFullrecordArrayValue('rft.series'); + } + + /** + * gets an array of volumes from record + * + * @return array of volumes + */ + public function getVolume() + { + return $this->_getAIFullrecordStringValue('rft.volume'); + } + + /** + * Get the ISSN from a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + public function getISSNs() + { + return $this->_getAIFullrecordArrayValue('rft.issn'); + } + + /** + * Get the eISSN from a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + public function getEISSNs() + { + return $this->_getAIFullrecordArrayValue('rft.eissn'); + } + + /** + * Get an array of all ISSNs associated with the record (may be empty). + * Can be the main ISSN and the parent ISSNs. + * + * @return array + * @access protected + */ + public function getISBNs() + { + return $this->_getAIFullrecordArrayValue('rft.isbn'); + } + + /** + * gets pages as 'start - end' if both exist + * + * @return string pages + */ + public function getPages() + { + if ($this->hasStartpages() + && $this->hasEndpages() + ) { + return sprintf( + '%s - %s', + $this->aiRecord['rft.spage'], + $this->aiRecord['rft.epage'] + ); + } else if ($this->hasStartpages()) { + return $this->aiRecord['rft.spage'][0]; + } else if ($this->hasEndpages()) { + return $this->aiRecord['rft.epage'][0]; + } + } + + /** + * Return the jtitle field of ai records + * + * @return array Return jtitle fields. + * @access public + */ + public function getJTitle () + { + return $this->_getAIFullrecordStringValue('rft.jtitle'); + } + + /** + * Return the jtitle field of ai records + * + * @return array Return jtitle fields. + * @access public + */ + public function getATitle () + { + return $this->_getAIFullrecordStringValue('rft.atitle'); + } + + /** + * Return the jtitle field of ai records + * + * @return array Return jtitle fields. + * @access public + */ + public function getBTitle () + { + return $this->_getAIFullrecordStringValue('rft.btitle'); + } + + /** + * Get the OpenURL parameters to represent this record (useful for the + * title attribute of a COinS span tag). + * + * @return string OpenURL parameters. + */ + public function getOpenURL() { + // Set up parameters based on the format of the record: + switch ($this->aiRecord['rft.genre']) { + case 'book': + $params = $this->getBookOpenURLParams(); + break; + case 'article': + $params = $this->getArticleOpenURLParams(); + break; + case 'journal': + $params = $this->getJournalOpenURLParams(); + break; + default: + $format = $this->getFormats(); + $params = $this->getUnknownFormatOpenURLParams($format); + break; + } + + // Assemble the URL: + return http_build_query($params); + } + + /** + * Get the COinS identifier. + * + * @return string + */ + protected function getCoinsID() + { + // Get the COinS ID -- it should be in the OpenURL section of config.ini, + // but we'll also check the COinS section for compatibility with legacy + // configurations (this moved between the RC2 and 1.0 releases). + if (isset($this->mainConfig->OpenURL->rfr_id) + && !empty($this->mainConfig->OpenURL->rfr_id) + ) { + return $this->mainConfig->OpenURL->rfr_id; + } + return 'vufind.svn.sourceforge.net'; + } + + /** + * Get default OpenURL parameters. + * + * @return array + */ + protected function getDefaultOpenURLParams() + { + // Start an array of OpenURL parameters: + return array( + 'ctx_ver' => 'Z39.88-2004', + 'ctx_enc' => 'info:ofi/enc:UTF-8', + 'rfr_id' => 'info:sid/' . $this->getCoinsID() . ':generator' + ); + } + + /** + * Get OpenURL parameters for an article. + * + * @return array + */ + protected function getArticleOpenURLParams() + { + $params = $this->getDefaultOpenURLParams(); + // unset default title -- we only want jtitle/atitle here: + //$params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:journal'; + $params['genre'] = 'article'; + if (isset($this->aiRecord['finc.record_id'])) { + $params['rft_id'] = $this->aiRecord['finc.record_id']; + } + if (isset($this->aiRecord['rft.issn'])) { + foreach ($this->aiRecord['rft.issn'] as $issn) { + $params['issn'] = $issn; + } + } + // an article may have also an ISBN: + if (isset($this->aiRecord['rft.isbn'])) { + $params['isbn'] = $this->aiRecord['rft.isbn']; + } + if (isset($this->aiRecord['rft.ssn'])) { + $params['ssn'] = $this->aiRecord['rft.ssn']; + } + if (isset($this->aiRecord['rft.volume'])) { + $params['volume'] = $this->aiRecord['rft.volume']; + } + if (isset($this->aiRecord['rft.issue'])) { + $params['issue'] = $this->aiRecord['rft.issue']; + } + if (isset($this->aiRecord['rft.spage'])) { + $params['spage'] = $this->aiRecord['rft.spage']; + } + if (isset($this->aiRecord['rft.epage'])) { + $params['epage'] = $this->aiRecord['rft.epage']; + } + if (isset($this->aiRecord['rft.pages'])) { + $params['pages'] = $this->aiRecord['rft.pages']; + } + if (isset($this->aiRecord['rft.coden'])) { + $params['coden'] = $this->aiRecord['rft.coden']; + } + if (isset($this->aiRecord['rft.artnum'])) { + $params['artnum'] = $this->aiRecord['rft.artnum']; + } + if (isset($this->aiRecord['rft.sici'])) { + $params['sici'] = $this->aiRecord['rft.sici']; + } + if (isset($this->aiRecord['rft.chron'])) { + $params['chron'] = $this->aiRecord['rft.chron']; + } + if (isset($this->aiRecord['rft.quarter'])) { + $params['quarter'] = $this->aiRecord['rft.quarter']; + } + if (isset($this->aiRecord['rft.part'])) { + $params['part'] = $this->aiRecord['rft.part']; + } + if (isset($this->aiRecord['rft.jtitle'])) { + $params['jtitle'] = $this->getJTitle(); + } + if (isset($this->aiRecord['rft.atitle'])) { + $params['atitle'] = $this->getATitle(); + } + if (isset($this->aiRecord['rft.stitle'])) { + $params['stitle'] = $this->aiRecord['rft.stitle']; + } + if (isset($this->aiRecord['authors'])) { + foreach ($this->aiRecord['authors'] as $author) { + if (isset($author['rft.au'])) { + $params['au'] = $author['rft.au']; + } + if (isset($author['rft.aulast'])) { + $params['aulast'] = $author['rft.aulast']; + } + if (isset($author['rft.aucorp'])) { + $params['aucorp'] = $author['rft.aucorp']; + } + if (isset($author['rft.auinitm'])) { + $params['auinitm'] = $author['rft.auinitm']; + } + if (isset($author['rft.aufirst'])) { + $params['aufirst'] = $author['rft.aufirst']; + } + if (isset($author['rft.auinit'])) { + $params['auinit'] = $author['rft.auinit']; + } + if (isset($author['rft.auinit1'])) { + $params['auinit1'] = $author['rft.auinit1']; + } + if (isset($author['rft.ausuffix'])) { + $params['ausuffix'] = $author['rft.ausuffix']; + } + } + } + + if (isset($this->aiRecord['rft.format'])) { + $params['format'] = $this->aiRecord['rft.format']; + } + if (isset($this->aiRecord['doi'])) { + $params['rft_id'] = 'info:doi/'.$this->aiRecord['doi']; + } + if (isset($this->aiRecord['languages'])) { + $params['rft.language'] = $this->aiRecord['languages']; + } + if (isset($this->aiRecord['rft.date'])) { + $params['rft.date'] = $this->aiRecord['rft.date']; + } + return $params; + } + + /** + * Get OpenURL parameters for a book. + * + * @return array + */ + protected function getBookOpenURLParams() + { + $params = $this->getDefaultOpenURLParams(); + $params['rft_val_fmt'] = 'info:ofi/fmt:kev:mtx:book'; + $params['genre'] = 'book'; + if (isset($this->aiRecord['rft.atitle'])) { + $params['atitle'] = $this->getATitle(); + } + if (isset($this->aiRecord['rft.btitle'])) { + $params['rft.btitle'] = $this->getBTitle(); + } + if (isset($this->aiRecord['finc.record_id'])) { + $params['rft_id'] = $this->aiRecord['finc.record_id']; + } + if (isset($this->aiRecord['rft.issn'])) { + foreach ($this->aiRecord['rft.issn'] as $issn) { + $params['issn'] = $issn; + } + } + if (isset($this->aiRecord['rft.edition'])) { + $params['edition'] = $this->aiRecord['rft.edition']; + } + // an article may have also an ISBN: + if (isset($this->aiRecord['rft.isbn'])) { + $params['isbn'] = $this->aiRecord['rft.isbn']; + } + if (isset($this->aiRecord['rft.ssn'])) { + $params['ssn'] = $this->aiRecord['rft.ssn']; + } + if (isset($this->aiRecord['rft.eissn'])) { + $params['eissn'] = $this->aiRecord['rft.eissn']; + } + if (isset($this->aiRecord['rft.volume'])) { + $params['volume'] = $this->aiRecord['rft.volume']; + } + if (isset($this->aiRecord['rft.issue'])) { + $params['issue'] = $this->aiRecord['rft.issue']; + } + if (isset($this->aiRecord['rft.spage'])) { + $params['spage'] = $this->aiRecord['rft.spage']; + } + if (isset($this->aiRecord['rft.epage'])) { + $params['epage'] = $this->aiRecord['rft.epage']; + } + if (isset($this->aiRecord['rft.pages'])) { + $params['pages'] = $this->aiRecord['rft.pages']; + } + if (isset($this->aiRecord['rft.series'])) { + $params['series'] = $this->aiRecord['rft.series']; + } + if (isset($this->aiRecord['rft.tpages'])) { + $params['tpages'] = $this->aiRecord['rft.tpages']; + } + if (isset($this->aiRecord['rft.bici'])) { + $params['bici'] = $this->aiRecord['rft.bici']; + } + if (isset($this->aiRecord['authors'])) { + foreach ($this->aiRecord['authors'] as $author) { + if (isset($author['rft.au'])) { + $params['au'] = $author['rft.au']; + } + if (isset($author['rft.aulast'])) { + $params['aulast'] = $author['rft.aulast']; + } + if (isset($author['rft.aucorp'])) { + $params['aucorp'] = $author['rft.aucorp']; + } + if (isset($author['rft.auinitm'])) { + $params['auinitm'] = $author['rft.auinitm']; + } + if (isset($author['rft.aufirst'])) { + $params['aufirst'] = $author['rft.aufirst']; + } + if (isset($author['rft.auinit'])) { + $params['auinit'] = $author['rft.auinit']; + } + if (isset($author['rft.auinit1'])) { + $params['auinit1'] = $author['rft.auinit1']; + } + if (isset($author['rft.ausuffix'])) { + $params['ausuffix'] = $author['rft.ausuffix']; + } + } + } + + if (isset($this->aiRecord['rft.format'])) { + $params['format'] = $this->aiRecord['rft.format']; + } + if (isset($this->aiRecord['doi'])) { + $params['rft_id'] = 'info:doi/'.$this->aiRecord['doi']; + } + $publishers = $this->getPublishers(); + if (count($publishers) > 0) { + $params['rft.pub'] = $publishers[0]; + } + return $params; + } + + /** + * Retrieve raw data from object (primarily for use in staff view and + * autocomplete; avoid using whenever possible). + * + * @return mixed + */ + public function getRawData() + { + $tmp = array(); + $i = 0; + if (!empty($this->aiRecord)) { + foreach ($this->aiRecord as $key => $value) { + $tmp[$i]['key'] = $key; + $tmp[$i]['value'] = $value; + $i++; + } + } + return $tmp; + } + + /** + * Retrieve data from ai-blobserver + * + * @param string $id Record Id of the raw recorddata to be retrieved + * @param string $baseUrl The Ai fullrecord server url. + * + * @return mixed Raw curl request response (should be json). + * @throws Exception + */ + protected function retrieveAiFullrecord($id, $baseUrl) + { + if (!isset($id)) { + throw new Exception('no id given'); + } + + $url = sprintf($baseUrl, $id); + + $curlDefaultOptions = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => true, + CURLOPT_USERAGENT => "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" + ); + $curlOptions = array(); + $curlOptions[CURLOPT_URL] = $url; + $handle = curl_init(); + $curl_options = $curlDefaultOptions + $curlOptions; + curl_setopt_array($handle, $curl_options); + $response = curl_exec($handle); + if (false === $response) { + throw new Exception(curl_error($handle)); + } + return $response; + } + + /** + * Returns the AI fullrecord as decoded json. + * + * @param string $id Record id to be retrieved. + * + * @return array + * @throws \Exception + */ + protected function getAIJSONFullrecord($id) + { + if (!isset($this->recordConfig->General)) { + throw new \Exception('SolrAI General settings missing.'); + } + + $baseUrl = $this->recordConfig->General->baseUrl; + + if (!isset($baseUrl)) { + throw new \Exception('no ai-blobserver configurated'); + } + + $response = $this->retrieveAiFullrecord($id, $baseUrl); + + return json_decode($response, true); + } + + /** + * returns the value of a certain record key or the default value if not exists + * + * @param string $key of record array + * @param mixed $default [optional] return value + * + * @return mixed value of key + * @access private + */ + private function _getAIFullrecordStringValue($key, $default = '') + { + if (!$this->_hasAIFullrecordStringValue($key)) { + if ($this->_hasAIFullrecordArrayValue($key)) { + return implode(',', $this->aiRecord[$key]); + } + return $default; + } + + return $this->aiRecord[$key]; + } + + /** + * returns the value of a certain record key or the default value if not exists + * + * @param string $key of record array + * @param mixed $default [optional] return value + * + * @return mixed value of key + * @access private + */ + private function _getAIFullrecordArrayValue($key, $default = array()) + { + if (!$this->_hasAIFullrecordArrayValue($key)) { + return $default; + } + return $this->aiRecord[$key]; + } + + /** + * checks whether a certain array key exists and is not empty in record data array + * + * @param string $key Key to be checked. + * + * @return boolean true or false + * @access private + */ + private function _hasAIFullrecordStringValue($key) + { + if (empty($this->aiRecord)) { + $this->aiRecord = $this->getAIJSONFullrecord($this->fields['id']); + } + if (isset($this->aiRecord[$key]) + && !empty($this->aiRecord[$key]) + && !is_array($this->aiRecord[$key]) + ) { + return true; + } + + return false; + } + + + /** + * checks whether a certain array key exists, is an array and has elements in + * record data array + * + * @param string $key Key to be checked + * + * @return boolean true or false + * @access private + */ + private function _hasAIFullrecordArrayValue($key) + { + if (empty($this->aiRecord)) { + $this->aiRecord = $this->getAIJSONFullrecord($this->fields['id']); + } + if (isset($this->aiRecord[$key]) + && is_array($this->aiRecord[$key]) + && count($this->aiRecord[$key]) > 0 + ) { + return true; + } + + return false; + } + + /** + * checks whether record has start pages + * + * @return boolean + */ + public function hasStartpages() + { + if (isset($this->aiRecord['rft.spage']) + && !empty($this->aiRecord['rft.spage']) + ) { + return true; + } + + return false; + } + + /** + * checks whether record has Endpages + * + * @return boolean + */ + public function hasEndpages() + { + if (isset($this->aiRecord['rft.epage']) + && !empty($this->aiRecord['rft.epage']) + ) { + return true; + } + + return false; + } + + +} diff --git a/module/finc/src/finc/RecordDriver/SolrDefault.php b/module/finc/src/finc/RecordDriver/SolrDefault.php new file mode 100644 index 0000000000000000000000000000000000000000..672cd5abc897594ebf77808e512a10bdd603fc97 --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrDefault.php @@ -0,0 +1,501 @@ +<?php +/** + * finc specific model for Solr records based on the stock + * VuFind\RecordDriver\SolrDefault + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Frank Morgner <morgnerf@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +namespace finc\RecordDriver; + +/** + * finc specific model for Solr records based on the stock + * VuFind\RecordDriver\SolrDefault + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @author Frank Morgner <morgnerf@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +class SolrDefault extends \VuFind\RecordDriver\SolrDefault +{ + + /** + * Return the custom index field local_heading if indexExtension is set. + * If indexExtension is set local_heading_{indexExtension} is returned, + * if local_heading_{indexExtesion} is empty, + * local_heading_facet_{indexExtension} is returned. + * + * @return array Containing local_heading_[facet_]{indexExtension} fields. + * @access public + */ + public function getLocalHeading() { + + $array = array(); + + if (isset($this->mainConfig->Site->indexExtension)) { + $array = isset($this->fields['local_heading_' . ($this->mainConfig->Site->indexExtension)]) ? + $this->fields['local_heading_' . ($this->mainConfig->Site->indexExtension)] : array(); + // Use local_heading_facet field if local_heading field delivers no results at first + if (count($array) == 0) { + $array = isset($this->fields['local_heading_facet_' . ($this->mainConfig->Site->indexExtension)]) ? + $this->fields['local_heading_facet_' . ($this->mainConfig->Site->indexExtension)] : array(); + } + } + return $array; + } + + /** + * Controller to decide when local format field of a library should be + * retrieved from marc. Pass through method for PrimoCentral + * + * Public method for tuf to display format at search modul. + * + * @internal This method should be dropped or renamed (getStandardFormat()) + * as it is only a wrapper for the custom method getFormat() which + * in turn behaves as the stock getFormats() method. + * + * @deprecated No need for this wrapper in custom SolrDefault + * + * @return array + * @access public + */ + public function getLocalFormat() + { + return $this->getFormat(); + } + + /** + * Get back the standardizied format field of Solr index. + * + * @deprecated Should also be possible to be dropped (@see getLocalFormat()) + * + * @return array + */ + public function getFormat() + { + return isset($this->fields['format']) ? $this->fields['format'] : array(); + } + + /** + * Get an array of all the formats associated with the record. If indexExtension + * is set and generalFormats is disabled in config.ini return the field + * format_{indexExtension}, format otherwise. + * + * @return array Array with formats associated with the record. + */ + public function getFormats() + { + // check if general 'format' index field should be used + $isGeneralFormat = (isset($this->mainConfig->Site->generalFormats) + && true == $this->mainConfig->Site->generalFormats) + ? true : false; + // check if there's an extension defined for the library depended format + // index field + $isExtension = (isset($this->mainConfig->Site->indexExtension)) + ? true : false; + + $format = (false === $isGeneralFormat && true === $isExtension) + ? 'format_' . $this->mainConfig->Site->indexExtension : 'format' ; + + return isset($this->fields[$format]) ? $this->fields[$format] : array(); + } + + + /** + * Get the formats for displaying the icons. Renders the format information to + * a specific css class. + * There are two setups possible. If combinedIcons sets to true at config.ini + * all format values will be concatenated to one string; if it's false only + * the first vlaue will be taken. + * + * @internal Should be moved out of RecordDriver (Controller/View?) + * @todo Should be moved out of RecordDriver (Controller/View?) + * + * @return array + */ +/* protected function getFormatIcon() + { + global $configArray; + + $format = $this->getFormats(); + // check which method to build the css class is chosen + if (isset($this->mainConfig->Site->combinedIcons) && true == $this->mainConfig->Site->combinedIcons) { + // sort it + sort($format, SORT_LOCALE_STRING); + return strtolower(implode('', $format)); + // otherwise take the first format + } else { + if (isset($this->fields['multipart_set'])) { + switch ($this->fields['multipart_set']) { + case 'a': return 'sets'; + case 'b': break; //return 'part-related'; + case 'c': break; //return 'part-not-related'; + } + } + //echo "<pre>"; print_r($format); echo "</pre>"; + return $format[0]; + } + }*/ + + /** + * Get the source id of the record. + * + * @return string + * @access public + */ + public function getSourceID() + { + return isset($this->fields['source_id']) ? + $this->fields['source_id'] : ''; + } + + /** + * Get the GND of an author. + * + * @return array + */ + public function getAuthorId() + { + return isset($this->fields['author_id']) ? + $this->fields['author_id'] : array(); + } + + /** + * Combined fields of author data. + * + * @todo Check whether static call of getCorporateAuthor is necessary + * + * @return array + * @link https://intern.finc.info/issues/1866 + */ + public function getCombinedAuthors() + { + $retval = array(); + + if ($this->getPrimaryAuthor() != '') { + $original = ''; + if ($this->getPrimaryAuthorOrig() != '') { + $original = $this->getPrimaryAuthorOrig(); + } + $retval[] = ($original == '') ? $this->getPrimaryAuthor() + : $this->getPrimaryAuthor() . ' (' . $original . ')'; + } elseif ( self::getCorporateAuthor() != '' ) { + $retval[] = self::getCorporateAuthor(); + } elseif (count($this->getSecondaryAuthors()) > 0) { + foreach ($this->getSecondaryAuthors() as $val) { + $retval[] = $val; + } + } elseif (count($this->getCorporateSecondaryAuthors()) > 0) { + foreach ($this->getCorporateSecondaryAuthors() as $val) { + $retval[] = $val; + } + } + + return $retval; + } + + /** + * Get the original author of the record. + * + * @return string + */ + public function getPrimaryAuthorOrig() + { + return isset($this->fields['author_orig']) ? + $this->_filterAuthorDates($this->fields['author_orig']) : ''; + } + + /** + * Get the main author of the record. + * + * @return string + * @deprecated + */ + public function getPrimaryAuthorRaw() + { + return isset($this->fields['author']) ? + $this->_removeAuthorDates($this->fields['author']) : ''; + } + + /** + * Get the main corporate author (if any) for the record. + * + * @return string + * @access public + */ + public function getCorporateAuthor() + { + return isset($this->fields['author_corp']) ? + $this->fields['author_corp'] : ''; + } + + /** + * Get the secondary corporate authors (if any) for the record. + * + * @return array + */ + public function getCorporateSecondaryAuthors() + { + return isset($this->fields['author_corp2']) ? + $this->fields['author_corp2'] : array(); + } + + /** + * Get an array of all ISMNs associated with the record (may be empty). + * + * @return array + */ + public function getISMNs() + { + return isset($this->fields['ismn']) && is_array($this->fields['ismn']) ? + $this->fields['ismn'] : array(); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewTitles() + { + return isset($this->fields['title_new']) ? + $this->fields['title_new'] : array(); + } + + /** + * After giving a record ids as e.g. ppn of the BSZ check if a record exists. + * This method can be used to indicate a direct link than to form a general + * look for query. + * + * @todo 1. Check if this method is still needed + * @todo 2. Refactor Solr-Query to be compatible with VuFind2 + * + * @param array $rids Array of record ids to test. + * + * @return int mixed If success return at least one finc id otherwise null. + * @deprecated Not used. + */ + protected function addFincIDToRecord ( $array ) { +/* + // record ids + $rids = array(); + // return array + $retval = array(); + + // check if array contain record_ids and collect it as an array to + // use only one solr request for all + if (isset($array) && is_array($array)) { + foreach ($array as $line) { + if (isset($line['record_id'])) { + $rids[] = $line['record_id']; + } + } + } + // solr call + // call index + $index = $this->getIndexEngine(); + + // build query and accept limit of solr + $limit = $index->getBooleanClauseLimit(); + if (count($rids) > $limit) { + $rids = array_slice($rids, 0, $limit); + $retVal = array(); + } + // build the query: + if (count($rids) == 1) { + // single query: + $query = "(record_id:". $rids[0] .")"; + } elseif (count($rids) > 1) { + // multi query: + $query = 'record_id:(' . implode(' OR ', $rids) . ')'; + } else { + return $array; + } + // set hidden filter to limited the range + $this->setHiddenFilters(); + // limited search for id and record_id values only + $result = $index->search($query, null, $this->hiddenFilters, 0, 100, null, '', null, null, 'id, record_id', HTTP_REQUEST_METHOD_POST , false, false); + + // log to find test data + // temporary logger + if (isset($result['response']['numFound']) + && isset($result['response']['numFound']) != 0) { + } + // if error break down + if (PEAR::isError($result)) { + return null; + } + if (isset($result['response']['docs']) + && !empty($result['response']['docs']) + ) { + foreach( $result['response']['docs'] as $key => $doc) { + $retval[($doc['record_id'])]=$doc['id']; + } + } + // write back in array + foreach ($array as &$val) { + if (isset($val['record_id'])) { + if (isset($retval[($val['record_id'])])) { + $val['id'] = $retval[($val['record_id'])]; + } + } + } + unset($val); + //echo "<pre>"; print_r($array); echo "</pre>";*/ + return $array; + } + + /** + * Get percentage of relevance of a title. First implementaion for TUBAF. + * + * @return float Percentage of Score / Maximum Score rounded by 5. + * @link https://intern.finc.info/issues/1908 + */ + public function getRelevance() { + + $score = isset($this->fields['score']) ? $this->fields['score'] : 0; + $maxScore = isset($this->fields['score_maximum']) ? $this->fields['score_maximum'] : 0; + + if ($score == 0 || $maxScore == 0) { + return 0; + } + return round( ($score / $maxScore) , 5); + } + + /** + * Get RVK classifcation number from Solr index. + * + * @return string + */ + public function getRvk() { + return isset($this->fields['rvk_facet']) ? + $this->fields['rvk_facet'] : ''; + } + + /** + * Get special record_id of libero system. + * + * @todo refactor to a more meaningful name? + * + * @return string + */ + public function getRID() + { + return isset($this->fields['record_id']) ? + $this->fields['record_id'] : ''; + } + + /** + * Get the original title of the record. + * + * @return string + */ + public function getTitleOrig() + { + return isset($this->fields['title_orig']) ? + $this->fields['title_orig'] : ''; + } + + /** + * Get the GND of topic. + * + * @return array + */ + public function getTopicId() + { + return isset($this->fields['topic_id']) ? + $this->fields['topic_id'] : array(); + } + + /** + * Get alternatives series titles as array. + * + * @return array + */ + public function getSeriesAlternative() + { + if (isset($this->fields['series2']) && !empty($this->fields['series2'])) { + return $this->fields['series2']; + } + return array(); + } + + /** + * Get alternatives series titles as array. + * + * @return array + */ + public function getSeriesOrig() + { + if (isset($this->fields['series_orig']) && !empty($this->fields['series_orig'])) { + return $this->fields['series_orig']; + } + return array(); + } + + /** + * Filter author data for author year of birth and death + * to give a better mark up. + * + * @param string $authordata + * + * @return strings + */ + private function _filterAuthorDates( $authordata ) + { + if (preg_match('/^(\s|.*)(\d{4})\s?-?\s?(\d{4})?$/Uu',$authordata, $match)) { + return (isset($match[3])) + ? $match[1] .' *'. $match[2] . '-†'. $match[3] + : $match[1] .' *'. $match[2] . '-'; + } + return $authordata; + } + + /** + * Remove author dates if exists. + * + * @param string authordata + * + * @return strings + * @deprecated + */ + private function _removeAuthorDates( $authordata ) + { + if (preg_match('/^(\s|.*)\s(fl.\s|d.\s|ca.\s)*\s?(\d{4})\??(\sor\s\d\d?)?\s?(-|–)?\s?(ca.\s|after\s)?(\d{1,4})?(.|,)?$/Uu',$authordata, $match)) { + return (isset($match[1])) ? $match[1] : $authordata; + } + return $authordata; + } + + +} diff --git a/module/finc/src/finc/RecordDriver/SolrMarc.php b/module/finc/src/finc/RecordDriver/SolrMarc.php new file mode 100644 index 0000000000000000000000000000000000000000..747f454800d771aabefc260eacd7dbe12cbe8c2b --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrMarc.php @@ -0,0 +1,1138 @@ +<?php +/** + * Model for MARC records in Solr. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +namespace finc\RecordDriver; +use VuFind\Exception\ILS as ILSException, + VuFind\View\Helper\Root\RecordLink, + VuFind\XSLT\Processor as XSLTProcessor; + +/** + * Model for MARC records in Solr. + * + * @category VuFind2 + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +class SolrMarc extends SolrDefault +{ + /** + * MARC record + * + * @var \File_MARC_Record + */ + protected $marcRecord; + + /** + * ILS connection + * + * @var \VuFind\ILS\Connection + */ + protected $ils = null; + + /** + * Hold logic + * + * @var \VuFind\ILS\Logic\Holds + */ + protected $holdLogic; + + /** + * Title hold logic + * + * @var \VuFind\ILS\Logic\TitleHolds + */ + protected $titleHoldLogic; + + /** + * Set raw data to initialize the object. + * + * @param mixed $data Raw data representing the record; Record Model + * objects are normally constructed by Record Driver objects using data + * passed in from a Search Results object. In this case, $data is a Solr record + * array containing MARC data in the 'fullrecord' field. + * + * @return void + */ + public function setRawData($data) + { + // Call the parent's set method... + parent::setRawData($data); + + // Also process the MARC record: + $marc = trim($data['fullrecord']); + + // check if we are dealing with MARCXML + $xmlHead = '<?xml version'; + if (strcasecmp(substr($marc, 0, strlen($xmlHead)), $xmlHead) === 0) { + $marc = new \File_MARCXML($marc, \File_MARCXML::SOURCE_STRING); + } else { + // When indexing over HTTP, SolrMarc may use entities instead of certain + // control characters; we should normalize these: + $marc = str_replace( + array('#29;', '#30;', '#31;'), array("\x1D", "\x1E", "\x1F"), $marc + ); + $marc = new \File_MARC($marc, \File_MARC::SOURCE_STRING); + } + + $this->marcRecord = $marc->next(); + if (!$this->marcRecord) { + throw new \File_MARC_Exception('Cannot Process MARC Record'); + } + } + + /** + * Get access restriction notes for the record. + * + * @return array + */ + public function getAccessRestrictions() + { + return $this->getFieldArray('506'); + } + + /** + * Get all subject headings associated with this record. Each heading is + * returned as an array of chunks, increasing from least specific to most + * specific. + * + * @return array + */ + public function getAllSubjectHeadings() + { + // These are the fields that may contain subject headings: + $fields = array( + '600', '610', '611', '630', '648', '650', '651', '655', '656' + ); + + // This is all the collected data: + $retval = array(); + + // Try each MARC field one at a time: + foreach ($fields as $field) { + // Do we have any results for the current field? If not, try the next. + $results = $this->marcRecord->getFields($field); + if (!$results) { + continue; + } + + // If we got here, we found results -- let's loop through them. + foreach ($results as $result) { + // Start an array for holding the chunks of the current heading: + $current = array(); + + // Get all the chunks and collect them together: + $subfields = $result->getSubfields(); + if ($subfields) { + foreach ($subfields as $subfield) { + // Numeric subfields are for control purposes and should not + // be displayed: + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + // If we found at least one chunk, add a heading to our result: + if (!empty($current)) { + $retval[] = $current; + } + } + } + } + + // Send back everything we collected: + return $retval; + } + + /** + * Get award notes for the record. + * + * @return array + */ + public function getAwards() + { + return $this->getFieldArray('586'); + } + + /** + * Get the bibliographic level of the current record. + * + * @return string + */ + public function getBibliographicLevel() + { + $leader = $this->marcRecord->getLeader(); + $biblioLevel = strtoupper($leader[7]); + + switch ($biblioLevel) { + case 'M': // Monograph + return "Monograph"; + case 'S': // Serial + return "Serial"; + case 'A': // Monograph Part + return "MonographPart"; + case 'B': // Serial Part + return "SerialPart"; + case 'C': // Collection + return "Collection"; + case 'D': // Collection Part + return "CollectionPart"; + default: + return "Unknown"; + } + } + + /** + * Get notes on bibliography content. + * + * @return array + */ + public function getBibliographyNotes() + { + return $this->getFieldArray('504'); + } + + /** + * Get the main corporate author (if any) for the record. + * + * @return string + */ + public function getCorporateAuthor() + { + // Try 110 first -- if none found, try 710 next. + $main = $this->getFirstFieldValue('110', array('a', 'b')); + if (!empty($main)) { + return $main; + } + return $this->getFirstFieldValue('710', array('a', 'b')); + } + + /** + * Return an array of all values extracted from the specified field/subfield + * combination. If multiple subfields are specified and $concat is true, they + * will be concatenated together in the order listed -- each entry in the array + * will correspond with a single MARC field. If $concat is false, the return + * array will contain separate entries for separate subfields. + * + * @param string $field The MARC field number to read + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * + * @return array + */ + protected function getFieldArray($field, $subfields = null, $concat = true) + { + // Default to subfield a if nothing is specified. + if (!is_array($subfields)) { + $subfields = array('a'); + } + + // Initialize return array + $matches = array(); + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->marcRecord->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + $next = $this->getSubfieldArray($currentField, $subfields, $concat); + $matches = array_merge($matches, $next); + } + + return $matches; + } + + /** + * Get notes on finding aids related to the record. + * + * @return array + */ + public function getFindingAids() + { + return $this->getFieldArray('555'); + } + + /** + * Get the first value matching the specified MARC field and subfields. + * If multiple subfields are specified, they will be concatenated together. + * + * @param string $field The MARC field to read + * @param array $subfields The MARC subfield codes to read + * + * @return string + */ + protected function getFirstFieldValue($field, $subfields = null) + { + $matches = $this->getFieldArray($field, $subfields); + return (is_array($matches) && count($matches) > 0) ? + $matches[0] : null; + } + + /** + * Get general notes on the record. + * + * @return array + */ + public function getGeneralNotes() + { + return $this->getFieldArray('500'); + } + + /** + * Get human readable publication dates for display purposes (may not be suitable + * for computer processing -- use getPublicationDates() for that). + * + * @return array + */ + public function getHumanReadablePublicationDates() + { + return $this->getPublicationInfo('c'); + } + + /** + * Get an array of newer titles for the record. + * + * @return array + */ + public function getNewerTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : array(); + return in_array('785', $fieldsNames) ? array() : parent::getNewerTitles(); + } + + /** + * Get the item's publication information + * + * @param string $subfield The subfield to retrieve ('a' = location, 'c' = date) + * + * @return array + */ + protected function getPublicationInfo($subfield = 'a') + { + // First check old-style 260 field: + $results = $this->getFieldArray('260', array($subfield)); + + // Now track down relevant RDA-style 264 fields; we only care about + // copyright and publication places (and ignore copyright places if + // publication places are present). This behavior is designed to be + // consistent with default SolrMarc handling of names/dates. + $pubResults = $copyResults = array(); + + $fields = $this->marcRecord->getFields('264'); + if (is_array($fields)) { + foreach ($fields as $currentField) { + $currentVal = $currentField->getSubfield($subfield); + $currentVal = is_object($currentVal) + ? $currentVal->getData() : null; + if (!empty($currentVal)) { + switch ($currentField->getIndicator('2')) { + case '1': + $pubResults[] = $currentVal; + break; + case '4': + $copyResults[] = $currentVal; + break; + } + } + } + } + if (count($pubResults) > 0) { + $results = array_merge($results, $pubResults); + } else if (count($copyResults) > 0) { + $results = array_merge($results, $copyResults); + } + + return $results; + } + + /** + * Get the item's places of publication. + * + * @return array + */ + public function getPlacesOfPublication() + { + return $this->getPublicationInfo(); + } + + /** + * Get an array of playing times for the record (if applicable). + * + * @return array + */ + public function getPlayingTimes() + { + $times = $this->getFieldArray('306', array('a'), false); + + // Format the times to include colons ("HH:MM:SS" format). + for ($x = 0; $x < count($times); $x++) { + $times[$x] = substr($times[$x], 0, 2) . ':' . + substr($times[$x], 2, 2) . ':' . + substr($times[$x], 4, 2); + } + + return $times; + } + + /** + * Get an array of previous titles for the record. + * + * @return array + */ + public function getPreviousTitles() + { + // If the MARC links are being used, return blank array + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? array_map('trim', explode(',', $this->mainConfig->Record->marc_links)) + : array(); + return in_array('780', $fieldsNames) ? array() : parent::getPreviousTitles(); + } + + /** + * Get credits of people involved in production of the item. + * + * @return array + */ + public function getProductionCredits() + { + return $this->getFieldArray('508'); + } + + /** + * Get an array of publication frequency information. + * + * @return array + */ + public function getPublicationFrequency() + { + return $this->getFieldArray('310', array('a', 'b')); + } + + /** + * Get an array of strings describing relationships to other items. + * + * @return array + */ + public function getRelationshipNotes() + { + return $this->getFieldArray('580'); + } + + /** + * Get an array of all series names containing the record. Array entries may + * be either the name string, or an associative array with 'name' and 'number' + * keys. + * + * @return array + */ + public function getSeries() + { + $matches = array(); + + // First check the 440, 800 and 830 fields for series information: + $primaryFields = array( + '440' => array('a', 'p'), + '800' => array('a', 'b', 'c', 'd', 'f', 'p', 'q', 't'), + '830' => array('a', 'p')); + $matches = $this->getSeriesFromMARC($primaryFields); + if (!empty($matches)) { + return $matches; + } + + // Now check 490 and display it only if 440/800/830 were empty: + $secondaryFields = array('490' => array('a')); + $matches = $this->getSeriesFromMARC($secondaryFields); + if (!empty($matches)) { + return $matches; + } + + // Still no results found? Resort to the Solr-based method just in case! + return parent::getSeries(); + } + + /** + * Support method for getSeries() -- given a field specification, look for + * series information in the MARC record. + * + * @param array $fieldInfo Associative array of field => subfield information + * (used to find series name) + * + * @return array + */ + protected function getSeriesFromMARC($fieldInfo) + { + $matches = array(); + + // Loop through the field specification.... + foreach ($fieldInfo as $field => $subfields) { + // Did we find any matching fields? + $series = $this->marcRecord->getFields($field); + if (is_array($series)) { + foreach ($series as $currentField) { + // Can we find a name using the specified subfield list? + $name = $this->getSubfieldArray($currentField, $subfields); + if (isset($name[0])) { + $currentArray = array('name' => $name[0]); + + // Can we find a number in subfield v? (Note that number is + // always in subfield v regardless of whether we are dealing + // with 440, 490, 800 or 830 -- hence the hard-coded array + // rather than another parameter in $fieldInfo). + $number + = $this->getSubfieldArray($currentField, array('v')); + if (isset($number[0])) { + $currentArray['number'] = $number[0]; + } + + // Save the current match: + $matches[] = $currentArray; + } + } + } + } + + return $matches; + } + + /** + * Return an array of non-empty subfield values found in the provided MARC + * field. If $concat is true, the array will contain either zero or one + * entries (empty array if no subfields found, subfield values concatenated + * together in specified order if found). If concat is false, the array + * will contain a separate entry for each subfield value found. + * + * @param object $currentField Result from File_MARC::getFields. + * @param array $subfields The MARC subfield codes to read + * @param bool $concat Should we concatenate subfields? + * + * @return array + */ + protected function getSubfieldArray($currentField, $subfields, $concat = true) + { + // Start building a line of text for the current field + $matches = array(); + $currentLine = ''; + + // Loop through all subfields, collecting results that match the whitelist; + // note that it is important to retain the original MARC order here! + $allSubfields = $currentField->getSubfields(); + if (count($allSubfields) > 0) { + foreach ($allSubfields as $currentSubfield) { + if (in_array($currentSubfield->getCode(), $subfields)) { + // Grab the current subfield value and act on it if it is + // non-empty: + $data = trim($currentSubfield->getData()); + if (!empty($data)) { + // Are we concatenating fields or storing them separately? + if ($concat) { + $currentLine .= $data . ' '; + } else { + $matches[] = $data; + } + } + } + } + } + + // If we're in concat mode and found data, it will be in $currentLine and + // must be moved into the matches array. If we're not in concat mode, + // $currentLine will always be empty and this code will be ignored. + if (!empty($currentLine)) { + $matches[] = trim($currentLine); + } + + // Send back our result array: + return $matches; + } + + /** + * Get an array of summary strings for the record. + * + * @return array + */ + public function getSummary() + { + return $this->getFieldArray('520'); + } + + /** + * Get an array of technical details on the item represented by the record. + * + * @return array + */ + public function getSystemDetails() + { + return $this->getFieldArray('538'); + } + + /** + * Get an array of note about the record's target audience. + * + * @return array + */ + public function getTargetAudienceNotes() + { + return $this->getFieldArray('521'); + } + + /** + * Get the text of the part/section portion of the title. + * + * @return string + */ + public function getTitleSection() + { + return $this->getFirstFieldValue('245', array('n', 'p')); + } + + /** + * Get the statement of responsibility that goes with the title (i.e. "by John + * Smith"). + * + * @return string + */ + public function getTitleStatement() + { + return $this->getFirstFieldValue('245', array('c')); + } + + /** + * Get an array of lines from the table of contents. + * + * @return array + */ + public function getTOC() + { + // Return empty array if we have no table of contents: + $fields = $this->marcRecord->getFields('505'); + if (!$fields) { + return array(); + } + + // If we got this far, we have a table -- collect it as a string: + $toc = array(); + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + foreach ($subfields as $subfield) { + // Break the string into appropriate chunks, and merge them into + // return array: + $toc = array_merge($toc, explode('--', $subfield->getData())); + } + } + return $toc; + } + + /** + * Get hierarchical place names (MARC field 752) + * + * returns an array of formatted hierarchical place names, consisting of all + * alpha-subfields, concatenated for display + * + * @return array + */ + public function getHierarchicalPlaceNames() + { + $placeNames = array(); + if ($fields = $this->marcRecord->getFields('752')) { + foreach ($fields as $field) { + $subfields = $field->getSubfields(); + $current = array(); + foreach ($subfields as $subfield) { + if (!is_numeric($subfield->getCode())) { + $current[] = $subfield->getData(); + } + } + $placeNames[] = implode(' -- ', $current); + } + } + return $placeNames; + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + $retVal = array(); + + // Which fields/subfields should we check for URLs? + $fieldsToCheck = array( + '856' => array('y', 'z'), // Standard URL + '555' => array('a') // Cumulative index/finding aids + ); + + foreach ($fieldsToCheck as $field => $subfields) { + $urls = $this->marcRecord->getFields($field); + if ($urls) { + foreach ($urls as $url) { + // Is there an address in the current field? + $address = $url->getSubfield('u'); + if ($address) { + $address = $address->getData(); + + // Is there a description? If not, just use the URL itself. + foreach ($subfields as $current) { + $desc = $url->getSubfield($current); + if ($desc) { + break; + } + } + if ($desc) { + $desc = $desc->getData(); + } else { + $desc = $address; + } + + $retVal[] = array('url' => $address, 'desc' => $desc); + } + } + } + } + + return $retVal; + } + + /** + * Get all record links related to the current record. Each link is returned as + * array. + * Format: + * array( + * array( + * 'title' => label_for_title + * 'value' => link_name + * 'link' => link_URI + * ), + * ... + * ) + * + * @return null|array + */ + public function getAllRecordLinks() + { + // Load configurations: + $fieldsNames = isset($this->mainConfig->Record->marc_links) + ? explode(',', $this->mainConfig->Record->marc_links) : array(); + $useVisibilityIndicator + = isset($this->mainConfig->Record->marc_links_use_visibility_indicator) + ? $this->mainConfig->Record->marc_links_use_visibility_indicator : true; + + $retVal = array(); + foreach ($fieldsNames as $value) { + $value = trim($value); + $fields = $this->marcRecord->getFields($value); + if (!empty($fields)) { + foreach ($fields as $field) { + // Check to see if we should display at all + if ($useVisibilityIndicator) { + $visibilityIndicator = $field->getIndicator('1'); + if ($visibilityIndicator == '1') { + continue; + } + } + + // Get data for field + $tmp = $this->getFieldData($field); + if (is_array($tmp)) { + $retVal[] = $tmp; + } + } + } + } + return empty($retVal) ? null : $retVal; + } + + /** + * Support method for getFieldData() -- factor the relationship indicator + * into the field number where relevant to generate a note to associate + * with a record link. + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return string + */ + protected function getRecordLinkNote($field) + { + // Normalize blank relationship indicator to 0: + $relationshipIndicator = $field->getIndicator('2'); + if ($relationshipIndicator == ' ') { + $relationshipIndicator = '0'; + } + + // Assign notes based on the relationship type + $value = $field->getTag(); + switch ($value) { + case '780': + if (in_array($relationshipIndicator, range('0', '7'))) { + $value .= '_' . $relationshipIndicator; + } + break; + case '785': + if (in_array($relationshipIndicator, range('0', '8'))) { + $value .= '_' . $relationshipIndicator; + } + break; + } + + return 'note_' . $value; + } + + /** + * Returns the array element for the 'getAllRecordLinks' method + * + * @param File_MARC_Data_Field $field Field to examine + * + * @return array|bool Array on success, boolean false if no + * valid link could be found in the data. + */ + protected function getFieldData($field) + { + // Make sure that there is a t field to be displayed: + if ($title = $field->getSubfield('t')) { + $title = $title->getData(); + } else { + return false; + } + + $linkTypeSetting = isset($this->mainConfig->Record->marc_links_link_types) + ? $this->mainConfig->Record->marc_links_link_types + : 'id,oclc,dlc,isbn,issn,title'; + $linkTypes = explode(',', $linkTypeSetting); + $linkFields = $field->getSubfields('w'); + + // Run through the link types specified in the config. + // For each type, check field for reference + // If reference found, exit loop and go straight to end + // If no reference found, check the next link type instead + foreach ($linkTypes as $linkType) { + switch (trim($linkType)){ + case 'oclc': + foreach ($linkFields as $current) { + if ($oclc = $this->getIdFromLinkingField($current, 'OCoLC')) { + $link = array('type' => 'oclc', 'value' => $oclc); + } + } + break; + case 'dlc': + foreach ($linkFields as $current) { + if ($dlc = $this->getIdFromLinkingField($current, 'DLC', true)) { + $link = array('type' => 'dlc', 'value' => $dlc); + } + } + break; + case 'id': + foreach ($linkFields as $current) { + if ($bibLink = $this->getIdFromLinkingField($current)) { + $link = array('type' => 'bib', 'value' => $bibLink); + } + } + break; + case 'isbn': + if ($isbn = $field->getSubfield('z')) { + $link = array( + 'type' => 'isn', 'value' => trim($isbn->getData()), + 'exclude' => $this->getUniqueId() + ); + } + break; + case 'issn': + if ($issn = $field->getSubfield('x')) { + $link = array( + 'type' => 'isn', 'value' => trim($issn->getData()), + 'exclude' => $this->getUniqueId() + ); + } + break; + case 'title': + $link = array('type' => 'title', 'value' => $title); + break; + } + // Exit loop if we have a link + if (isset($link)) { + break; + } + } + // Make sure we have something to display: + return !isset($link) ? false : array( + 'title' => $this->getRecordLinkNote($field), + 'value' => $title, + 'link' => $link + ); + } + + /** + * Returns an id extracted from the identifier subfield passed in + * + * @param \File_MARC_Subfield $idField MARC field containing id information + * @param string $prefix Prefix to search for in id field + * @param bool $raw Return raw match, or normalize? + * + * @return string|bool ID on success, false on failure + */ + protected function getIdFromLinkingField($idField, $prefix = null, $raw = false) + { + $text = $idField->getData(); + if (preg_match('/\(([^)]+)\)(.+)/', $text, $matches)) { + // If prefix matches, return ID: + if ($matches[1] == $prefix) { + // Special case -- LCCN should not be stripped: + return $raw + ? $matches[2] + : trim(str_replace(range('a', 'z'), '', ($matches[2]))); + } + } else if ($prefix == null) { + // If no prefix was given or found, we presume it is a raw bib record + return $text; + } + return false; + } + + /** + * Get Status/Holdings Information from the internally stored MARC Record + * (support method used by the NoILS driver). + * + * @param array $field The MARC Field to retrieve + * @param array $data A keyed array of data to retrieve from subfields + * + * @return array + */ + public function getFormattedMarcDetails($field, $data) + { + // Initialize return array + $matches = array(); + $i = 0; + + // Try to look up the specified field, return empty array if it doesn't + // exist. + $fields = $this->marcRecord->getFields($field); + if (!is_array($fields)) { + return $matches; + } + + // Extract all the requested subfields, if applicable. + foreach ($fields as $currentField) { + foreach ($data as $key => $info) { + $split = explode("|", $info); + if ($split[0] == "msg") { + if ($split[1] == "true") { + $result = true; + } elseif ($split[1] == "false") { + $result = false; + } else { + $result =$split[1]; + } + $matches[$i][$key] = $result; + } else { + // Default to subfield a if nothing is specified. + if (count($split) < 2) { + $subfields = array('a'); + } else { + $subfields = str_split($split[1]); + } + $result = $this->getSubfieldArray( + $currentField, $subfields, true + ); + $matches[$i][$key] = count($result) > 0 + ? (string)$result[0] : ''; + } + } + $matches[$i]['id'] = $this->getUniqueID(); + $i++; + } + return $matches; + } + + /** + * Return an XML representation of the record using the specified format. + * Return false if the format is unsupported. + * + * @param string $format Name of format to use (corresponds with OAI-PMH + * metadataPrefix parameter). + * @param string $baseUrl Base URL of host containing VuFind (optional; + * may be used to inject record URLs into XML when appropriate). + * @param RecordLink $recordLink Record link helper (optional; may be used to + * inject record URLs into XML when appropriate). + * + * @return mixed XML, or false if format unsupported. + */ + public function getXML($format, $baseUrl = null, $recordLink = null) + { + // Special case for MARC: + if ($format == 'marc21') { + $xml = $this->marcRecord->toXML(); + $xml = str_replace( + array(chr(27), chr(28), chr(29), chr(30), chr(31)), ' ', $xml + ); + $xml = simplexml_load_string($xml); + if (!$xml || !isset($xml->record)) { + return false; + } + + // Set up proper namespacing and extract just the <record> tag: + $xml->record->addAttribute('xmlns', "http://www.loc.gov/MARC21/slim"); + $xml->record->addAttribute( + 'xsi:schemaLocation', + 'http://www.loc.gov/MARC21/slim ' . + 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd', + 'http://www.w3.org/2001/XMLSchema-instance' + ); + $xml->record->addAttribute('type', 'Bibliographic'); + return $xml->record->asXML(); + } + + // Try the parent method: + return parent::getXML($format, $baseUrl, $recordLink); + } + + /** + * Attach an ILS connection and related logic to the driver + * + * @param \VuFind\ILS\Connection $ils ILS connection + * @param \VuFind\ILS\Logic\Holds $holdLogic Hold logic handler + * @param \VuFind\ILS\Logic\TitleHolds $titleHoldLogic Title hold logic handler + * + * @return void + */ + public function attachILS(\VuFind\ILS\Connection $ils, + \VuFind\ILS\Logic\Holds $holdLogic, + \VuFind\ILS\Logic\TitleHolds $titleHoldLogic + ) { + $this->ils = $ils; + $this->holdLogic = $holdLogic; + $this->titleHoldLogic = $titleHoldLogic; + } + + /** + * Do we have an attached ILS connection? + * + * @return bool + */ + protected function hasILS() + { + return null !== $this->ils; + } + + /** + * Get an array of information about record holdings, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHoldings() + { + return $this->hasILS() + ? $this->holdLogic->getHoldings($this->getUniqueID()) + : array(); + } + + /** + * Get an array of information about record history, obtained in real-time + * from the ILS. + * + * @return array + */ + public function getRealTimeHistory() + { + // Get Acquisitions Data + if (!$this->hasILS()) { + return array(); + } + try { + return $this->ils->getPurchaseHistory($this->getUniqueID()); + } catch (ILSException $e) { + return array(); + } + } + + /** + * Get a link for placing a title level hold. + * + * @return mixed A url if a hold is possible, boolean false if not + */ + public function getRealTimeTitleHold() + { + if ($this->hasILS()) { + $biblioLevel = strtolower($this->getBibliographicLevel()); + if ("monograph" == $biblioLevel || strstr("part", $biblioLevel)) { + if ($this->ils->getTitleHoldsMode() != "disabled") { + return $this->titleHoldLogic->getHold($this->getUniqueID()); + } + } + } + + return false; + } + + /** + * Returns true if the record supports real-time AJAX status lookups. + * + * @return bool + */ + public function supportsAjaxStatus() + { + return true; + } + + /** + * Get access to the raw File_MARC object. + * + * @return File_MARCBASE + */ + public function getMarcRecord() + { + return $this->marcRecord; + } + + /** + * Get an XML RDF representation of the data in this record. + * + * @return mixed XML RDF data (empty if unsupported or error). + */ + public function getRDFXML() + { + return XSLTProcessor::process( + 'record-rdf-mods.xsl', trim($this->marcRecord->toXML()) + ); + } +} diff --git a/module/finc/src/finc/RecordDriver/SolrMarcRemote.php b/module/finc/src/finc/RecordDriver/SolrMarcRemote.php index 7c307fe7ca36c234fb3102bdebe48cfb5964b923..2604a0ad79d254890d2aafa1be7fd66e807f9d96 100644 --- a/module/finc/src/finc/RecordDriver/SolrMarcRemote.php +++ b/module/finc/src/finc/RecordDriver/SolrMarcRemote.php @@ -22,7 +22,9 @@ * * @category VuFind2 * @package RecordDrivers - * @author André Lahmann <lahmann@ub.uni-leipzig.de>, Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:record_drivers Wiki */ @@ -35,11 +37,13 @@ use \Zend\Log\LoggerInterface; * * @category VuFind2 * @package RecordDrivers - * @author André Lahmann <lahmann@ub.uni-leipzig.de>, Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Ulf Seltmann <seltmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:record_drivers Wiki */ -class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc +class SolrMarcRemote extends SolrMarc { /** * Logger (or false for none) @@ -69,6 +73,13 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc */ protected $mainConfig; + /** + * holds searches.ini data + * + * @var array + */ + protected $searchesConfig; + /** * Constructor * @@ -95,6 +106,7 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc } $this->mainConfig = $mainConfig; + $this->searchesConfig = $searchSettings; } /** @@ -173,7 +185,7 @@ class SolrMarcRemote extends \VuFind\RecordDriver\SolrMarc * @throws \Exception * @throws \File_MARC_Exception */ - private function getRemoteData() { + protected function getRemoteData() { // handle availability of fullrecord if (isset($this->fields['fullrecord'])) { diff --git a/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php b/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php new file mode 100644 index 0000000000000000000000000000000000000000..af2cfc78b9582c74672dd3e15dbafda7a5d9ef13 --- /dev/null +++ b/module/finc/src/finc/RecordDriver/SolrMarcRemoteFinc.php @@ -0,0 +1,1020 @@ +<?php +/** + * finc specific model for MARC records without a fullrecord in Solr. The fullrecord is being + * retrieved from an external source. + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +namespace finc\RecordDriver; + +/** + * finc specific model for MARC records without a fullrecord in Solr. The fullrecord is being + * retrieved from an external source. + * + * @category VuFind2 + * @package RecordDrivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:record_drivers Wiki + */ +class SolrMarcRemoteFinc extends SolrMarcRemote +{ + + /** + * pattern to identify bsz + */ + const BSZ_PATTERN = '/^(\(DE-576\))(\d+)(\w|)/'; + + /** + * @var string ISIL of this instance's library + */ + protected $isil = ''; + + /** + * @var array Array of ISILs set in the LibraryGroup section in config.ini. + */ + protected $libraryGroup = array(); + + /** + * @var string|null + * @link https://intern.finc.info/fincproject/projects/finc-intern/wiki/FincMARC_-_Erweiterung_von_MARC21_f%C3%BCr_finc + */ + protected $localMarcFieldOfLibrary = null; + + /** + * Constructor + * + * @param \Zend\Config\Config $mainConfig VuFind main configuration (omit for + * built-in defaults) + * @param \Zend\Config\Config $recordConfig Record-specific configuration file + * (omit to use $mainConfig as $recordConfig) + * @param \Zend\Config\Config $searchSettings Search-specific configuration file + */ + public function __construct($mainConfig = null, $recordConfig = null, + $searchSettings = null + ) + { + parent::__construct($mainConfig, $recordConfig, $searchSettings); + + if (isset($mainConfig->InstitutionInfo->isil)) { + $this->isil = $this->mainConfig->InstitutionInfo->isil; + } else { + $this->debug('InstitutionInfo setting is missing.'); + } + + if (isset($mainConfig->LibraryGroup->libraries)) { + $this->libraryGroup = explode(',' , $this->mainConfig->LibraryGroup->libraries); + } else { + $this->debug('LibraryGroup setting is missing.'); + } + + if (isset($this->mainConfig->CustomSite->namespace)) { + // map for marc fields + $map = [ + 'che' => '971', + 'hgb' => '979', + 'hfbk' => '978', + 'hfm' => '977', + 'hmt' => '970', + 'htw' => '973', + 'htwk' => '974', + 'tuf' => '972', + 'ubl' => '969', + 'zit' => '976', + 'zwi' => '975', + ]; + $this->localMarcFieldOfLibrary = + isset($map[$this->mainConfig->CustomSite->namespace]) ? + $map[$this->mainConfig->CustomSite->namespace] : null; + } else { + $this->debug('Namespace setting for localMarcField is missing.'); + } + } + + /** + * Return an array of associative URL arrays with one or more of the following + * keys: + * + * <li> + * <ul>desc: URL description text to display (optional)</ul> + * <ul>url: fully-formed URL (required if 'route' is absent)</ul> + * <ul>route: VuFind route to build URL with (required if 'url' is absent)</ul> + * <ul>routeParams: Parameters for route (optional)</ul> + * <ul>queryString: Query params to append after building route (optional)</ul> + * </li> + * + * @return array + */ + public function getURLs() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retVal = array(); + + // Which fields/subfields should we check for URLs? + $fieldsToCheck = array( + '856' => array('u'), // Standard URL + '555' => array('a') // Cumulative index/finding aids + ); + + foreach ($fieldsToCheck as $field => $subfields) { + $urls = $this->marcRecord->getFields($field); + if ($urls) { + foreach ($urls as $url) { + + $isil = $url->getSubfield('9'); + + $isISIL = false; + + if($isil) { + $isil = $isil->getData(); + if(preg_match('/'.$this->isil.'.*/', $isil)) { + $isISIL = true; + } + } else { + $isISIL = true; + } + + if($isISIL) { + + // Is there an address in the current field? + $address = $url->getSubfield('u'); + if ($address) { + $address = $address->getData(); + + // Is there a description? If not, just use the URL itself. + foreach (array('3', 'y', 'z', 'x') as $current) { + $desc = $url->getSubfield($current); + if ($desc) { + break; + } + } + if ($desc) { + $desc = $desc->getData(); + } else { + $desc = $address; + } + + // If url doesn't exist as key so far write to return variable. + if (!in_array(array('url' => $address, 'desc' => $desc), $retVal)) { + $retVal[] = array('url' => $address, 'desc' => $desc); + } + } + } + } + } + } + return $retVal; + } + + /** + * Return the local callnumber. + * + * @todo Optimization by removing of prefixed isils + * + * @return array Return fields. + * @access public + * @link https://intern.finc.info/issues/2639 + */ + public function getLocalCallnumber() + { + $array = array(); + + if (count($this->libraryGroup) > 0 && isset($this->fields['itemdata'])) + { + $itemdata = json_decode($this->fields['itemdata'], true); + if (count($itemdata) > 0) { + // error_log('Test: '. print_r($this->fields['itemdata'], true)); + $i = 0; + foreach ($this->libraryGroup as $isil) { + if (isset($itemdata[$isil])) { + foreach ($itemdata[$isil] as $val) { + $array[$i]['barcode'] = '(' . $isil . ')' . $val['bc']; + $array[$i]['callnumber'] = '(' . $isil . ')' . $val['cn']; + $i++; + } + } // end if + } // end foreach + } // end if + } // end if + return $array; + } + + /** + * Get local callnumbers of a special library. + * + * @return array + * @access protected + */ + protected function getLocalCallnumbersByLibrary() + { + $array = array(); + $callnumbers = array(); + + if (count($this->libraryGroup) > 0 && isset($this->fields['itemdata'])) + { + $itemdata = json_decode($this->fields['itemdata'], true); + if (count($itemdata) > 0) { + $i = 0; + foreach ($this->libraryGroup as $isil) { + if (isset($itemdata[$isil])) { + foreach ($itemdata[$isil] as $val) { + // exclude equal callnumbers + if (false == in_array($val['cn'], $callnumbers)) { + $array[$i]['callnumber'] = $val['cn']; + $array[$i]['location'] = $isil; + $callnumbers[] = $val['cn']; + $i++; + } + } // end foreach + } // end if + } // end foreach + } // end if + } // end if + unset($callnumbers); + return $array; + } + + /** + * Get the special local call number; for the moment only used by the + * university library of Freiberg at finc marc 972i. + * + * @return string + * @access protected + */ + protected function getLocalGivenCallnumber() + { + $retval = array(); + $arrSignatur = $this->getFieldArray($this->localMarcFieldOfLibrary, array('i')); + + foreach ($arrSignatur as $signatur) { + foreach ($this->libraryGroup as $code) { + if (0 < preg_match('/^\('.$code.'\)/', $signatur)) { + + $retval[] = preg_replace( '/^\('.$code.'\)/','', $signatur); + } + } + } + return $retval; + } + + /** + * Get an array of supplements and special issue entry. + * + * @link http://www.loc.gov/marc/bibliographic/bd770.html + * @return array + * @access protected + */ + protected function getSupplements() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + //return $this->_getFieldArray('770', array('i','t')); // has been originally 'd','h','n','x' but only 'i' and 't' for ubl requested; + $array = array(); + $supplement = $this->marcRecord->getFields('770'); + // if not return void value + if (!$supplement) { + return $array; + } // end if + + foreach ($supplement as $key => $line) { + $array[$key]['pretext'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$key]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + //$id = $this->checkIfRecordExists($matches[2]); + //if ($id != null) { + $array[$key]['record_id'] = $matches[2].$matches[3]; + //} + //break; + } + } // end foreach + } // end foreach + + return $this->addFincIDToRecord($array); + } + + /** + * Special method to extracting the index of German prints of the marc21 + * field 024 indicator 8 subfield a + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1442 + */ + protected function getIndexOfGermanPrints() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // define a false indicator + $lookfor_indicator = '8'; + $retval = array(); + + $fields = $this->marcRecord->getFields('024'); + if (!$fields) { + return null; + } + foreach ($fields as $field) { + // ->getIndicator(position) + $subjectrow = $field->getIndicator('1'); + if ($subjectrow == $lookfor_indicator) { + if ($subfield = $field->getSubfield('a')){ + if (preg_match('/^VD/i', $subfield->getData()) > 0) { + $retval[] = $subfield->getData(); + } + } + } + } + // echo "<pre>"; print_r($retval); echo "</pre>"; + return $retval; + } + + /** + * Get an array of instrumentation notes taken from the local data + * of the Petrucci music library subfield 590b + * + * @return array + * @access protected + */ + protected function getInstrumentation() + { + return $this->getFieldArray('590', array('b')); + } + + /** + * Get the ISSN from a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + protected function getISSN() + { + return $this->getFieldArray('022', array('a')); + } + + /** + * Get the ISSN from a the parallel title of a record. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/969 description + */ + protected function getISSNsParallelTitles() + { + return $this->getFieldArray('029', array('a')); + } + + /** + * Get an array of information about Journal holdings realised for the + * special needs of University library of Chemnitz. MAB fields 720. + * + * @return array + * @access public + * @link https://intern.finc.info/fincproject/issues/338 + */ + public function getJournalHoldings() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retval = array(); + $match = array(); + + // Get ID and connect to catalog + //$catalog = ConnectionManager::connectToCatalog(); + //$terms = $catalog->getConfig('OrderJournalTerms'); + + $fields = $this->marcRecord->getFields('971'); + if (!$fields) { + return array(); + } + + $key = 0; + foreach ($fields as $field) { + /*if ($subfield = $field->getSubfield('j')) { + preg_match('/\(.*\)(.*)/', $subfield->getData(), $match); + $retval[$key]['callnumber'] = trim($match[1]); + }*/ + if ($subfield = $field->getSubfield('k')) { + preg_match('/(.*)##(.*)##(.*)/', trim($subfield->getData()), $match); + $retval[$key]['callnumber'] = trim($match[1]); + $retval[$key]['holdings'] = trim($match[2]); + $retval[$key]['footnote'] = trim($match[3]); + // deprecated check if a certain wording exist + // $retval[$key]['is_holdable'] = (in_array(trim($match[3]), $terms['terms'])) ? 1 : 0; + // if subfield k exists so make journal holdable + $retval[$key]['is_holdable'] = 1; + + if (count($this->getBarcode()) == 1) { + $current = $this->getBarcode(); + $barcode = $current[0]; + } else { + $barcode = ''; + } + // deprecated check if a certain wording exist + // $retval[$key]['link'] = (in_array(trim($match[3]), $terms['terms'])) ? '/Record/' . $this->getUniqueID() .'/HoldJournalCHE?callnumber=' . urlencode($retval[$key]['callnumber']) .'&barcode=' . $barcode : ''; + // if subfield k exists so make journal holdable + $retval[$key]['link'] = '/Record/' . $this->getUniqueID() .'/HoldJournalCHE?callnumber=' . urlencode($retval[$key]['callnumber']) .'&barcode=' . $barcode; + //var_dump($retval[$key]['is_holdable'], $terms); + $key++; + } + + } + return $retval; + } + + /** + * Return a local access number for call number. + * Marc field depends on library e.g. 975 for WHZ. + * Seems to be very extraordinary special case. + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/1302 + */ + protected function getLocalAccessNumber() + { + if (null != $this->localMarcFieldOfLibrary) { + return $this->getFieldArray($this->localMarcFieldOfLibrary, array('o')); + } + return array(); + } + + /** + * Get all local class subjects. First realization for HGB. + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/2626 + */ + protected function getLocalClassSubjects() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $classsubjects = $this->marcRecord->getFields('979'); + // if not return void value + if (!$classsubjects) { + return $array; + } // end if + foreach ($classsubjects as $key => $line) { + // if subfield with class subjects exists + if ($line->getSubfield('f')) { + // get class subjects + $array[$key]['nb'] = $line->getSubfield('f')->getData(); + } // end if subfield a + if ($line->getSubfield('9')) { + $array[$key]['data'] = $line->getSubfield('9')->getData(); + /* $tmp = $line->getSubfield('9')->getData(); + $tmpArray = array(); + $data = explode(',', $tmp); + if(is_array($data) && (count($data) > 0)) { + foreach ($data as $value) { + $tmpArray[] = $value; + } + } + if(count($tmpArray) > 0) { + $array[$key]['data'] = $tmpArray; + } else { + $array[$key]['data'] = $data; + }*/ + } + } // end foreach + return $array; + } + + + /** + * Returning local format field of a library using an consortial defined + * field with subfield $c. Marc field depends on library e.g. 970 for HMT or + * 972 for TUBAF + * + * @return array + * @access protected + */ + public function getLocalFormat() + { + if (null != $this->localMarcFieldOfLibrary) { + if (count($localformat = $this->getFieldArray($this->localMarcFieldOfLibrary, array('c'))) > 0) { + foreach ($localformat as &$line) { + if ($line != "") { + $line = trim('local_format_' . strtolower($line)); + } + } + unset($line); + return $localformat; + } + } + return array(); + } + + /** + * Return a local notice via an consortial defined field with subfield $k. + * Marc field depends on library e.g. 970 for HMT or 972 for TUBAF. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1308 + */ + protected function getLocalNotice() + { + if (null != $this->localMarcFieldOfLibrary) { + return $this->getFieldArray($this->localMarcFieldOfLibrary, array('k')); + } + return array(); + } + + /** + * Get an array of musical heading based on a swb field + * at the marc field. + * + * @return mixed null if there's no field or array with results + * @access protected + */ + protected function getMusicHeading() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $retval = array(); + + $fields = $this->marcRecord->getFields('937'); + if (!$fields) { + return null; + } + foreach ($fields as $key => $field) { + if ($d = $field->getSubfield('d')) { + $retval[$key][] = $d->getData(); + } + if ($e = $field->getSubfield('e')) { + $retval[$key][] = $e->getData(); + } + if ($f = $field->getSubfield('f')) { + $retval[$key][] = $f->getData(); + } + } + return $retval; + } + + /** + * Get notice of a title representing a special case of University + * library of Chemnitz: MAB field 999l + * + * @return string + * @access protected + */ + protected function getNotice() + { + return $this->getFirstFieldValue('971', array('l')); + } + + /** + * Get an array of style/genre of a piece taken from the local data + * of the Petrucci music library subfield 590a + * + * @return array + * @access protected + */ + protected function getPieceStyle() + { + return $this->getFieldArray('590', array('a')); + } + + /** + * Get specific marc information about parallel editions. Unflexible solution + * for HMT only implemented. + * + * @todo more flexible implementation + * + * @return array + * @access protected + * @link https://intern.finc.info/issues/4327 + */ + protected function getParallelEditions() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $fields = array('775'); + $i = 0; + + foreach ($fields as $field) { + + $related = $this->marcRecord->getFields($field); + // if no entry break it + if ($related) { + foreach ($related as $key => $line) { + // check if subfields i or t exist. if yes do a record. + if ($line->getSubfield('i') || $line->getSubfield('t')) { + $array[$i]['identifier'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$i]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + if (is_array($linkFields) && count($linkFields) > 0) { + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + $array[$key]['record_id'] = $matches[2].$matches[3]; + } + } // end foreach + } // end if + $i++; + } // end if + } // end foreach + } + } + return $this->addFincIDToRecord($array); + } + + /** + * Get an array of previous titles for the record. + * + * @todo use HttpService for URL query + * + * @return string + * @access protected + */ + public function getPrice() + { + $currency = $this->getFirstFieldValue('365', array('c')); + $price = $this->getFirstFieldValue('365', array('b')); + if (!empty($currency) && !empty($price) ) { + // if possible convert it in euro + if (is_array($converted = + json_decode(str_replace( + array('lhs','rhs','error','icc'), + array('"lhs"','"rhs"','"error"','"icc"'), + file_get_contents("http://www.google.com/ig/calculator?q=".$price.$currency."=?EUR") + ),true) + )) { + if(empty($converted['error'])){ + $rhs = explode(' ', trim($converted['rhs'])); + return money_format('%.2n', $rhs[0]); + } + } + return $currency . " ". $price; + } + return ""; + } + + /** + * Get the provenience of a title. + * + * @return array + * @access protected + */ + protected function getProvenience() + { + return $this->getFieldArray('561', array('a')); + } + + /** + * Checked if an title is ordered by the library using an consortial defined + * field with subfield $m. Marc field depends on library e.g. 970 for HMT or + * 972 for TUBAF + * + * @return bool + * @access protected + */ + protected function getPurchaseInformation() + { + if (null != $this->localMarcFieldOfLibrary) { + if ( $this->getFirstFieldValue($this->localMarcFieldOfLibrary, array('m')) == 'e') { + return true; + } + } + return false; + } + + /** + * Get a short list of series for ISBD citation style + * + * @return array + * @access protected + * @link http://www.loc.gov/marc/bibliographic/bd830.html + * @link https://intern.finc.info/fincproject/issues/457 + */ + protected function getSeriesWithVolume() + { + return $this->getFieldArray('830', array('a','v'), false); + } + + /** + * Get local classification of UDK. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1135 + */ + protected function getUDKs() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + if (null != $this->localMarcFieldOfLibrary) { + + $udk = $this->marcRecord->getFields($this->localMarcFieldOfLibrary); + // if not return void value + if (!$udk) { + return $array; + } // end if + + foreach ($udk as $key => $line) { + // if subfield with udk exists + if ($line->getSubfield('f')) { + // get udk + $array[$key]['index'] = $line->getSubfield('f')->getData(); + // get udk notation + // fixes by update of File_MARC to version 0.8.0 + // @link https://intern.finc.info/issues/2068 + /* + if ($notation = $line->getSubfield('n')) { + // get first value + $array[$key]['notation'][] = $notation->getData(); + // iteration over udk notation + while ($record = $notation->next()) { + $array[$key]['notation'][] = $record->getData(); + $notation = $record; + } + } // end if subfield n + unset($notation); + */ + if ($record = $line->getSubfields('n')) { + // iteration over rvk notation + foreach ($record as $field) { + $array[$key]['notation'][] = $field->getData(); + } + } // end if subfield n + } // end if subfield f + } // end foreach + } + //error_log(print_r($array, true)); + return $array; + } + + /** + * Get addional entries for personal names. + * + * @return array + * @access protected + * @link http://www.loc.gov/marc/bibliographic/bd700.html + */ + protected function getAdditionalAuthors() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // result array to return + $retval = array(); + + $results = $this->marcRecord->getFields('700'); + if (!$results) { + return $retval; + } + + foreach ($results as $key => $line) { + $retval[$key]['name'] = ($line->getSubfield('a')) + ? $line->getSubfield('a')->getData() : ''; + $retval[$key]['dates'] = ($line->getSubfield('d')) + ? $line->getSubfield('d')->getData() : ''; + $retval[$key]['relator'] = ($line->getSubfield('e')) + ? $line->getSubfield('e')->getData() : ''; + } + // echo "<pre>"; print_r($retval); echo "</pre>"; + return $retval; + } + + /** + * Get specific marc information about additional items. Unflexible solution + * for UBL only implemented. + * + * @return array + * @access protected + * @link https://intern.finc.info/fincproject/issues/1315 + */ + protected function getAdditionals() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + $array = array(); + $fields = array('770','775','776'); + $i = 0; + + foreach ($fields as $field) { + + $related = $this->marcRecord->getFields($field); + // if no entry break it + if ($related) { + foreach ($related as $key => $line) { + // check if subfields i or t exist. if yes do a record. + if ($line->getSubfield('i') || $line->getSubfield('t')) { + $array[$i]['identifier'] = ($line->getSubfield('i')) + ? $line->getSubfield('i')->getData() : ''; + $array[$i]['text'] = ($line->getSubfield('t')) + ? $line->getSubfield('t')->getData() : ''; + // get ppns of bsz + $linkFields = $line->getSubfields('w'); + if (is_array($linkFields) && count($linkFields) > 0) { + foreach ($linkFields as $current) { + $text = $current->getData(); + // Extract parenthetical prefixes: + if (preg_match(self::BSZ_PATTERN, $text, $matches)) { + $array[$i]['record_id'] = $matches[2].$matches[3]; + } + } // end foreach + } // end if + $i++; + } // end if + } // end foreach + } + } + return $this->addFincIDToRecord($array); + } + + /** + * Special method to extracting the data of the marc21 field 689 of the + * the bsz heading subjects chains. + * + * @return array + * @access protected + */ + protected function getAllSubjectHeadingsExtended() + { + + if(empty($this->marcRecord)) { + $this->getRemoteData(); + } + + // define a false indicator + $firstindicator = 'x'; + $retval = array(); + + $fields = $this->marcRecord->getFields('689'); + if (!$fields) { + return null; + } + foreach ($fields as $field) { + $subjectrow = $field->getIndicator('1'); + if ($subjectrow != $firstindicator) { + $key = (isset($key) ? $key +1 : 0); + $firstindicator = $subjectrow; + } + if ($subfield = $field->getSubfield('a')){ + $retval[$key]['subject'][] = $subfield->getData(); + } + if ($subfield = $field->getSubfield('t')){ + $retval[$key]['subject'][] = $subfield->getData(); + } + if ($subfield = $field->getSubfield('9')){ + $retval[$key]['subsubject'] = $subfield->getData(); + } + } + return $retval; + } + + /** + * Return all barcode of finc marc 983 $a at full marc record. + * + * @param string Prefixes of library seals. + * + * @return array List of barcodes. + * @access protected + */ + protected function getBarcode() + { + + $barcodes = array(); + + //$driver = ConnectionManager::connectToCatalog(); + //$libraryCodes = $driver->getIniFieldAsArray('searches','LibraryGroup'); + $libraryCodes = $this->searchesConfig->LibrarayGroup; + + // get barcodes from marc + $barcodes = $this->getFieldArray('983', array('a')); + + if (!isset($libraryCodes->libraries)) { + return $barcodes; + } else { + if (count($barcodes) > 0) { + $codes = explode(",", $libraryCodes->libraries); + $match = array(); + $retval = array(); + foreach($barcodes as $barcode) { + if (preg_match('/^\((.*)\)(.*)$/', trim($barcode), $match)); + if ( in_array($match[1], $codes) ) { + $retval[] = $match[2]; + } + } // end foreach + if (count($retval) > 0 ) { + return $retval; + } + } + } + return array(); + } + + /** + * Get the catalogue or opus number of a title. Implemented + * for petrucci music library. + * + * @return array + * @access protected + */ + protected function getCatalogueNumber() + { + return $this->getFieldArray('245', array('b')); + } + + /** + * Get an array of content notes. + * + * @return array + * @access protected + */ + protected function getContentNote() + { + return $this->getFieldArray('505', array('t')); + } + + /** + * Get dissertation notes for the record. + * + * @return array + * @access protected + */ + protected function getDissertationNote() + { + return $this->getFieldArray('502', array('a')); + } + + /** + * Get id of related items + * + * @return string + * @access protected + */ + protected function getRelatedItems() + { + return $this->getFirstFieldValue('776', array('z')); + } +} diff --git a/module/finc/src/finc/Resolver/Driver/Factory.php b/module/finc/src/finc/Resolver/Driver/Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..f17535739c698e2dae8e9ea20b3778f221a792fa --- /dev/null +++ b/module/finc/src/finc/Resolver/Driver/Factory.php @@ -0,0 +1,58 @@ +<?php +/** + * Resolver Driver Factory Class + * + * PHP version 5 + * + * Copyright (C) Villanova University 2014. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Resolver_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +namespace finc\Resolver\Driver; +use Zend\ServiceManager\ServiceManager; + +/** + * Resolver Driver Factory Class + * + * @category VuFind2 + * @package Resolver_Drivers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:hierarchy_components Wiki + */ +class Factory +{ + + /** + * Factory for Redi record driver. + * + * @param ServiceManager $sm Service manager. + * + * @return Redi + */ + public static function getRedi(ServiceManager $sm) + { + $config = $sm->getServiceLocator()->get('VuFind\Config')->get('config'); + return new Redi( + $config->OpenURL->url, + $sm->getServiceLocator()->get('VuFind\Http')->createClient() + ); + } +} \ No newline at end of file diff --git a/module/finc/src/finc/Resolver/Driver/Redi.php b/module/finc/src/finc/Resolver/Driver/Redi.php new file mode 100644 index 0000000000000000000000000000000000000000..cb38f91904fece39135eb8384fce249380e23099 --- /dev/null +++ b/module/finc/src/finc/Resolver/Driver/Redi.php @@ -0,0 +1,318 @@ +<?php +/** + * ReDi Link Resolver Driver + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015 + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Resolver_Drivers + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:link_resolver_drivers Wiki + */ +namespace finc\Resolver\Driver; +use DOMDocument; + +/** + * ReDi Link Resolver Driver + * + * @category VuFind2 + * @package Resolver_Drivers + * @author Gregor Gawol <gawol@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:link_resolver_drivers Wiki + */ +class Redi implements \VuFind\Resolver\Driver\DriverInterface +{ + /** + * Base URL for link resolver + * + * @var string + */ + protected $baseUrl; + + /** + * HTTP client + * + * @var \Zend\Http\Client + */ + protected $httpClient; + + /** + * Base URL for link resolver + * + * @var string + */ + protected $doc; + + /** + * Constructor + * + * @param string $baseUrl Base URL for link resolver + * @param \Zend\Http\Client $httpClient HTTP client + */ + public function __construct($baseUrl, \Zend\Http\Client $httpClient) + { + $this->baseUrl = $baseUrl; + $this->httpClient = $httpClient; + } + + /** + * Fetch Links + * + * Fetches a set of links corresponding to an OpenURL + * + * @param string $openURL openURL (url-encoded) + * + * @return string raw XML returned by resolver + */ + public function fetchLinks($openURL) + { + $url = $this->baseUrl.'&'.$openURL; + $feed = $this->httpClient->setUri($url)->send()->getBody(); + return $feed; + } + + + /** + * Parse Links + * + * Parses an XML file returned by a link resolver + * and converts it to a standardised format for display + * + * @param string $xmlstr Raw XML returned by resolver + * + * @return array Array of values + */ + public function parseLinks($xmlstr) + { + $retval = array(); + $xml = new DOMDocument(); + if (!@$xml->loadHTML($xmlstr)) { + return $retval; + } + + $this->parseDOI($xml, $retval); + $this->parseRediInfo($xml, $retval); + $this->parseRediOpenURLs($xml, $retval); + + return $retval; + } + + /** + * Parse if the Redi xml snippet contains a DOI. + * + * @param DOMDocument $xml Loaded xml document + * @param array &$retval Get back a array with title, URL and service_type + * + * @return void + * @access private + */ + protected function parseDOI ($xml, &$retval) + { + $citation = $xml->getElementById('citation'); + if (is_object($citation->childNodes)) { + foreach ($citation->childNodes as $deflist) { + if (is_object($deflist->childNodes)) { + foreach ($deflist->childNodes as $defterm) { + $tmp = []; + if ($defterm->hasAttributes()) { + $elem = $defterm->getAttribute('class'); + if ($elem == 'doi_t') { + $doiText = trim($defterm->nodeValue); + $tmp['title'] = $doiText; + } + if ($elem == 'doi_d') { + $doiURL = trim($this->getRediLink($defterm)); + $tmp['href'] = $doiURL; + } + } + $tmp['service_type'] = 'getDOI'; + + if (!empty($tmp['title']) && !empty($tmp['href'])) { + $retval[] = $tmp; + } + } + } + } + } + } + + /** + * Parse if the Redi xml snippet contains information about the Redi offer. + * + * @param DOMDocument $xml Loaded xml document + * @param array &$retval Return array with Redi catalogue information + * consisting of Text & Link. + * + * @return void + * @access protected + */ + protected function parseRediInfo($xml, &$retval) + { + if ($ezb = $xml->getElementById('t_ezb')) { + if (is_object($ezb->childNodes)) { + foreach ($ezb->childNodes as $divClassT) { + if (is_object($divClassT->childNodes)) { + foreach ($divClassT->childNodes as $nodes) { + // infotext + + if ($nodes->nodeName == 'p') { + $tmp = []; + $rediInfoText = trim($nodes->firstChild->nodeValue); + if ($this->isRediOpenURLsWithInfo($xml)) { + $rediInfoInfo = trim($nodes->nodeValue); + } + $rediInfoURL = trim($this->getRediLink($nodes)); + if (is_object($nodes->childNodes)) { + foreach ($nodes->childNodes as $bold) { + if ($bold->nodeName == 'b') { + $rediInfoText = $rediInfoText + .' '.trim($bold->nodeValue); + } + } + } + $tmp['title'] = (isset($rediInfoText)? + $rediInfoText:''); + $tmp['href'] = (isset($rediInfoURL)? + $rediInfoURL:''); + $tmp['info'] = (isset($rediInfoInfo)? + $rediInfoInfo:''); + $tmp['service_type'] = 'getHolding'; + + if (!empty($tmp['title']) && !empty($tmp['href'])) { + $retval[] = $tmp; + } + } + } + } + } + } + } + } + + /** + * Parse if the Redi xml snippet contains Redi urls. + * + * @param DOMDocument $xml Loaded xml document + * @param array &$retval Get back Redi direct link to sources + * containing title, URL and service_type + * + * @return void + * @access protected + */ + protected function parseRediOpenURLs($xml, &$retval) + { + if ($ezb = $xml->getElementById('t_ezb')) { + if (is_object($ezb->childNodes)) { + foreach ($ezb->childNodes as $divClassT) { + if (is_object($divClassT->childNodes)) { + foreach ($divClassT->childNodes as $nodes) { + $tmp = []; + // fulltext + if ($nodes->nodeName == 'div') { + $text = trim( + str_replace( + array('»', + chr(194).chr(160) + ), + array('', ''), + $nodes->nodeValue + ) + ); // hack to replace \u00a0 + $available = $nodes->getElementsByTagName('span'); + foreach ($available as $span) { + if ($span->hasAttributes()) { + $class = $span->getAttribute('class'); + if ($class == 't_link') { + $url = $this->getRediLink($nodes); + } + } + } + $tmp['title'] = (isset($text)?$text:''); + $tmp['href'] = (isset($url)?$url:''); + $tmp['service_type'] = 'getFullTxt'; + + if (!empty($tmp['title']) && !empty($tmp['href'])) { + $retval[] = $tmp; + } + } + } // end foreach + } // end if + } // end foreach + } // end if + } // end if + } + + /** + * Is a star in ReDi links text snippet + * + * @param DOMDocument $xml loaded xml document + * + * @return bool + * @access protected + */ + protected function isRediOpenURLsWithInfo($xml) + { + if ($ezb = $xml->getElementById('t_ezb')) { + if (is_object($ezb->childNodes)) { + foreach ($ezb->childNodes as $divClassT) { + if (is_object($divClassT->childNodes)) { + foreach ($divClassT->childNodes as $nodes) { + if ($nodes->nodeName == 'div') { + $text = trim( + str_replace( + array('»', + chr(194).chr(160) + ), + array('',''), + $nodes->nodeValue + ) + ); // hack to replace \u00a0 + if (preg_match('/.*(\*).*/', $text)) { + return true; + } + } + } // end foreach + } // end if + } // end foreach + } // end if + } // end if + return false; + } + + /** + * Get the ReDi links of a DOM document snippet. + * + * @param object $doc Document snippet from ReDi site + * + * @return string Url of ReDi + * @access protected + */ + protected function getRediLink($doc) + { + $hrefs = $doc->getElementsByTagName('a'); + foreach ($hrefs as $a) { + if ($a->hasAttributes()) { + return $a->getAttribute('href'); + } + } + return ''; + } +} \ No newline at end of file diff --git a/module/finc/src/finc/View/Helper/Root/Factory.php b/module/finc/src/finc/View/Helper/Root/Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..e0ad5a2c01bd0346e5dac764093113ee2be9f2a0 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/Factory.php @@ -0,0 +1,56 @@ +<?php +/** + * Factory for Root view helpers. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2014. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace finc\View\Helper\Root; +use Zend\ServiceManager\ServiceManager; + +/** + * Factory for Root view helpers. + * + * @category VuFind2 + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Factory +{ + + /** + * Construct the Record helper. + * + * @param ServiceManager $sm Service manager. + * + * @return Record + */ + public static function getRecord(ServiceManager $sm) + { + return new Record( + $sm->getServiceLocator()->get('VuFind\Config')->get('config') + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/Record.php b/module/finc/src/finc/View/Helper/Root/Record.php new file mode 100644 index 0000000000000000000000000000000000000000..9f5535a086fd49a5e493c2840329c6359cf30c2a --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/Record.php @@ -0,0 +1,72 @@ +<?php +/** + * Record driver view helper + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +namespace finc\View\Helper\Root; +use Zend\View\Exception\RuntimeException, Zend\View\Helper\AbstractHelper; + +/** + * Record driver view helper + * + * @category VuFind2 + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:developer_manual Wiki + */ +class Record extends \VuFind\View\Helper\Root\Record +{ + /** + * VuFind configuration + * + * @var \Zend\Config\Config + */ + protected $config; + + /** + * Constructor + * + * @param \Zend\Config\Config $config VuFind configuration + */ + public function __construct($config = null) + { + parent::__construct($config); + } + + /** + * Render the link of the type ISN. + * + * @param array $issns Array with ISSNS + * + * @return string + */ + public function getLinkISN($issns) + { + return $this->renderTemplate( + 'link-isn.phtml', array('issns' => $issns) + ); + } +} \ No newline at end of file diff --git a/themes/finc/js/openurl.js b/themes/finc/js/openurl.js new file mode 100644 index 0000000000000000000000000000000000000000..3a11b23995b4a50d86d6f781db3cfab3e61aa471 --- /dev/null +++ b/themes/finc/js/openurl.js @@ -0,0 +1,41 @@ +/*global extractClassParams, path*/ + +function loadResolverLinks(target, openUrl) { + target.addClass('ajax_availability'); + var url = path + '/AJAX/JSON?' + $.param({method:'getResolverLinks',openurl:openUrl}); + $.ajax({ + dataType: 'json', + url: url, + success: function(response) { + if (response.status == 'OK') { + target.removeClass('ajax_availability') + .empty().append(response.data); + } else { + target.removeClass('ajax_availability').addClass('error') + .empty().append(response.data); + } + } + }); +} + +var redi = { + init: function(doc){ + var params = extractClassParams(doc); + var openUrl = $(doc).children('span.openUrl:first').attr('title'); + $(doc).hide(); + loadResolverLinks($('#openUrlEmbed'+params.openurl_id).removeClass('hidden'), openUrl); + return false; + } +} + +$(document).ready(function() { + // assign action to the openUrlWindow link class + $('a.openUrlWindow').click(function(){ + var params = extractClassParams(this); + var settings = params.window_settings; + window.open($(this).attr('href'), 'openurl', settings); + return false; + }); + + redi.init($('a.openUrlEmbed')); +}); \ No newline at end of file diff --git a/themes/finc/templates/RecordDriver/SolrAI/core.phtml b/themes/finc/templates/RecordDriver/SolrAI/core.phtml new file mode 100644 index 0000000000000000000000000000000000000000..fc36192f92ae7c7b3996e9a7741533a98e85c058 --- /dev/null +++ b/themes/finc/templates/RecordDriver/SolrAI/core.phtml @@ -0,0 +1,264 @@ +<div class="row" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> + <div class="col-sm-3"> + <div class="text-center"> + <? /* Display thumbnail if appropriate: */ ?> + <? $mediumThumb = $this->record($this->driver)->getThumbnail('medium'); $largeThumb = $this->record($this->driver)->getThumbnail('large'); ?> + <? if ($mediumThumb): ?> + <? if ($largeThumb): ?><a href="<?=$this->escapeHtmlAttr($largeThumb)?>"><? endif; ?> + <img alt="<?=$this->transEsc('Cover Image')?>" class="recordcover" src="<?=$this->escapeHtmlAttr($mediumThumb);?>"/> + <? if ($largeThumb): ?></a><? endif; ?> + <? else: ?> + <img src="<?=$this->url('cover-unavailable')?>" class="recordcover" alt="<?=$this->transEsc('No Cover Image')?>"/> + <? endif; ?> + + <? /* Display qrcode if appropriate: */ ?> + <? $QRCode = $this->record($this->driver)->getQRCode("core"); ?> + <? if($QRCode): ?> + <span class="hidden-xs"> + <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> + </span> + <? endif; ?> + </div> + + <?=$this->record($this->driver)->getPreviews()?> + </div> + + <div class="col-sm-9"> + <h3 property="name"><?=$this->escapeHtml($this->driver->getTitle())?></h3> + + <? $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $summary[0] : false; ?> + <? if ($summary): ?> + <p><?=$this->truncate($summary, 300)?></p> + + <? if(strlen($summary) > 300): ?> + <p><a href='<?=$this->recordLink()->getTabUrl($this->driver, 'Description')?>#tabnav'><?=$this->transEsc('Full description')?></a></p> + <? endif; ?> + <? endif; ?> + + <? if ($this->userlist()->getMode() !== 'disabled'): ?> + <? /* Display the lists that this record is saved to */ ?> + <div class="savedLists hidden alert alert-info" id="savedLists"> + <strong><?=$this->transEsc("Saved in")?>:</strong> + </div> + <? endif; ?> + + <?/* Display Main Details */?> + <table class="table table-striped" summary="<?=$this->transEsc('Bibliographic Details')?>"> + <? $journalTitle = $this->driver->getContainerTitle(); if (!empty($journalTitle)): ?> + <tr> + <th><?=$this->transEsc('Journal Title')?>:</th> + <td> + <a href="<?=$this->record($this->driver)->getLink('journaltitle', $journalTitle)?>"><?=$this->escapeHtml($journalTitle)?></a> + <? $ref = $this->driver->getContainerReference(); if (!empty($ref)) { echo $this->escapeHtml($ref); } ?> + </td> + </tr> + <? endif; ?> + + <? $nextTitles = $this->driver->getNewerTitles(); $prevTitles = $this->driver->getPreviousTitles(); ?> + <? if (!empty($nextTitles)): ?> + <tr> + <th><?=$this->transEsc('New Title')?>: </th> + <td> + <? foreach($nextTitles as $field): ?> + <a href="<?=$this->record($this->driver)->getLink('title', $field)?>"><?=$this->escapeHtml($field)?></a><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? if (!empty($prevTitles)): ?> + <tr> + <th><?=$this->transEsc('Previous Title')?>: </th> + <td> + <? foreach($prevTitles as $field): ?> + <a href="<?=$this->record($this->driver)->getLink('title', $field)?>"><?=$this->escapeHtml($field)?></a><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $authors = $this->driver->getDeduplicatedAuthors(); ?> + <? if (isset($authors['main']) && !empty($authors['main'])): ?> + <tr> + <th><?=$this->transEsc('Main Author')?>: </th> + <td property="author"><a href="<?=$this->record($this->driver)->getLink('author', $authors['main'])?>"><?=$this->escapeHtml($authors['main'])?></a></td> + </tr> + <? endif; ?> + + <? if (isset($authors['corporate']) && !empty($authors['corporate'])): ?> + <tr> + <th><?=$this->transEsc('Corporate Author')?>: </th> + <td property="creator"><a href="<?=$this->record($this->driver)->getLink('author', $authors['corporate'])?>"><?=$this->escapeHtml($authors['corporate'])?></a></td> + </tr> + <? endif; ?> + + <? if (isset($authors['secondary']) && !empty($authors['secondary'])): ?> + <tr> + <th><?=$this->transEsc('Other Authors')?>: </th> + <td> + <? $i = 0; foreach ($authors['secondary'] as $field): ?><?=($i++ == 0)?'':', '?><span property="contributor"><a href="<?=$this->record($this->driver)->getLink('author', $field)?>"><?=$this->escapeHtml($field)?></a></span><? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $aidatain = $this->driver->getAIDataIn(); if (!empty($aidatain)): ?> + <tr> + <th><?=$this->transEsc('In')?>: </th> + <td> + <? $issns = $aidatain['issns']; if (!empty($issns)): ?> + <a href="<?=$this->record($this->driver)->getLinkISN($issns)?>"> + <? $jtitle = $aidatain['jtitle']; + if (!empty($jtitle)): ?><?=$this->escapeHtml($jtitle)?><? endif; ?> + </a><? endif; ?><? $volume = $aidatain['volume']; if (!empty($volume)): ?>, <?=$this->escapeHtml($volume) ?><? endif; ?><? $date = $aidatain['date']; if (!empty($date)): ?><? if (empty($volume)): ?>, <? endif; ?>(<?=$this->escapeHtml($date) ?>)<? endif; ?><? $issue = $aidatain['issue']; if (!empty($issue)): ?>, <?=$this->escapeHtml($issue) ?><? endif; ?><? $pages = $aidatain['pages']; if (!empty($pages)): ?>, <?=$this->transEsc('p.')?> <?=$this->escapeHtml($pages) ?><? endif; ?> + </td> + </tr> + <? endif; ?> + + <? $publications = $this->driver->getPublicationDetails(); if (!empty($publications)): ?> + <tr> + <th><?=$this->transEsc('Published')?>: </th> + <td> + <? foreach ($publications as $field): ?> + <span property="publisher" typeof="Organization"> + <? $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> + <span property="location"><?=$this->escapeHtml($pubPlace)?></span> + <? endif; ?> + <? $pubName = $field->getName(); if (!empty($pubName)): ?> + <span property="name"><?=$this->escapeHtml($pubName)?></span> + <? endif; ?> + </span> + <? $pubDate = $field->getDate(); if (!empty($pubDate)): ?> + <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> + <? endif; ?> + <br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $formats = $this->driver->getFormats(); if (!empty($formats)): ?> + <tr> + <th><?=$this->transEsc('Format')?>: </th> + <td><?=$this->record($this->driver)->getFormatList()?></td> + </tr> + <? endif; ?> + + <? $langs = $this->driver->getLanguages(); if (!empty($langs)): ?> + <tr> + <th><?=$this->transEsc('Language')?>: </th> + <td><? foreach ($langs as $lang): ?><?= $this->escapeHtml($lang)?><br/><? endforeach; ?></td> + </tr> + <? endif; ?> + + <? $edition = $this->driver->getEdition(); if (!empty($edition)): ?> + <tr> + <th><?=$this->transEsc('Edition')?>: </th> + <td property="bookEdition"><?=$this->escapeHtml($edition)?></td> + </tr> + <? endif; ?> + + <?/* Display series section if at least one series exists. */?> + <? $series = $this->driver->getSeries(); if (!empty($series)): ?> + <tr> + <th><?=$this->transEsc('Series')?>: </th> + <td> + <? foreach ($series as $field): ?> + <?/* Depending on the record driver, $field may either be an array with + "name" and "number" keys or a flat string containing only the series + name. We should account for both cases to maximize compatibility. */?> + <? if (is_array($field)): ?> + <? if (!empty($field['name'])): ?> + <a href="<?=$this->record($this->driver)->getLink('series', $field['name'])?>"><?=$this->escapeHtml($field['name'])?></a> + <? if (!empty($field['number'])): ?> + <?=$this->escapeHtml($field['number'])?> + <? endif; ?> + <br/> + <? endif; ?> + <? else: ?> + <a href="<?=$this->record($this->driver)->getLink('series', $field)?>"><?=$this->escapeHtml($field)?></a><br/> + <? endif; ?> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $subjects = $this->driver->getAllSubjectHeadings(); if (!empty($subjects)): ?> + <tr> + <th><?=$this->transEsc('Subjects')?>: </th> + <td> + <? foreach ($subjects as $field): ?> + <div class="subjectLine" property="keywords"> + <? $subject = ''; ?> + <? if(count($field) == 1) $field = explode('--', $field[0]); ?> + <? $i = 0; foreach ($field as $subfield): ?> + <?=($i++ == 0) ? '' : ' > '?> + <? $subject = trim($subject . ' ' . $subfield); ?> + <a class="backlink" title="<?=$this->escapeHtmlAttr($subject)?>" href="<?=$this->record($this->driver)->getLink('subject', $subject)?>"><?=trim($this->escapeHtml($subfield))?></a> + <? endforeach; ?> + </div> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <?/* + $openUrl = $this->driver->openURLActive('record') ? $this->driver->getOpenURL() : false; + // Account for replace_other_urls setting + $urls = ($openUrl && $this->driver->replaceURLsWithOpenURL()) ? array() : $this->record($this->driver)->getLinkDetails(); + ?> + <? if (!empty($urls) || $openUrl): ?> + <tr> + <th><?=$this->transEsc('Online Access')?>: </th> + <td> + <? foreach ($urls as $current): ?> + <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>"><?=$this->escapeHtml($current['desc'])?></a><br/> + <? endforeach; ?> + <? if ($openUrl): ?> + <?=$this->openUrl($openUrl)?><br/> + <? endif; ?> + </td> + </tr> + <? endif; */?> + + <? $recordLinks = $this->driver->getAllRecordLinks(); ?> + <? if(!empty($recordLinks)): ?> + <tr> + <th><?=$this->transEsc('Related Items')?>:</th> + <td> + <? foreach ($recordLinks as $recordLink): ?> + <?=$this->transEsc($recordLink['title'])?>: + <a href="<?=$this->recordLink()->related($recordLink['link'])?>"><?=$this->escapeHtml($recordLink['value'])?></a><br /> + <? endforeach; ?> + <? /* if we have record links, display relevant explanatory notes */ + $related = $this->driver->getRelationshipNotes(); + if (!empty($related)): ?> + <? foreach ($related as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + <? endif; ?> + </td> + </tr> + <? endif; ?> + + <? if ($this->usertags()->getMode() !== 'disabled'): ?> + <? $tagList = $this->driver->getTags(); ?> + <tr> + <th><?=$this->transEsc('Tags')?>: </th> + <td> + <span class="pull-right"> + <i class="fa fa-plus"></i> <a id="tagRecord" class="modal-link" href="<?=$this->recordLink()->getActionUrl($this->driver, 'AddTag')?>" title="<?=$this->transEsc('Add Tag')?>"><?=$this->transEsc('Add Tag')?></a> + </span> + <div id="tagList"> + <? if (count($tagList) > 0): ?> + <? $i = 0; foreach ($tagList as $tag): ?><?=($i++ == 0)?'':', '?><a href="<?=$this->url('tag-home')?>?lookfor=<?=urlencode($tag->tag)?>"><?=$this->escapeHtml($tag->tag)?></a> (<?=$this->escapeHtml($tag->cnt)?>)<? endforeach; ?> + <? else: ?> + <?=$this->transEsc('No Tags')?>, <?=$this->transEsc('Be the first to tag this record')?>! + <? endif; ?> + </div> + </td> + </tr> + <? endif; ?> + </table> + <?/* End Main Details */?> + </div> +</div> diff --git a/themes/finc/templates/RecordDriver/SolrAI/link-isn.phtml b/themes/finc/templates/RecordDriver/SolrAI/link-isn.phtml new file mode 100644 index 0000000000000000000000000000000000000000..80531d8f5f964e549318268a7c6cc8de69ef71a3 --- /dev/null +++ b/themes/finc/templates/RecordDriver/SolrAI/link-isn.phtml @@ -0,0 +1 @@ +<?=$this->url('search-results')?>?join=AND&bool0[]=AND&<? $issns = $this->issns; if (isset($issns)): ?><? foreach ($issns as $issn): ?>lookfor0[]=<?=$this->escapeHtml($issn)?>&type0[]=ISN&<? endforeach; ?><? endif; ?>sort=year&view=list diff --git a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml new file mode 100644 index 0000000000000000000000000000000000000000..925e7774845fe50bca385d51decc69df17a6f5de --- /dev/null +++ b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml @@ -0,0 +1,193 @@ +<div class="<?=$this->driver->supportsAjaxStatus()?'ajaxItem ':''?>col-xs-11"> + <div class="row"> + <div class="col-sm-2 col-xs-3 left"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueID())?>" class="hiddenId" /> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getResourceSource())?>" class="hiddenSource" /> + <a href="<?=$this->recordLink()->getUrl($this->driver)?>"> + <? if ($summThumb = $this->record($this->driver)->getThumbnail()): ?> + <img class="recordcover" src="<?=$this->escapeHtmlAttr($summThumb)?>" alt="<?=$this->transEsc('Cover Image')?>"/> + <? else: ?> + <img class="recordcover" src="<?=$this->url('cover-unavailable')?>" alt="<?=$this->transEsc('No Cover Image')?>"/> + <? endif; ?> + </a> + </div> + <div class="col-sm-7 col-xs-6 middle"> + <div> + <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title"> + <? + $summHighlightedTitle = $this->driver->getHighlightedTitle(); + $summTitle = $this->driver->getTitle(); + if (!empty($summHighlightedTitle)) { + echo $this->highlight($this->addEllipsis($summHighlightedTitle, $summTitle)); + } else if (!empty($summTitle)) { + echo $this->escapeHtml($this->truncate($summTitle, 180)); + } else { + echo $this->transEsc('Title not available'); + } + ?> + </a> + </div> + + <div> + <? if($this->driver->isCollection()): ?> + <?=implode('<br>', $this->driver->getSummary()); ?> + <? else: ?> + <? $summAuthors = $this->driver->getCombinedAuthors(); if (!empty($summAuthors)): ?> + <? foreach($summAuthors as $summAuthor) : ?> + <a href="<?=$this->record($this->driver)->getLink('author', $summAuthor)?>"><? + $summHighlightedAuthor = $this->driver->getHighlightedAuthor(); + echo !empty($summHighlightedAuthor) + ? $this->highlight($summHighlightedAuthor) + : $this->escapeHtml($summAuthor); + ?></a> + <? endforeach; ?> + <? endif; ?> + + <? $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); $placesOfPublication = $this->driver->getPlacesOfPublication(); ?> + <? if (!empty($journalTitle)): ?> + <?=!empty($summAuthor) ? '<br />' : ''?> + <?=/* TODO: handle highlighting more elegantly here */ $this->transEsc('Published in') . ' <a href="' . $this->record($this->driver)->getLink('journaltitle', str_replace(array('{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'), '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> + <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate) . ')' : ''?> + <? elseif (!empty($summDate)): ?> + <?=!empty($summAuthor) ? '<br />' : ''?> + <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate)?> + <? endif; ?> + <? $summInCollection = $this->driver->getContainingCollections(); if (!empty($summInCollection)): ?> + <? foreach ($summInCollection as $collId => $collText): ?> + <div> + <b><?=$this->transEsc("in_collection_label")?></b> + <a class="collectionLinkText" href="<?=$this->url('collection', array('id' => $collId))?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <?=$this->escapeHtml($collText)?> + </a> + </div> + <? endforeach; ?> + <? endif; ?> + <? endif; ?> + </div> + + <? if(!$this->driver->isCollection()): ?> + <? if ($snippet = $this->driver->getHighlightedSnippet()): ?> + <? if (!empty($snippet['caption'])): ?> + <strong><?=$this->transEsc($snippet['caption']) ?>:</strong> '; + <? endif; ?> + <? if (!empty($snippet['snippet'])): ?> + <span class="quotestart">“</span>...<?=$this->highlight($snippet['snippet']) ?>...<span class="quoteend">”</span><br/> + <? endif; ?> + <? endif; ?> + <? endif; ?> + + <? + /* Display information on duplicate records if available */ + $dedupData = $this->driver->getDedupData(); + if ($dedupData): ?> + <div class="dedupInformation"> + <? + $i = 0; + foreach ($dedupData as $source => $current) { + if (++$i == 1) { + ?><span class="currentSource"><a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc("source_$source", array(), $source)?></a></span><? + } else { + if ($i == 2) { + ?> <span class="otherSources">(<?=$this->transEsc('Other Sources')?>: <? + } else { + ?>, <? + } + ?><a href="<?=$this->recordLink()->getUrl($current['id'])?>"><?=$this->transEsc("source_$source", array(), $source)?></a><? + } + } + if ($i > 1) { + ?>)</span><? + }?> + </div> + <? endif; ?> + + <div class="callnumAndLocation ajax-availability hidden"> + <? if ($this->driver->supportsAjaxStatus()): ?> + <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong> + <span class="callnumber ajax-availability hidden"> + <?=$this->transEsc('Loading')?>...<br/> + </span> + <strong><?=$this->transEsc('Located')?>:</strong> + <span class="location ajax-availability hidden"> + <?=$this->transEsc('Loading')?>... + </span> + <div class="locationDetails"></div> + <? else: ?> + <? $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> + <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> + <? endif; ?> + <? endif; ?> + </div> + + <? /* We need to find out if we're supposed to display an OpenURL link ($openUrlActive), + but even if we don't plan to display the link, we still want to get the $openUrl + value for use in generating a COinS (Z3988) tag -- see bottom of file. + */ + $openUrl = $this->driver->getOpenURL(); + $openUrlActive = $this->driver->openURLActive('results'); + $urls = $this->record($this->driver)->getLinkDetails(); + if ($openUrlActive || !empty($urls)): ?> + <? if ($openUrlActive): ?> + <br/> + <?=$this->openUrl($openUrl)?> + <? if ($this->driver->replaceURLsWithOpenURL()) $urls = array(); // clear URL list if replace setting is active ?> + <? endif; ?> + <? if (!is_array($urls)) $urls = array(); + 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"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a><br/> + <? endforeach; ?> + <? endif; ?> + <? endif; ?> + + <?=str_replace('class="', 'class="label label-info ', $this->record($this->driver)->getFormatList())?> + + <? if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> + <span class="status ajax-availability hidden"> + <span class="label label-default"><?=$this->transEsc('Loading')?>...</span> + </span> + <? endif; ?> + <?=$this->record($this->driver)->getPreviews()?> + </div> + <div class="col-xs-3 right hidden-print"> + <? /* Display qrcode if appropriate: */ ?> + <? if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> + <? + // Add JS Variables for QrCode + $this->jsTranslations()->addStrings(array('qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show')); + ?> + <span class="hidden-xs"> + <i class="fa fa-qrcode"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> + <div class="qrcode hidden"> + <img alt="<?=$this->transEsc('QR Code')?>" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> + </div><br/> + </span> + <? endif; ?> + + <? if ($this->userlist()->getMode() !== 'disabled'): ?> + <? /* Add to favorites */ ?> + <i class="fa fa-heart"></i> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" class="save-record modal-link" id="<?=$this->driver->getUniqueId() ?>" title="<?=$this->transEsc('Add to favorites')?>"><?=$this->transEsc('Add to favorites')?></a><br/> + + <? /* Saved lists */ ?> + <div class="savedLists alert alert-info hidden"> + <strong><?=$this->transEsc("Saved in")?>:</strong> + </div> + <? endif; ?> + + <? /* Hierarchy tree link */ ?> + <? $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> + <? foreach ($trees as $hierarchyID => $hierarchyTitle): ?> + <div class="hierarchyTreeLink"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> + <i class="fa fa-sitemap"></i> + <a class="hierarchyTreeLinkText modal-link" href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> + <?=$this->transEsc('hierarchy_view_context')?><? if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><? endif; ?> + </a> + </div> + <? endforeach; ?> + <? endif; ?> + + <?=$openUrl?'<span class="Z3988" title="'.$this->escapeHtmlAttr($openUrl).'"></span>':''?> + </div> + </div> +</div> \ No newline at end of file diff --git a/themes/finc/templates/RecordTab/description.phtml b/themes/finc/templates/RecordTab/description.phtml new file mode 100644 index 0000000000000000000000000000000000000000..2d682adf1b69d0fa6d3e9ae6dcd6001addef7ab1 --- /dev/null +++ b/themes/finc/templates/RecordTab/description.phtml @@ -0,0 +1,242 @@ +<? + // Set page title. + $this->headTitle($this->translate('Description') . ': ' . $this->driver->getBreadcrumb()); + + // Grab clean ISBN for convenience: + $isbn = $this->driver->getCleanISBN(); +?> +<table class="table table-striped" summary="<?=$this->transEsc('Description')?>"> + <? $summ = $this->driver->getSummary(); if (!empty($summ)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Summary')?>: </th> + <td> + <? foreach ($summ as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $dateSpan = $this->driver->getDateSpan(); if (!empty($dateSpan)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Published')?>: </th> + <td> + <? foreach ($dateSpan as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $notes = $this->driver->getGeneralNotes(); if (!empty($notes)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Item Description')?>: </th> + <td> + <? foreach ($notes as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $physical = $this->driver->getPhysicalDescriptions(); if (!empty($physical)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Physical Description')?>: </th> + <td> + <? foreach ($physical as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $freq = $this->driver->getPublicationFrequency(); if (!empty($freq)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Publication Frequency')?>: </th> + <td> + <? foreach ($freq as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $playTime = $this->driver->getPlayingTimes(); if (!empty($playTime)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Playing Time')?>: </th> + <td> + <? foreach ($playTime as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $system = $this->driver->getSystemDetails(); if (!empty($system)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Format')?>: </th> + <td> + <? foreach ($system as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $audience = $this->driver->getTargetAudienceNotes(); if (!empty($audience)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Audience')?>: </th> + <td> + <? foreach ($audience as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $awards = $this->driver->getAwards(); if (!empty($awards)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Awards')?>: </th> + <td> + <? foreach ($awards as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $credits = $this->driver->getProductionCredits(); if (!empty($credits)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Production Credits')?>: </th> + <td> + <? foreach ($credits as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $bib = $this->driver->getBibliographyNotes(); if (!empty($bib)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Bibliography')?>: </th> + <td> + <? foreach ($bib as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $isbns = $this->driver->getISBNs(); if (!empty($isbns)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('ISBN')?>: </th> + <td> + <? foreach ($isbns as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $issns = $this->driver->getISSNs(); if (!empty($issns)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('ISSN')?>: </th> + <td> + <? foreach ($issns as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $eissns = $this->driver->getEISSNs(); if (!empty($eissns)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('EISSN')?>: </th> + <td> + <? foreach ($eissns as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $related = $this->driver->getRelationshipNotes(); if (!empty($related)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Related Items')?>: </th> + <td> + <? foreach ($related as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $access = $this->driver->getAccessRestrictions(); if (!empty($access)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Access')?>: </th> + <td> + <? foreach ($access as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $findingAids = $this->driver->getFindingAids(); if (!empty($findingAids)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Finding Aid')?>: </th> + <td> + <? foreach ($findingAids as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $publicationPlaces = $this->driver->getHierarchicalPlaceNames(); if (!empty($publicationPlaces)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Publication_Place')?>: </th> + <td> + <? foreach ($publicationPlaces as $field): ?> + <?=$this->escapeHtml($field)?><br/> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? $authorNotes = empty($isbn) ? array() : $this->authorNotes($isbn); if (!empty($authorNotes)): ?> + <? $contentDisplayed = true; ?> + <tr> + <th><?=$this->transEsc('Author Notes')?>: </th> + <td> + <? foreach ($authorNotes as $provider => $list): ?> + <? foreach ($list as $field): ?> + <?=$field['Content']?><br/> + <? endforeach; ?> + <? endforeach; ?> + </td> + </tr> + <? endif; ?> + + <? if (!isset($contentDisplayed) || !$contentDisplayed): // Avoid errors if there were no rows above ?> + <tr><td><?=$this->transEsc('no_description')?></td></tr> + <? endif; ?> +</table> diff --git a/themes/finc/templates/RecordTab/staffviewarray.phtml b/themes/finc/templates/RecordTab/staffviewarray.phtml new file mode 100644 index 0000000000000000000000000000000000000000..03cee1701d7610c624fdb9b9a1b53cdc44265f66 --- /dev/null +++ b/themes/finc/templates/RecordTab/staffviewarray.phtml @@ -0,0 +1,23 @@ +<? + // Set page title. + $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); +?> +<table class="citation table table-striped"> + <? foreach ($this->driver->getRawData() as $data): ?> + <tr> + <th><?=$this->escapeHtml($data['key'])?></th> + <td> + <? if (!is_array($data['value'])) { $data['value'] = array($data['value']); } ?> + <? foreach ($data['value'] as $values): ?> + <? if (is_array($values)): ?> + <? foreach ($values as $value): ?> + <?=$this->escapeHtml($value)?><br /> + <? endforeach; ?> + <? else: ?> + <?=$this->escapeHtml($values)?><br /> + <? endif; ?> + <? endforeach; ?> + </td> + </tr> + <? endforeach; ?> +</table> \ No newline at end of file diff --git a/themes/finc/templates/ajax/resolverLinks.phtml b/themes/finc/templates/ajax/resolverLinks.phtml new file mode 100644 index 0000000000000000000000000000000000000000..7f1d204a28e9cd79139b0674dea54dced5cc4977 --- /dev/null +++ b/themes/finc/templates/ajax/resolverLinks.phtml @@ -0,0 +1,51 @@ +<div> + <? if (!empty($this->electronic)): ?> + <div class="openurls"> + <strong><?=$this->transEsc('Electronic')?></strong> + <ul> + <? foreach ($this->electronic as $link): ?> + <li> + <? if (isset($link['href']) && !empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <? else: ?> + <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <? endif; ?> + <? if (isset($link['info']) && !empty($link['info'])): ?> + <?=$this->escapeHtml($link['info'])?> + <? endif; ?> + </li> + <? endforeach; ?> + </ul> + </div> + <? endif; ?> + <? if (!empty($this->print)): ?> + <div class="openurls"> + <strong><?=$this->transEsc('Holdings')?></strong> + <ul> + <? foreach ($this->print as $link): ?> + <li> + <? if (isset($link['href']) && !empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <? else: ?> + <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> + <? endif; ?> + </li> + <? endforeach; ?> + </ul> + </div> + <? endif; ?> + <div class="openurls"> + <strong><a href="<?=$this->escapeHtmlAttr($this->openUrlBase)?>?<?=$this->escapeHtmlAttr($this->openUrl)?>"><?=$this->transEsc('More options')?></a></strong> + <? if (!empty($this->services)): ?> + <ul> + <? foreach ($this->services as $link): ?> + <? if (isset($link['href']) && !empty($link['href'])): ?> + <li> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> + </li> + <? endif; ?> + <? endforeach; ?> + </ul> + <? endif; ?> + </div> +</div> diff --git a/themes/finc/theme.config.php b/themes/finc/theme.config.php index 018a473aeb6395abb5ed5a8c63dbb2661fd46f33..a3a8cd1765393b8df953682dd68c8d024f46ac89 100644 --- a/themes/finc/theme.config.php +++ b/themes/finc/theme.config.php @@ -1,4 +1,9 @@ <?php return array( - 'extends' => 'bootstrap3' + 'extends' => 'bootstrap3', + 'helpers' => array( + 'factories' => array( + 'record' => 'finc\View\Helper\Root\Factory::getRecord' + ), + ), ); diff --git a/util/synclangfiles.php b/util/synclangfiles.php new file mode 100644 index 0000000000000000000000000000000000000000..955f86a6450c3a25d1d3010845c96d393e66dc58 --- /dev/null +++ b/util/synclangfiles.php @@ -0,0 +1,37 @@ +<?php +/** + * This tool allows to compare and synchronize language files for VuFind2. + * You can either run it as CLI-tool with output on STDOUT to check whether + * language files are out of sync (default) or synchronize language files + * automatically by writing the missing lines into the .ini files. + * + * For more available options see run php synclangfiles.php --help + * + * PHP version 5 + * + * Copyright (C) Leipzig University Library 2015. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Utilities + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki Wiki + */ + +// Load the Zend framework -- this will automatically trigger the appropriate +// controller action based on directory and file names +define('CLI_DIR', __DIR__); // save directory name of current script +require_once __DIR__ . '/../public/index.php';