diff --git a/.gitignore b/.gitignore index 3a1e5cc52598a02ff651d7c267a4d1ef273d3050..20cbba2092d2087678ed01049b8e43ee56db06a2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ ChangeLog \#* .\#* .php_cs_cache +.vagrant +.vscode/* TAGS *~ /data/.languages diff --git a/.travis.yml b/.travis.yml index d990cf834471e84db73561f3ad4e2d63202c5068..57cecee724f0c45e7a9230546a59c2de309b87b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,10 @@ sudo: false language: php php: - - 7.0 - 7.1 - 7.2 + - 7.3 + - 7.4 env: - VUFIND_HOME=$PWD VUFIND_LOCAL_DIR=$PWD/local @@ -14,6 +15,7 @@ before_script: - composer install - npm install -g eslint@"<5.0.0" - npm install -g jshint@"2.9.6" + - npm install cache: directories: @@ -22,4 +24,4 @@ cache: - $HOME/.composer/cache/files script: - - vendor/bin/phing eslint jshint phpunitfast phpcs-console php-cs-fixer-dryrun + - vendor/bin/phing eslint jshint phpunitfast phpcs-console php-cs-fixer-dryrun checkLessToSass diff --git a/Gruntfile.js b/Gruntfile.js index 7b8ac2951f9ce5a38d30859c7e1886da4113a946..6447608ad69a6d133bb2fcd0b450eacf49e2f7cf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -244,4 +244,17 @@ module.exports = function(grunt) { grunt.task.run('sass'); }); grunt.registerTask('default', ['scss']); + + grunt.registerTask('help', function help() { + grunt.log.writeln(`\nHello! Here are your grunt command options: + + - grunt less = compile and compress all themes' LESS files to css. + - grunt scss = compile and map all themes' SASS files to css. + - grunt lessdev = compile and map all themes' LESS files to css. + - grunt watch:[cmd] = continuous monitor source files and run command when changes are detected. + - grunt watch:less + - grunt watch:scss + - grunt watch:lessdev + - grunt lessToSass = transpile all LESS files to SASS.`); + }); }; diff --git a/Vagrantfile b/Vagrantfile index 44d8b801dfea7a213a79d21e695b9e76b7a6ac26..426e9a9ba7c21594da1d737c65e51ee450301784 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -8,8 +8,8 @@ Vagrant.configure("2") do |config| # https://docs.vagrantup.com. # Every Vagrant development environment requires a box. You can search for - # boxes at https://atlas.hashicorp.com/search. - config.vm.box = "ubuntu/xenial64" + # boxes at https://app.vagrantup.com/boxes/search + config.vm.box = "ubuntu/bionic64" # Provider-specific configuration so you can fine-tune various # backing providers for Vagrant. These expose provider-specific options. @@ -18,7 +18,7 @@ Vagrant.configure("2") do |config| config.vm.provider "virtualbox" do |vb| # Display the VirtualBox GUI when booting the machine # vb.gui = true - + # Customize the amount of resources on the VM: vb.cpus = 2 vb.memory = "2048" @@ -28,22 +28,23 @@ Vagrant.configure("2") do |config| config.vm.network :forwarded_port, guest: 80, host: 4567 config.vm.network :forwarded_port, guest: 8080, host: 4568 config.vm.synced_folder ".", "/vagrant", :owner => 'ubuntu' - + # Enable provisioning with a shell script. Additional provisioners such as # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the # documentation for more information about their specific syntax and use. config.vm.provision "shell", inline: <<-SHELL - + # Configure mysql-server package credential values. # NOTE: Please don't use these default values in any situation where security is a concern. debconf-set-selections <<< 'mysql-server mysql-server/root_password password root' debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password root' # Install package dependencies. + export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y git zip unzip apache2 default-jdk mysql-server apt-get install -y libapache2-mod-php php-mbstring php-pear php php-dev php-gd php-intl php-json php-ldap php-mysql php-soap php-xml php-curl - + # Install composer. php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('SHA384', 'composer-setup.php') === trim(file_get_contents('https://composer.github.io/installer.sig'))) { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" diff --git a/build.xml b/build.xml index 1d3805b9d30c14d6e28a474d3feb2fae528925bc..c9441af6e0cca8573b22d8b461664675a562d228 100644 --- a/build.xml +++ b/build.xml @@ -27,9 +27,10 @@ <property name="snooze_multiplier" value="1" /><!-- can be used to slow down tests (selenium only) --> <property name="solr_startup_sleep" value="0" /> <property name="solr_version" value="7.3.1" /> - <property name="phpdoc_version" value="2.9.0" /> + <property name="skip_phpdoc" value="false" /> + <property name="phpdoc_version" value="3.0.0-alpha.4" /> - <property name="version" value="5.1.1" /> + <property name="version" value="6.1.2" /> <!-- We only need the -p switch if the password is non-blank --> <if><not><equals arg1="${mysqlrootpass}" arg2="" /></not><then> @@ -131,32 +132,45 @@ <exec command="jshint --config=${srcdir}/tests/jshint.json --exclude=themes/*/js/vendor --reporter=checkstyle ${srcdir}/themes > ${builddir}/reports/jshint-checkstyle.xml" /> </target> + <!-- Run LessToSass, error if there are uncommitted changes (used by Travis) --> + <target name="checkLessToSass"> + <exec command="grunt lessToSass" checkreturn="true" passthru="true" /> + <exec command="git diff --exit-code *.scss" checkreturn="true" passthru="true" /> + </target> + <!-- PHP API Documentation --> <target name="phpdoc"> - <!-- GET phpDocumentor.phar --> + <!-- Skip the whole phpdoc task when disabled --> <if> - <not><available file="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" /></not> + <not><istrue value="${skip_phpdoc}" /></not> <then> - <httpget followRedirects="true" url="https://github.com/phpDocumentor/phpDocumentor2/releases/download/v${phpdoc_version}/phpDocumentor.phar" dir="${srcdir}/vendor/bin" filename="phpDocumentor-${phpdoc_version}.phar" /> - <chmod mode="0755"> - <fileset dir="${srcdir}/vendor/bin"> - <include name="phpDocumentor-${phpdoc_version}.phar" /> + <!-- GET phpDocumentor.phar --> + <if> + <not><available file="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" /></not> + <then> + <httpget followRedirects="true" url="https://github.com/phpDocumentor/phpDocumentor2/releases/download/v${phpdoc_version}/phpDocumentor.phar" dir="${srcdir}/vendor/bin" filename="phpDocumentor-${phpdoc_version}.phar" /> + <chmod mode="0755"> + <fileset dir="${srcdir}/vendor/bin"> + <include name="phpDocumentor-${phpdoc_version}.phar" /> + </fileset> + </chmod> + </then> + </if> + <!-- Run phpdoc --> + <mkdir dir="${builddir}/apidocs" /> + <mkdir dir="${builddir}/docs_cache" /> + <!-- Old embedded version; no longer works correctly... + <phpdoc2 title="VuFind API Documentation" + pharlocation="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" + destdir="${builddir}/apidocs"> + <fileset dir="."> + <include name="module/*/src/**/*.php" /> </fileset> - </chmod> + </phpdoc2> + --> + <exec command="php ${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar --cache-folder=${builddir}/docs_cache --title="VuFind API Documentation" -t ${builddir}/apidocs -d ${srcdir}/module" passthru="true" /> </then> </if> - <!-- Run phpdoc --> - <mkdir dir="${builddir}/apidocs" /> - <!-- Old embedded version; no longer works correctly... - <phpdoc2 title="VuFind API Documentation" - pharlocation="${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar" - destdir="${builddir}/apidocs"> - <fileset dir="."> - <include name="module/*/src/**/*.php" /> - </fileset> - </phpdoc2> - --> - <exec command="php ${srcdir}/vendor/bin/phpDocumentor-${phpdoc_version}.phar --title="VuFind API Documentation" -t ${builddir}/apidocs -d ${srcdir}/module" passthru="true" /> </target> <!-- PHPUnit --> @@ -169,6 +183,11 @@ <exec dir="${srcdir}/module/VuFind/tests" command="VUFIND_MINK_DRIVER=${mink_driver} VUFIND_SELENIUM_BROWSER=${selenium_browser} VUFIND_SNOOZE_MULTIPLIER=${snooze_multiplier} VUFIND_LOCAL_DIR=${srcdir}/local VUFIND_URL=${vufindurl} ${srcdir}/vendor/bin/phpunit -dzend.enable_gc=0 ${phpunit_extra_params}" passthru="true" checkreturn="true" /> </target> + <!-- PHPUnit without logging output, stopping at first error or failure --> + <target name="phpunitfaster" description="Run tests until first failure"> + <exec dir="${srcdir}/module/VuFind/tests" command="VUFIND_MINK_DRIVER=${mink_driver} VUFIND_SELENIUM_BROWSER=${selenium_browser} VUFIND_SNOOZE_MULTIPLIER=${snooze_multiplier} VUFIND_LOCAL_DIR=${srcdir}/local VUFIND_URL=${vufindurl} ${srcdir}/vendor/bin/phpunit -dzend.enable_gc=0 --stop-on-failure ${phpunit_extra_params}" passthru="true" checkreturn="true" /> + </target> + <target name="installsolr" description="Install Solr"> <!-- load previously installed version from marker file, if present --> <if> @@ -236,9 +255,6 @@ <!-- Install and Activate VuFind --> <target name="startup" description="install and activate demo"> - <!-- get dependencies --> - <phingcall target="composer" /> - <!-- set up appropriate read/write permissions for Apache --> <exec command="chmod -R a+w ${srcdir}/local/cache" /> @@ -284,7 +300,9 @@ <!-- build database --> <exec command="mysqladmin -f -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} drop ${vufinddb}" /> <exec command="mysqladmin -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} create ${vufinddb}" checkreturn="true" /> - <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "GRANT SELECT,INSERT,UPDATE,DELETE ON ${vufinddb}.* TO '${vufinddbuser}'@'${mysqlhost}' IDENTIFIED BY '${vufinddbpass}' WITH GRANT OPTION"" checkreturn="true" /> + <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "DROP USER '${vufinddbuser}'@'${mysqlhost}'"" /> + <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "CREATE USER '${vufinddbuser}'@'${mysqlhost}' IDENTIFIED BY '${vufinddbpass}'"" checkreturn="true" /> + <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "GRANT SELECT,INSERT,UPDATE,DELETE ON ${vufinddb}.* TO '${vufinddbuser}'@'${mysqlhost}' WITH GRANT OPTION"" checkreturn="true" /> <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "FLUSH PRIVILEGES"" checkreturn="true" /> <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -D ${vufinddb} < ${srcdir}/module/VuFind/sql/mysql.sql" checkreturn="true" /> @@ -293,12 +311,13 @@ </else> </if> - <!-- Update config.ini to activate DB connection and exception logging --> + <!-- Update config.ini to activate DB connection, exception logging and test mode --> <copy file="${srcdir}/config/vufind/config.ini" tofile="${srcdir}/local/config/vufind/config.ini"> <filterchain> <replaceregexp> <regexp pattern="mysql://root@localhost/vufind" replace="${db_connection_string}" /> <regexp pattern=";file\s+= /var/log/vufind.log:alert,error,notice,debug" replace="file = ${srcdir}/vufind-exception.log:alert-5,error-5" /> + <regexp pattern="(\[System\]\s+)" replace="\1runningTestSuite=1" /> </replaceregexp> </filterchain> </copy> @@ -352,6 +371,7 @@ <exec command="sudo su -c "psql -c \"DROP USER ${vufinddbuser};\"" ${pgsqlrootuser}" checkreturn="true" /> </then> <else> + <exec command="mysql -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} -e "DROP USER '${vufinddbuser}'@'${mysqlhost}'"" /> <exec command="mysqladmin -f -h ${mysqlhost} -u ${mysqlrootuser} ${mysqlpwswitch}${mysqlrootpass} drop ${vufinddb}" /> </else> </if> diff --git a/composer.json b/composer.json index 7a7d7fdde1791e84a764bb5a3720eeb710b902f6..e086f4d8d97736b241bb9f099c8bb38e49587a75 100644 --- a/composer.json +++ b/composer.json @@ -8,76 +8,84 @@ } ], "license": "GPL-2.0", + "config": { + "platform": { + "php": "7.1" + } + }, "require": { - "php": ">=7.0.8", + "php": ">=7.1", "ahand/mobileesp": "dev-master", "cap60552/php-sip2": "1.0.0", - "endroid/qr-code": "2.5.0", + "colinmollenhour/credis": "1.11.1", + "endroid/qr-code": "3.5.9", "ghislainf/zf2-whoops": "dev-master#2649cf7caf400409942ddc3f8fe15b89381fc74e", - "jasig/phpcas": "1.3.6", - "matthiasmullie/minify": "1.3.61", - "ocramius/proxy-manager": "2.0.4", + "jasig/phpcas": "1.3.8", + "matthiasmullie/minify": "1.3.62", + "misd/linkify": "1.1.4", + "ocramius/proxy-manager": "2.1.1", "oyejorge/less.php": "1.7.0.14", "pear/archive_tar": "^1.4", - "pear/file_marc": "1.2.0", + "pear/file_marc": "1.4.1", "pear/http_request2": "2.3.0", "pear/validate_ispn": "dev-master", - "phing/phing": "2.16.1", + "phing/phing": "2.16.2", "serialssolutions/summon": "1.2.0", - "symfony/yaml": "3.4.21", + "symfony/yaml": "3.4.36", "swagger-api/swagger-ui": "2.2.10", - "vufind-org/vufindcode": "1.1.1", + "vufind-org/vufindcode": "1.2", "vufind-org/vufinddate": "1.0.0", - "vufind-org/vufindharvest": "2.4.0", - "vufind-org/vufindhttp": "2.2.0", - "yajra/laravel-pdo-via-oci8": "1.3.7", - "zendframework/zend-cache": "2.8.2", - "zendframework/zend-captcha": "2.8.0", - "zendframework/zend-code": "3.1.0", - "zendframework/zend-config": "3.2.0", - "zendframework/zend-console": "2.7.0", - "zendframework/zend-crypt": "3.3.0", - "zendframework/zend-db": "2.9.3", - "zendframework/zend-dom": "2.7.1", - "zendframework/zend-escaper": "2.6.0", + "vufind-org/vufindharvest": "2.4.1", + "vufind-org/vufindhttp": "2.3.0", + "wikimedia/composer-merge-plugin": "1.4.1", + "yajra/laravel-pdo-via-oci8": "2.1.1", + "zendframework/zend-cache": "2.9.0", + "zendframework/zend-captcha": "2.9.0", + "zendframework/zend-code": "3.4.1", + "zendframework/zend-config": "3.3.0", + "zendframework/zend-console": "2.8.0", + "zendframework/zend-crypt": "3.3.1", + "zendframework/zend-db": "2.10.1", + "zendframework/zend-dom": "2.7.2", + "zendframework/zend-escaper": "2.6.1", "zendframework/zend-eventmanager": "3.2.1", - "zendframework/zend-feed": "2.10.3", - "zendframework/zend-filter": "2.9.1", - "zendframework/zend-form": "2.14.0", - "zendframework/zend-http": "2.9.1", - "zendframework/zend-i18n": "2.9.0", + "zendframework/zend-feed": "2.12.0", + "zendframework/zend-filter": "2.9.2", + "zendframework/zend-form": "2.14.3", + "zendframework/zend-http": "2.11.2", + "zendframework/zend-i18n": "2.10.1", "zendframework/zend-loader": "2.6.0", "zendframework/zend-log": "2.10.0", "zendframework/zend-mail": "2.10.0", - "zendframework/zend-modulemanager": "2.8.2", + "zendframework/zend-modulemanager": "2.8.4", "zendframework/zend-mvc": "3.1.1", "zendframework/zend-mvc-console": "1.2.0", - "zendframework/zend-mvc-i18n": "1.1.0", - "zendframework/zend-mvc-plugin-flashmessenger": "1.1.0", - "zendframework/zend-paginator": "2.8.1", - "zendframework/zend-serializer": "2.9.0", + "zendframework/zend-mvc-i18n": "1.1.1", + "zendframework/zend-mvc-plugin-flashmessenger": "1.2.0", + "zendframework/zend-paginator": "2.8.2", + "zendframework/zend-serializer": "2.9.1", "zendframework/zend-servicemanager": "3.4.0", - "zendframework/zend-session": "2.8.5", - "zendframework/zend-soap": "2.7.0", + "zendframework/zend-session": "2.9.1", + "zendframework/zend-soap": "2.8.0", "zendframework/zend-stdlib": "3.2.1", - "zendframework/zend-text": "2.7.0", - "zendframework/zend-validator": "2.11.0", - "zendframework/zend-view": "2.11.1", + "zendframework/zend-text": "2.7.1", + "zendframework/zend-validator": "2.13.0", + "zendframework/zend-view": "2.11.2", "zendframework/zendrest": "2.0.2", - "zendframework/zendservice-amazon": "2.3.0", - "zendframework/zendservice-recaptcha": "3.1.0", - "zf-commons/zfc-rbac": "2.6.3", - "wikimedia/composer-merge-plugin": "^1.4" + "zendframework/zendservice-amazon": "2.3.1", + "zendframework/zendservice-recaptcha": "3.2.0", + "zf-commons/zfc-rbac": "2.6.3" }, "require-dev": { "behat/mink": "1.7.1", "behat/mink-selenium2-driver": "1.3.1", - "friendsofphp/php-cs-fixer": "2.14.0", + "friendsofphp/php-cs-fixer": "2.16.1", "phploc/phploc": "4.0.1", - "phpmd/phpmd": "2.6.0", - "phpunit/phpunit": "6.5.13", - "sebastian/phpcpd": "3.0.1", - "squizlabs/php_codesniffer": "3.4.0" + "phpmd/phpmd": "2.8.1", + "phpunit/phpunit": "7.5.20", + "sebastian/phpcpd": "4.1.0", + "squizlabs/php_codesniffer": "3.5.3", + "dmore/chrome-mink-driver": "^2.7" }, "extra": { "merge-plugin": { diff --git a/composer.local.json.dist b/composer.local.json.dist new file mode 100644 index 0000000000000000000000000000000000000000..3bb250cceebb619c0a8b8f5585074f6d0a332ca8 --- /dev/null +++ b/composer.local.json.dist @@ -0,0 +1,10 @@ +{ + "__comment__": [ + "Rename this file from composer.local.json.dist to composer.local.json to" + "manage your local dependencies without changing the core composer.json." + "See https://github.com/wikimedia/composer-merge-plugin for more details." + ], + "require": { + "example/dependency": "1.0.0" + } +} diff --git a/composer.lock b/composer.lock index 4a05f1a74ff56088a648fbb9051c39f1ce937cc7..7c49da681a0b795a01227a1f8948753c4dce6541 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8f632938475a6265104ca49d4fe4539a", + "content-hash": "836f81172cac4113d8770470fc00d6ae", "packages": [ { "name": "ahand/mobileesp", @@ -62,32 +62,36 @@ }, { "name": "bacon/bacon-qr-code", - "version": "1.0.3", + "version": "2.0.7", "source": { "type": "git", "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "5a91b62b9d37cee635bbf8d553f4546057250bee" + "reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/5a91b62b9d37cee635bbf8d553f4546057250bee", - "reference": "5a91b62b9d37cee635bbf8d553f4546057250bee", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/d70c840f68657ce49094b8d91f9ee0cc07fbf66c", + "reference": "d70c840f68657ce49094b8d91f9ee0cc07fbf66c", "shasum": "" }, "require": { + "dasprid/enum": "^1.0.3", "ext-iconv": "*", - "php": "^5.4|^7.0" + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8" + "phly/keep-a-changelog": "^2.1", + "phpunit/phpunit": "^7 | ^8 | ^9", + "spatie/phpunit-snapshot-assertions": "^4.2.9", + "squizlabs/php_codesniffer": "^3.4" }, "suggest": { - "ext-gd": "to generate QR code images" + "ext-imagick": "to generate QR code images" }, "type": "library", "autoload": { - "psr-0": { - "BaconQrCode": "src/" + "psr-4": { + "BaconQrCode\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -98,13 +102,13 @@ { "name": "Ben Scholzen 'DASPRiD'", "email": "mail@dasprids.de", - "homepage": "http://www.dasprids.de", + "homepage": "https://dasprids.de/", "role": "Developer" } ], "description": "BaconQrCode is a QR code generator for PHP.", "homepage": "https://github.com/Bacon/BaconQrCode", - "time": "2017-10-17T09:59:25+00:00" + "time": "2022-03-14T02:02:36+00:00" }, { "name": "cap60552/php-sip2", @@ -141,6 +145,46 @@ "homepage": "https://github.com/cap60552/php-sip2", "time": "2015-11-03T04:42:39+00:00" }, + { + "name": "colinmollenhour/credis", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/credis.git", + "reference": "bd1da4698ab1918477f9e71e5ff0062b9a345008" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/bd1da4698ab1918477f9e71e5ff0062b9a345008", + "reference": "bd1da4698ab1918477f9e71e5ff0062b9a345008", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "Client.php", + "Cluster.php", + "Sentinel.php", + "Module.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Colin Mollenhour", + "email": "colin@mollenhour.com" + } + ], + "description": "Credis is a lightweight interface to the Redis key-value store which wraps the phpredis library when available for better performance.", + "homepage": "https://github.com/colinmollenhour/credis", + "time": "2019-11-26T18:09:45+00:00" + }, { "name": "container-interop/container-interop", "version": "1.2.0", @@ -173,34 +217,78 @@ "abandoned": "psr/container", "time": "2017-02-14T19:40:03+00:00" }, + { + "name": "dasprid/enum", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/DASPRiD/Enum.git", + "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/5abf82f213618696dda8e3bf6f64dd042d8542b2", + "reference": "5abf82f213618696dda8e3bf6f64dd042d8542b2", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "^7 | ^8 | ^9", + "squizlabs/php_codesniffer": "^3.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "DASPRiD\\Enum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Ben Scholzen 'DASPRiD'", + "email": "mail@dasprids.de", + "homepage": "https://dasprids.de/", + "role": "Developer" + } + ], + "description": "PHP 7.1 enum implementation", + "keywords": [ + "enum", + "map" + ], + "time": "2020-10-02T16:03:48+00:00" + }, { "name": "doctrine/annotations", - "version": "v1.4.0", + "version": "1.13.3", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + "reference": "648b0343343565c4a056bfc8392201385e8d89f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/648b0343343565c4a056bfc8392201385e8d89f0", + "reference": "648b0343343565c4a056bfc8392201385e8d89f0", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^1.4.10 || ^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2", + "vimeo/psalm": "^4.10" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" @@ -211,6 +299,10 @@ "MIT" ], "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { "name": "Roman Borschel", "email": "roman@code-factory.org" @@ -219,10 +311,6 @@ "name": "Benjamin Eberlei", "email": "kontakt@beberlei.de" }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, { "name": "Jonathan Wage", "email": "jonwage@gmail.com" @@ -233,50 +321,49 @@ } ], "description": "Docblock Annotations Parser", - "homepage": "http://www.doctrine-project.org", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ "annotations", "docblock", "parser" ], - "time": "2017-02-24T16:22:25+00:00" + "time": "2022-07-02T10:48:51+00:00" }, { "name": "doctrine/cache", - "version": "1.9.0", + "version": "1.13.0", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a" + "reference": "56cd022adb5514472cb144c087393c1821911d09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c15dcd24b756f9e52ea7c3ae8227354f3628f11a", - "reference": "c15dcd24b756f9e52ea7c3ae8227354f3628f11a", + "url": "https://api.github.com/repos/doctrine/cache/zipball/56cd022adb5514472cb144c087393c1821911d09", + "reference": "56cd022adb5514472cb144c087393c1821911d09", "shasum": "" }, "require": { - "php": "~7.1" + "php": "~7.1 || ^8.0" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { "alcaeus/mongo-php-adapter": "^1.1", - "doctrine/coding-standard": "^6.0", + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", "mongodb/mongodb": "^1.1", - "phpunit/phpunit": "^7.0", - "predis/predis": "~1.0" + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "predis/predis": "~1.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" }, "suggest": { "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.9.x-dev" - } - }, "autoload": { "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" @@ -319,37 +406,37 @@ "memcached", "php", "redis", - "riak", "xcache" ], - "time": "2019-11-11T10:31:52+00:00" + "time": "2022-05-20T20:06:54+00:00" }, { "name": "doctrine/lexer", - "version": "v1.0.1", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/c268e882d4dbdd85e36e4ad69e02dc284f89d229", + "reference": "c268e882d4dbdd85e36e4ad69e02dc284f89d229", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.1 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "doctrine/coding-standard": "^9.0", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.11" }, + "type": "library", "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" } }, "notification-url": "https://packagist.org/downloads/", @@ -357,65 +444,63 @@ "MIT" ], "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, { "name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com" }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ + "annotations", + "docblock", "lexer", - "parser" + "parser", + "php" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2022-02-28T11:07:21+00:00" }, { "name": "endroid/qr-code", - "version": "2.5.0", + "version": "3.5.9", "source": { "type": "git", "url": "https://github.com/endroid/qr-code.git", - "reference": "a9a57ab57ac75928fcdcfb2a71179963ff6fe573" + "reference": "ae14093866558c1d91aa1f9b0ca07c09a95effc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/endroid/qr-code/zipball/a9a57ab57ac75928fcdcfb2a71179963ff6fe573", - "reference": "a9a57ab57ac75928fcdcfb2a71179963ff6fe573", + "url": "https://api.github.com/repos/endroid/qr-code/zipball/ae14093866558c1d91aa1f9b0ca07c09a95effc7", + "reference": "ae14093866558c1d91aa1f9b0ca07c09a95effc7", "shasum": "" }, "require": { - "bacon/bacon-qr-code": "^1.0.3", + "bacon/bacon-qr-code": "^2.0", "ext-gd": "*", - "khanamiryan/qrcode-detector-decoder": "^1.0", + "khanamiryan/qrcode-detector-decoder": "^1.0.2", "myclabs/php-enum": "^1.5", - "php": ">=5.6", - "symfony/options-resolver": ">=2.7", - "symfony/property-access": ">=2.7" + "php": ">=7.1", + "symfony/options-resolver": "^2.7|^3.0|^4.0", + "symfony/property-access": "^2.7|^3.0|^4.0" }, "require-dev": { - "phpunit/phpunit": ">=5.7", - "symfony/asset": ">=2.7", - "symfony/browser-kit": ">=2.7", - "symfony/finder": ">=2.7", - "symfony/framework-bundle": ">=2.7", - "symfony/http-kernel": ">=2.7", - "symfony/templating": ">=2.7", - "symfony/twig-bundle": ">=2.7", - "symfony/yaml": ">=2.7" - }, - "type": "symfony-bundle", + "phpunit/phpunit": "^5.7|^6.0" + }, + "suggest": { + "symfony/http-foundation": "Install if you want to use QrCodeResponse" + }, + "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -430,45 +515,43 @@ "authors": [ { "name": "Jeroen van den Enden", - "email": "info@endroid.nl", - "homepage": "http://endroid.nl/" + "email": "info@endroid.nl" } ], "description": "Endroid QR Code", - "homepage": "https://github.com/endroid/QrCode", + "homepage": "https://github.com/endroid/qr-code", "keywords": [ "bundle", "code", "endroid", - "flex", + "php", "qr", - "qrcode", - "symfony" + "qrcode" ], - "time": "2017-10-22T18:56:00+00:00" + "time": "2019-11-15T12:32:01+00:00" }, { "name": "filp/whoops", - "version": "2.3.1", + "version": "2.14.5", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7" + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/bc0fd11bc455cc20ee4b5edabc63ebbf859324c7", - "reference": "bc0fd11bc455cc20ee4b5edabc63ebbf859324c7", + "url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0", - "psr/log": "^1.0.1" + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.35 || ^5.7", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0" + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -477,7 +560,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.7-dev" } }, "autoload": { @@ -506,7 +589,7 @@ "throwable", "whoops" ], - "time": "2018-10-23T09:00:00+00:00" + "time": "2022-01-07T12:00:00+00:00" }, { "name": "finc/rules-evaluator", @@ -544,11 +627,11 @@ }, { "name": "finc/symfony-serializer-zend-bridge", - "version": "v0.0.1", + "version": "v0.0.1.1", "source": { "type": "git", "url": "https://git.sc.uni-leipzig.de/ubl/finc/fid/symfony-serializer-zend-bridge.git", - "reference": "9b0c05c112c40a626f8bead49d9ad34b3916ce8c" + "reference": "bb5903e3d799f2b6af6aeb40c891b19d1117e968" }, "require": { "doctrine/annotations": "^1.4", @@ -573,10 +656,14 @@ { "name": "Sebastian Kehr", "email": "kehr@ub.uni-leipzig.de" + }, + { + "name": "Robert Lange", + "email": "lange@ub.uni-leipzig.de" } ], "description": "Reusable adapters for usage with zend-serializer.", - "time": "2019-05-07T12:27:13+00:00" + "time": "2021-11-11T13:20:31+00:00" }, { "name": "finc/vufindhttp-psrcompat", @@ -706,16 +793,16 @@ }, { "name": "jasig/phpcas", - "version": "1.3.6", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/apereo/phpCAS.git", - "reference": "7972833e84f6ee5fa41f1479eab5d855109627f5" + "reference": "40c0769ce05a30c8172b36ceab11124375c8366e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/apereo/phpCAS/zipball/7972833e84f6ee5fa41f1479eab5d855109627f5", - "reference": "7972833e84f6ee5fa41f1479eab5d855109627f5", + "url": "https://api.github.com/repos/apereo/phpCAS/zipball/40c0769ce05a30c8172b36ceab11124375c8366e", + "reference": "40c0769ce05a30c8172b36ceab11124375c8366e", "shasum": "" }, "require": { @@ -757,40 +844,41 @@ "cas", "jasig" ], - "time": "2018-10-25T20:22:09+00:00" + "time": "2019-08-18T20:01:55+00:00" }, { "name": "khanamiryan/qrcode-detector-decoder", - "version": "1.0.2", + "version": "1.0.5.2", "source": { "type": "git", "url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git", - "reference": "a75482d3bc804e3f6702332bfda6cccbb0dfaa76" + "reference": "04fdd58d86a387065f707dc6d3cc304c719910c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/a75482d3bc804e3f6702332bfda6cccbb0dfaa76", - "reference": "a75482d3bc804e3f6702332bfda6cccbb0dfaa76", + "url": "https://api.github.com/repos/khanamiryan/php-qrcode-detector-decoder/zipball/04fdd58d86a387065f707dc6d3cc304c719910c1", + "reference": "04fdd58d86a387065f707dc6d3cc304c719910c1", "shasum": "" }, "require": { - "php": "^5.6|^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7 | ^7.5 | ^8.0 | ^9.0" }, "type": "library", "autoload": { - "psr-4": { - "Zxing\\": "lib/" - }, "files": [ "lib/Common/customFunctions.php" - ] + ], + "psr-4": { + "Zxing\\": "lib/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "MIT", + "Apache-2.0" ], "authors": [ { @@ -807,20 +895,20 @@ "qr", "zxing" ], - "time": "2018-04-26T11:41:33+00:00" + "time": "2021-07-13T18:46:38+00:00" }, { "name": "matthiasmullie/minify", - "version": "1.3.61", + "version": "1.3.62", "source": { "type": "git", "url": "https://github.com/matthiasmullie/minify.git", - "reference": "d5acb8ce5b6acb7d11bafe97cecc533f6e4fd751" + "reference": "47a53716f94139aff22922ffd73283ff04f23cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/d5acb8ce5b6acb7d11bafe97cecc533f6e4fd751", - "reference": "d5acb8ce5b6acb7d11bafe97cecc533f6e4fd751", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/47a53716f94139aff22922ffd73283ff04f23cdf", + "reference": "47a53716f94139aff22922ffd73283ff04f23cdf", "shasum": "" }, "require": { @@ -867,20 +955,20 @@ "minifier", "minify" ], - "time": "2018-11-26T23:10:39+00:00" + "time": "2019-12-19T07:54:47+00:00" }, { "name": "matthiasmullie/path-converter", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/matthiasmullie/path-converter.git", - "reference": "5e4b121c8b9f97c80835c1d878b0812ba1d607c9" + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/5e4b121c8b9f97c80835c1d878b0812ba1d607c9", - "reference": "5e4b121c8b9f97c80835c1d878b0812ba1d607c9", + "url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/e7d13b2c7e2f2268e1424aaed02085518afa02d9", + "reference": "e7d13b2c7e2f2268e1424aaed02085518afa02d9", "shasum": "" }, "require": { @@ -916,28 +1004,75 @@ "paths", "relative" ], - "time": "2018-10-25T15:19:41+00:00" + "time": "2019-02-05T23:41:09+00:00" + }, + { + "name": "misd/linkify", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/misd-service-development/php-linkify.git", + "reference": "3481b148806a23b4001712de645247a1a4dcc10a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/misd-service-development/php-linkify/zipball/3481b148806a23b4001712de645247a1a4dcc10a", + "reference": "3481b148806a23b4001712de645247a1a4dcc10a", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Misd\\Linkify\\": "src/Misd/Linkify" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Converts URLs and email addresses in text into HTML links", + "homepage": "https://github.com/misd-service-development/php-linkify", + "keywords": [ + "convert", + "email address", + "link", + "url" + ], + "time": "2017-08-17T08:33:35+00:00" }, { "name": "myclabs/php-enum", - "version": "1.6.4", + "version": "1.7.7", "source": { "type": "git", "url": "https://github.com/myclabs/php-enum.git", - "reference": "550d2334d77f91b0816a5cbd6965272fe20146b8" + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/php-enum/zipball/550d2334d77f91b0816a5cbd6965272fe20146b8", - "reference": "550d2334d77f91b0816a5cbd6965272fe20146b8", + "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7", + "reference": "d178027d1e679832db9f38248fcc7200647dc2b7", "shasum": "" }, "require": { - "php": ">=5.4" + "ext-json": "*", + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.7|^6.0", - "squizlabs/php_codesniffer": "1.*" + "phpunit/phpunit": "^7", + "squizlabs/php_codesniffer": "1.*", + "vimeo/psalm": "^3.8" }, "type": "library", "autoload": { @@ -960,31 +1095,32 @@ "keywords": [ "enum" ], - "time": "2018-10-30T14:36:18+00:00" + "time": "2020-11-14T18:14:52+00:00" }, { "name": "ocramius/package-versions", - "version": "1.2.0", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/Ocramius/PackageVersions.git", - "reference": "ad8a245decad4897cc6b432743913dad0d69753c" + "reference": "44af6f3a2e2e04f2af46bcb302ad9600cba41c7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/ad8a245decad4897cc6b432743913dad0d69753c", - "reference": "ad8a245decad4897cc6b432743913dad0d69753c", + "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/44af6f3a2e2e04f2af46bcb302ad9600cba41c7d", + "reference": "44af6f3a2e2e04f2af46bcb302ad9600cba41c7d", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0", - "php": "~7.0" + "composer-plugin-api": "^1.0.0", + "php": "^7.1.0" }, "require-dev": { - "composer/composer": "^1.3", + "composer/composer": "^1.6.3", + "doctrine/coding-standard": "^5.0.1", "ext-zip": "*", - "humbug/humbug": "dev-master", - "phpunit/phpunit": "^6.4" + "infection/infection": "^0.7.1", + "phpunit/phpunit": "^7.5.17" }, "type": "composer-plugin", "extra": { @@ -1009,33 +1145,37 @@ } ], "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "time": "2017-11-24T11:07:03+00:00" + "time": "2019-11-15T16:17:10+00:00" }, { "name": "ocramius/proxy-manager", - "version": "2.0.4", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/Ocramius/ProxyManager.git", - "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6" + "reference": "e18ac876b2e4819c76349de8f78ccc8ef1554cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/a55d08229f4f614bf335759ed0cf63378feeb2e6", - "reference": "a55d08229f4f614bf335759ed0cf63378feeb2e6", + "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/e18ac876b2e4819c76349de8f78ccc8ef1554cd7", + "reference": "e18ac876b2e4819c76349de8f78ccc8ef1554cd7", "shasum": "" }, "require": { - "ocramius/package-versions": "^1.0", - "php": "7.0.0 - 7.0.5 || ^7.0.7", - "zendframework/zend-code": "3.0.0 - 3.0.2 || ^3.0.4" + "ocramius/package-versions": "^1.1.1", + "php": "^7.1.0", + "zendframework/zend-code": "^3.1.0" }, "require-dev": { - "couscous/couscous": "^1.4.0", + "couscous/couscous": "^1.5.2", "ext-phar": "*", - "phpbench/phpbench": "^0.11.2", - "phpunit/phpunit": "^5.4.6", - "squizlabs/php_codesniffer": "^2.6.0" + "humbug/humbug": "dev-master@DEV", + "nikic/php-parser": "^3.0.4", + "phpbench/phpbench": "^0.12.2", + "phpstan/phpstan": "^0.6.4", + "phpunit/phpunit": "^5.6.4", + "phpunit/phpunit-mock-objects": "^3.4.1", + "squizlabs/php_codesniffer": "^2.7.0" }, "suggest": { "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects", @@ -1074,7 +1214,7 @@ "proxy pattern", "service proxies" ], - "time": "2016-11-04T15:53:15+00:00" + "time": "2017-05-04T11:12:50+00:00" }, { "name": "oyejorge/less.php", @@ -1186,16 +1326,16 @@ }, { "name": "pear/archive_tar", - "version": "1.4.5", + "version": "1.4.14", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "ff716ca697c5e9e8593212cb785ffd03ee11b01f" + "reference": "4d761c5334c790e45ef3245f0864b8955c562caa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/ff716ca697c5e9e8593212cb785ffd03ee11b01f", - "reference": "ff716ca697c5e9e8593212cb785ffd03ee11b01f", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa", + "reference": "4d761c5334c790e45ef3245f0864b8955c562caa", "shasum": "" }, "require": { @@ -1248,20 +1388,20 @@ "archive", "tar" ], - "time": "2019-01-02T21:45:13+00:00" + "time": "2021-07-20T13:53:39+00:00" }, { "name": "pear/console_getopt", - "version": "v1.4.1", + "version": "v1.4.3", "source": { "type": "git", "url": "https://github.com/pear/Console_Getopt.git", - "reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f" + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f", - "reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f", + "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0", + "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0", "shasum": "" }, "type": "library", @@ -1278,11 +1418,6 @@ "BSD-2-Clause" ], "authors": [ - { - "name": "Greg Beaver", - "email": "cellog@php.net", - "role": "Helper" - }, { "name": "Andrei Zmievski", "email": "andrei@php.net", @@ -1292,30 +1427,36 @@ "name": "Stig Bakken", "email": "stig@php.net", "role": "Developer" + }, + { + "name": "Greg Beaver", + "email": "cellog@php.net", + "role": "Helper" } ], "description": "More info available on: http://pear.php.net/package/Console_Getopt", - "time": "2015-07-20T20:28:12+00:00" + "time": "2019-11-20T18:27:48+00:00" }, { "name": "pear/file_marc", - "version": "1.2.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/pear/File_MARC.git", - "reference": "84b7f633c9261245bf6b16d7fbe87fe503551c9a" + "reference": "a4997f93d13933ad478cd8b6f43c6345d7388a70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/File_MARC/zipball/84b7f633c9261245bf6b16d7fbe87fe503551c9a", - "reference": "84b7f633c9261245bf6b16d7fbe87fe503551c9a", + "url": "https://api.github.com/repos/pear/File_MARC/zipball/a4997f93d13933ad478cd8b6f43c6345d7388a70", + "reference": "a4997f93d13933ad478cd8b6f43c6345d7388a70", "shasum": "" }, "require": { "pear/pear_exception": "1.*" }, "require-dev": { - "phpunit/phpunit": "*" + "phpunit/phpunit": "*", + "squizlabs/php_codesniffer": "*" }, "suggest": { "pear/validate_ispn": "Install optionally via your project's composer.json" @@ -1324,7 +1465,11 @@ "autoload": { "psr-0": { "File": "./" - } + }, + "classmap": [ + "./File/MARC/Data_Field.php", + "./File/MARC/Control_Field.php" + ] }, "notification-url": "https://packagist.org/downloads/", "include-path": [ @@ -1342,7 +1487,7 @@ } ], "description": "Supports the MAchine Readable Cataloging (MARC) file format documented at http://loc.gov/marc/", - "time": "2017-12-04T10:30:19+00:00" + "time": "2019-11-13T17:33:56+00:00" }, { "name": "pear/http_request2", @@ -1469,16 +1614,16 @@ }, { "name": "pear/pear-core-minimal", - "version": "v1.10.7", + "version": "v1.10.11", "source": { "type": "git", "url": "https://github.com/pear/pear-core-minimal.git", - "reference": "19a3e0fcd50492c4357372f623f55f1b144346da" + "reference": "68d0d32ada737153b7e93b8d3c710ebe70ac867d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/19a3e0fcd50492c4357372f623f55f1b144346da", - "reference": "19a3e0fcd50492c4357372f623f55f1b144346da", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/68d0d32ada737153b7e93b8d3c710ebe70ac867d", + "reference": "68d0d32ada737153b7e93b8d3c710ebe70ac867d", "shasum": "" }, "require": { @@ -1509,27 +1654,27 @@ } ], "description": "Minimal set of PEAR core files to be used as composer dependency", - "time": "2018-12-05T20:03:52+00:00" + "time": "2021-08-10T22:31:03+00:00" }, { "name": "pear/pear_exception", - "version": "v1.0.0", + "version": "v1.0.2", "source": { "type": "git", "url": "https://github.com/pear/PEAR_Exception.git", - "reference": "8c18719fdae000b690e3912be401c76e406dd13b" + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b", - "reference": "8c18719fdae000b690e3912be401c76e406dd13b", + "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", + "reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0", "shasum": "" }, "require": { - "php": ">=4.4.0" + "php": ">=5.2.0" }, "require-dev": { - "phpunit/phpunit": "*" + "phpunit/phpunit": "<9" }, "type": "class", "extra": { @@ -1538,9 +1683,9 @@ } }, "autoload": { - "psr-0": { - "PEAR": "" - } + "classmap": [ + "PEAR/" + ] }, "notification-url": "https://packagist.org/downloads/", "include-path": [ @@ -1564,7 +1709,7 @@ "keywords": [ "exception" ], - "time": "2015-02-10T20:07:52+00:00" + "time": "2021-03-21T15:43:46+00:00" }, { "name": "pear/validate", @@ -1643,12 +1788,12 @@ "source": { "type": "git", "url": "https://github.com/pear/Validate_ISPN.git", - "reference": "9ea9312a0841b5d745742c737772aeffa6d06e96" + "reference": "40272ba7f7eec3756aec29ba1f2f836c32edfcc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Validate_ISPN/zipball/9ea9312a0841b5d745742c737772aeffa6d06e96", - "reference": "9ea9312a0841b5d745742c737772aeffa6d06e96", + "url": "https://api.github.com/repos/pear/Validate_ISPN/zipball/40272ba7f7eec3756aec29ba1f2f836c32edfcc3", + "reference": "40272ba7f7eec3756aec29ba1f2f836c32edfcc3", "shasum": "" }, "require": { @@ -1683,25 +1828,24 @@ } ], "description": "More info available on: http://pear.php.net/package/Validate_ISPN", - "time": "2015-04-14T04:17:31+00:00" + "time": "2021-08-10T23:36:56+00:00" }, { "name": "phing/phing", - "version": "2.16.1", + "version": "2.16.2", "source": { "type": "git", "url": "https://github.com/phingofficial/phing.git", - "reference": "cbe0f969e434e269af91b4160b86fe899c6e07c7" + "reference": "d11c6328c450cb3cda4ffa6548aa9cd60f30dd17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phingofficial/phing/zipball/cbe0f969e434e269af91b4160b86fe899c6e07c7", - "reference": "cbe0f969e434e269af91b4160b86fe899c6e07c7", + "url": "https://api.github.com/repos/phingofficial/phing/zipball/d11c6328c450cb3cda4ffa6548aa9cd60f30dd17", + "reference": "d11c6328c450cb3cda4ffa6548aa9cd60f30dd17", "shasum": "" }, "require": { - "php": ">=5.2.0", - "symfony/yaml": "^3.1 || ^4.0" + "php": ">=5.2.0" }, "require-dev": { "ext-pdo_sqlite": "*", @@ -1721,7 +1865,8 @@ "sebastian/phpcpd": "2.x", "siad007/versioncontrol_hg": "^1.0", "simpletest/simpletest": "^1.1", - "squizlabs/php_codesniffer": "~2.2" + "squizlabs/php_codesniffer": "~2.2", + "symfony/yaml": "^2.8 || ^3.1 || ^4.0" }, "suggest": { "pdepend/pdepend": "PHP version of JDepend", @@ -1756,7 +1901,7 @@ "classes" ], "license": [ - "LGPL-3.0" + "LGPL-3.0-only" ], "authors": [ { @@ -1776,39 +1921,34 @@ "task", "tool" ], - "time": "2018-01-25T13:18:09+00:00" + "time": "2020-01-03T10:18:48+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", - "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", "shasum": "" }, "require": { - "php": ">=5.5" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "phpDocumentor\\Reflection\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1830,31 +1970,32 @@ "reflection", "static analysis" ], - "time": "2017-09-11T18:02:19+00:00" + "time": "2020-04-27T09:25:28+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", + "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", "phpunit/phpunit": "^6.4" }, "type": "library", @@ -1881,41 +2022,40 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", + "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", - "phpdocumentor/reflection-common": "^1.0" + "php": "^7.1", + "phpdocumentor/reflection-common": "^2.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "ext-tokenizer": "^7.1", + "mockery/mockery": "~1", + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1928,7 +2068,8 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "time": "2019-08-22T18:11:29+00:00" }, { "name": "psr/cache", @@ -2027,20 +2168,20 @@ }, { "name": "psr/http-client", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/php-fig/http-client.git", - "reference": "496a823ef742b632934724bf769560c2a5c7c44e" + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/496a823ef742b632934724bf769560c2a5c7c44e", - "reference": "496a823ef742b632934724bf769560c2a5c7c44e", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", "shasum": "" }, "require": { - "php": "^7.0", + "php": "^7.0 || ^8.0", "psr/http-message": "^1.0" }, "type": "library", @@ -2072,7 +2213,7 @@ "psr", "psr-18" ], - "time": "2018-10-30T23:29:13+00:00" + "time": "2020-06-29T06:28:15+00:00" }, { "name": "psr/http-factory", @@ -2178,16 +2319,16 @@ }, { "name": "psr/log", - "version": "1.1.0", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { @@ -2196,7 +2337,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -2211,7 +2352,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interface for logging libraries", @@ -2221,7 +2362,7 @@ "psr", "psr-3" ], - "time": "2018-11-20T15:27:04+00:00" + "time": "2021-05-03T11:20:27+00:00" }, { "name": "psr/simple-cache", @@ -2413,16 +2554,16 @@ }, { "name": "symfony/cache", - "version": "v3.4.35", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "8d5db9c0cecf8b6f79fa96583fae652224d897da" + "reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/8d5db9c0cecf8b6f79fa96583fae652224d897da", - "reference": "8d5db9c0cecf8b6f79fa96583fae652224d897da", + "url": "https://api.github.com/repos/symfony/cache/zipball/a7a14c4832760bd1fbd31be2859ffedc9b6ff813", + "reference": "a7a14c4832760bd1fbd31be2859ffedc9b6ff813", "shasum": "" }, "require": { @@ -2441,16 +2582,11 @@ }, "require-dev": { "cache/integration-tests": "dev-master", - "doctrine/cache": "~1.6", - "doctrine/dbal": "~2.4", - "predis/predis": "~1.0" + "doctrine/cache": "^1.6", + "doctrine/dbal": "^2.4|^3.0", + "predis/predis": "^1.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Cache\\": "" @@ -2479,32 +2615,28 @@ "caching", "psr6" ], - "time": "2019-11-12T12:50:33+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/expression-language", - "version": "v3.4.35", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "434b23d0deaf6a9735f36036aac484bf423a2bae" + "reference": "de38e66398fca1fcb9c48e80279910e6889cb28f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/434b23d0deaf6a9735f36036aac484bf423a2bae", - "reference": "434b23d0deaf6a9735f36036aac484bf423a2bae", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/de38e66398fca1fcb9c48e80279910e6889cb28f", + "reference": "de38e66398fca1fcb9c48e80279910e6889cb28f", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8", - "symfony/cache": "~3.1|~4.0" + "symfony/cache": "~3.1|~4.0", + "symfony/polyfill-php70": "~1.6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" @@ -2529,20 +2661,20 @@ ], "description": "Symfony ExpressionLanguage Component", "homepage": "https://symfony.com", - "time": "2019-10-24T15:33:53+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "c24ce3d18ccc9bb9d7e1d6ce9330fcc6061cafde" + "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/c24ce3d18ccc9bb9d7e1d6ce9330fcc6061cafde", - "reference": "c24ce3d18ccc9bb9d7e1d6ce9330fcc6061cafde", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e58d7841cddfed6e846829040dca2cca0ebbbbb3", + "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3", "shasum": "" }, "require": { @@ -2550,11 +2682,6 @@ "symfony/polyfill-ctype": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Filesystem\\": "" @@ -2579,20 +2706,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/http-foundation", - "version": "v3.4.36", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d2d0cfe8e319d9df44c4cca570710fcf221d4593" + "reference": "b9885fcce6fe494201da4f70a9309770e9d13dc8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d2d0cfe8e319d9df44c4cca570710fcf221d4593", - "reference": "d2d0cfe8e319d9df44c4cca570710fcf221d4593", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b9885fcce6fe494201da4f70a9309770e9d13dc8", + "reference": "b9885fcce6fe494201da4f70a9309770e9d13dc8", "shasum": "" }, "require": { @@ -2604,11 +2731,6 @@ "symfony/expression-language": "~2.8|~3.0|~4.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" @@ -2633,20 +2755,20 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2019-11-28T12:52:59+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/inflector", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/inflector.git", - "reference": "71ea67b4843bc668c17302ac79fa6f61437487bb" + "reference": "b557c5d061b72cadf454dd87cd1308d0710c8021" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/71ea67b4843bc668c17302ac79fa6f61437487bb", - "reference": "71ea67b4843bc668c17302ac79fa6f61437487bb", + "url": "https://api.github.com/repos/symfony/inflector/zipball/b557c5d061b72cadf454dd87cd1308d0710c8021", + "reference": "b557c5d061b72cadf454dd87cd1308d0710c8021", "shasum": "" }, "require": { @@ -2654,11 +2776,6 @@ "symfony/polyfill-ctype": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Inflector\\": "" @@ -2691,31 +2808,27 @@ "symfony", "words" ], - "time": "2019-01-01T13:45:19+00:00" + "abandoned": "EnglishInflector from the String component", + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "8a10e36ffd04c0c551051594952304d34ecece71" + "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/8a10e36ffd04c0c551051594952304d34ecece71", - "reference": "8a10e36ffd04c0c551051594952304d34ecece71", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", + "reference": "c7efc97a47b2ebaabc19d5b6c6b50f5c37c92744", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" @@ -2745,38 +2858,42 @@ "configuration", "options" ], - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/polyfill-apcu", - "version": "v1.12.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-apcu.git", - "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5" + "reference": "43273a33c46f9d5a08dac76859f63d6814242e81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/71ce80635d5dcd67772b4dda00b86068595f64d5", - "reference": "71ce80635d5dcd67772b4dda00b86068595f64d5", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/43273a33c46f9d5a08dac76859f63d6814242e81", + "reference": "43273a33c46f9d5a08dac76859f63d6814242e81", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.12-dev" + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Apcu\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Apcu\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2801,24 +2918,27 @@ "portable", "shim" ], - "time": "2019-08-06T08:03:45+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.10.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" }, "suggest": { "ext-ctype": "For best performance" @@ -2826,29 +2946,33 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -2859,24 +2983,27 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.10.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", - "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" }, "suggest": { "ext-mbstring": "For best performance" @@ -2884,16 +3011,20 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2918,42 +3049,34 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.10.0", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224" + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/6b88000cdd431cd2e940caa2cb569201f3f84224", - "reference": "6b88000cdd431cd2e940caa2cb569201f3f84224", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/5f03a781d984aae42cebd18e7912fa80f02ee644", + "reference": "5f03a781d984aae42cebd18e7912fa80f02ee644", "shasum": "" }, "require": { - "paragonie/random_compat": "~1.0|~2.0|~9.99", - "php": ">=5.3.3" + "php": ">=7.1" }, - "type": "library", + "type": "metapackage", "extra": { "branch-alias": { - "dev-master": "1.9-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php70\\": "" + "dev-main": "1.20-dev" }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2977,20 +3100,20 @@ "portable", "shim" ], - "time": "2018-09-21T06:26:08+00:00" + "time": "2020-10-23T14:02:19+00:00" }, { "name": "symfony/property-access", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "2d14c17d63ede1f23bb8a591a3255e63ecfef85f" + "reference": "f1dc91d0c987f3ba95be1d7874527d11477b25ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/2d14c17d63ede1f23bb8a591a3255e63ecfef85f", - "reference": "2d14c17d63ede1f23bb8a591a3255e63ecfef85f", + "url": "https://api.github.com/repos/symfony/property-access/zipball/f1dc91d0c987f3ba95be1d7874527d11477b25ff", + "reference": "f1dc91d0c987f3ba95be1d7874527d11477b25ff", "shasum": "" }, "require": { @@ -3005,11 +3128,6 @@ "psr/cache-implementation": "To cache access methods." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" @@ -3045,20 +3163,20 @@ "property path", "reflection" ], - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/property-info", - "version": "v3.4.35", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "5248becda0a0554ca4ae982825f012076f9e66a6" + "reference": "a5f1e77c881342a5b1e05fdc12642650853bd112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/5248becda0a0554ca4ae982825f012076f9e66a6", - "reference": "5248becda0a0554ca4ae982825f012076f9e66a6", + "url": "https://api.github.com/repos/symfony/property-info/zipball/a5f1e77c881342a5b1e05fdc12642650853bd112", + "reference": "a5f1e77c881342a5b1e05fdc12642650853bd112", "shasum": "" }, "require": { @@ -3084,11 +3202,6 @@ "symfony/serializer": "To use Serializer metadata" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\PropertyInfo\\": "" @@ -3121,20 +3234,20 @@ "type", "validator" ], - "time": "2019-10-11T04:14:16+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/serializer", - "version": "v3.4.35", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "9d14f7ff2c585a8a9f6f980253066285ddc2f675" + "reference": "6d69ccc1dcfb64c1e9c9444588643e98718d1849" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/9d14f7ff2c585a8a9f6f980253066285ddc2f675", - "reference": "9d14f7ff2c585a8a9f6f980253066285ddc2f675", + "url": "https://api.github.com/repos/symfony/serializer/zipball/6d69ccc1dcfb64c1e9c9444588643e98718d1849", + "reference": "6d69ccc1dcfb64c1e9c9444588643e98718d1849", "shasum": "" }, "require": { @@ -3171,11 +3284,6 @@ "symfony/yaml": "For using the default YAML mapping loader." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Serializer\\": "" @@ -3200,20 +3308,20 @@ ], "description": "Symfony Serializer Component", "homepage": "https://symfony.com", - "time": "2019-11-12T17:51:12+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.21", + "version": "v3.4.36", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea" + "reference": "dab657db15207879217fc81df4f875947bf68804" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/554a59a1ccbaac238a89b19c8e551a556fd0e2ea", - "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea", + "url": "https://api.github.com/repos/symfony/yaml/zipball/dab657db15207879217fc81df4f875947bf68804", + "reference": "dab657db15207879217fc81df4f875947bf68804", "shasum": "" }, "require": { @@ -3259,7 +3367,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2019-10-24T15:33:53+00:00" }, { "name": "true/punycode", @@ -3309,16 +3417,16 @@ }, { "name": "vufind-org/vufindcode", - "version": "v1.1.1", + "version": "v1.2", "source": { "type": "git", "url": "https://github.com/vufind-org/vufindcode.git", - "reference": "c95683bbe2ed5259a807669fd3d0f58ad24964fe" + "reference": "df7f4d2188c9f2c654dfee69774b80b9d03b1ab4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindcode/zipball/c95683bbe2ed5259a807669fd3d0f58ad24964fe", - "reference": "c95683bbe2ed5259a807669fd3d0f58ad24964fe", + "url": "https://api.github.com/repos/vufind-org/vufindcode/zipball/df7f4d2188c9f2c654dfee69774b80b9d03b1ab4", + "reference": "df7f4d2188c9f2c654dfee69774b80b9d03b1ab4", "shasum": "" }, "require": { @@ -3350,9 +3458,9 @@ "email": "demian.katz@villanova.edu" } ], - "description": "Class for representing ISBNs (a VuFind support library)", + "description": "Classes for working with EANs, ISBNs and ISMNs (a VuFind support library)", "homepage": "https://vufind.org/", - "time": "2019-01-22T14:41:47+00:00" + "time": "2019-11-07T14:29:07+00:00" }, { "name": "vufind-org/vufinddate", @@ -3403,16 +3511,16 @@ }, { "name": "vufind-org/vufindharvest", - "version": "v2.4.0", + "version": "v2.4.1", "source": { "type": "git", "url": "https://github.com/vufind-org/vufindharvest.git", - "reference": "a7391a2e3b9efc031c4e223debf7a56678e420a8" + "reference": "6f914f42428ebd20b9132d21ff16d3bb5e6c128a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/a7391a2e3b9efc031c4e223debf7a56678e420a8", - "reference": "a7391a2e3b9efc031c4e223debf7a56678e420a8", + "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/6f914f42428ebd20b9132d21ff16d3bb5e6c128a", + "reference": "6f914f42428ebd20b9132d21ff16d3bb5e6c128a", "shasum": "" }, "require": { @@ -3449,20 +3557,20 @@ ], "description": "VuFind Harvest Tools", "homepage": "https://vufind.org/", - "time": "2018-05-23T19:14:41+00:00" + "time": "2019-09-13T12:56:58+00:00" }, { "name": "vufind-org/vufindhttp", - "version": "v2.2.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/vufind-org/vufindhttp.git", - "reference": "2415b70424156ef9ebcbcff7900500c5fa62789b" + "reference": "f6516ae29ef60d6e99aaa53bd375fb894915ab2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindhttp/zipball/2415b70424156ef9ebcbcff7900500c5fa62789b", - "reference": "2415b70424156ef9ebcbcff7900500c5fa62789b", + "url": "https://api.github.com/repos/vufind-org/vufindhttp/zipball/f6516ae29ef60d6e99aaa53bd375fb894915ab2c", + "reference": "f6516ae29ef60d6e99aaa53bd375fb894915ab2c", "shasum": "" }, "require": { @@ -3494,7 +3602,7 @@ { "name": "David Maus", "email": "maus@hab.de", - "role": "developer" + "role": "Developer" }, { "name": "Demian Katz", @@ -3504,36 +3612,34 @@ ], "description": "VuFind HTTP service library", "homepage": "https://vufind.org/", - "time": "2018-05-23T17:51:55+00:00" + "time": "2019-10-23T13:22:49+00:00" }, { "name": "webmozart/assert", - "version": "1.4.0", + "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" + "url": "https://github.com/webmozarts/assert.git", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "symfony/polyfill-ctype": "^1.8" }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<3.9.1" + }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -3555,7 +3661,7 @@ "check", "validate" ], - "time": "2018-12-25T11:19:39+00:00" + "time": "2020-07-08T17:02:28+00:00" }, { "name": "wikimedia/composer-merge-plugin", @@ -3608,22 +3714,27 @@ }, { "name": "yajra/laravel-pdo-via-oci8", - "version": "v1.3.7", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/yajra/pdo-via-oci8.git", - "reference": "68d8387fae774b1ba56c809a59f87ca91a29dbf5" + "reference": "7295ed52a724887f66d01180a500ecdb76a22f4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/pdo-via-oci8/zipball/68d8387fae774b1ba56c809a59f87ca91a29dbf5", - "reference": "68d8387fae774b1ba56c809a59f87ca91a29dbf5", + "url": "https://api.github.com/repos/yajra/pdo-via-oci8/zipball/7295ed52a724887f66d01180a500ecdb76a22f4c", + "reference": "7295ed52a724887f66d01180a500ecdb76a22f4c", "shasum": "" }, "require-dev": { "phpunit/phpunit": "^6.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, "autoload": { "psr-4": { "Yajra\\": "src/" @@ -3640,20 +3751,20 @@ } ], "description": "PDO userspace driver proxying calls to PHP OCI8 driver", - "time": "2018-09-05T03:22:57+00:00" + "time": "2019-12-05T06:00:56+00:00" }, { "name": "zendframework/zend-cache", - "version": "2.8.2", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-cache.git", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3" + "reference": "cffd54a2dc4db094976d3b3f05e418a047cc9110" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/4983dff629956490c78b88adcc8ece4711d7d8a3", - "reference": "4983dff629956490c78b88adcc8ece4711d7d8a3", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/cffd54a2dc4db094976d3b3f05e418a047cc9110", + "reference": "cffd54a2dc4db094976d3b3f05e418a047cc9110", "shasum": "" }, "require": { @@ -3662,7 +3773,7 @@ "psr/simple-cache": "^1.0", "zendframework/zend-eventmanager": "^2.6.3 || ^3.2", "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "provide": { "psr/cache-implementation": "1.0", @@ -3695,8 +3806,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\Cache", @@ -3724,26 +3835,26 @@ "zf" ], "abandoned": "laminas/laminas-cache", - "time": "2018-05-01T21:58:00+00:00" + "time": "2019-08-29T18:30:41+00:00" }, { "name": "zendframework/zend-captcha", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-captcha.git", - "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b" + "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/37e9b6a4f632a9399eecbf2e5e325ad89083f87b", - "reference": "37e9b6a4f632a9399eecbf2e5e325ad89083f87b", + "url": "https://api.github.com/repos/zendframework/zend-captcha/zipball/4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", + "reference": "4272f3d0cde0a1fa9135d0cbc4a629fb655391d3", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-math": "^2.7 || ^3.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", @@ -3763,8 +3874,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8.x-dev", - "dev-develop": "2.9.x-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" } }, "autoload": { @@ -3783,31 +3894,34 @@ "zf" ], "abandoned": "laminas/laminas-captcha", - "time": "2018-04-24T17:24:10+00:00" + "time": "2019-06-18T09:32:52+00:00" }, { "name": "zendframework/zend-code", - "version": "3.1.0", + "version": "3.4.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-code.git", - "reference": "2899c17f83a7207f2d7f53ec2f421204d3beea27" + "reference": "268040548f92c2bfcba164421c1add2ba43abaaa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-code/zipball/2899c17f83a7207f2d7f53ec2f421204d3beea27", - "reference": "2899c17f83a7207f2d7f53ec2f421204d3beea27", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/268040548f92c2bfcba164421c1add2ba43abaaa", + "reference": "268040548f92c2bfcba164421c1add2ba43abaaa", "shasum": "" }, "require": { - "php": "^5.6 || 7.0.0 - 7.0.4 || ^7.0.6", + "php": "^7.1", "zendframework/zend-eventmanager": "^2.6 || ^3.0" }, + "conflict": { + "phpspec/prophecy": "<1.9.0" + }, "require-dev": { - "doctrine/annotations": "~1.0", + "doctrine/annotations": "^1.7", "ext-phar": "*", - "phpunit/phpunit": "^4.8.21", - "squizlabs/php_codesniffer": "^2.5", + "phpunit/phpunit": "^7.5.16 || ^8.4", + "zendframework/zend-coding-standard": "^1.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "suggest": { @@ -3817,8 +3931,9 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev", - "dev-develop": "3.2-dev" + "dev-master": "3.4.x-dev", + "dev-develop": "3.5.x-dev", + "dev-dev-4.0": "4.0.x-dev" } }, "autoload": { @@ -3830,27 +3945,27 @@ "license": [ "BSD-3-Clause" ], - "description": "provides facilities to generate arbitrary code using an object oriented interface", - "homepage": "https://github.com/zendframework/zend-code", + "description": "Extensions to the PHP Reflection API, static code scanning, and code generation", "keywords": [ + "ZendFramework", "code", - "zf2" + "zf" ], "abandoned": "laminas/laminas-code", - "time": "2016-10-24T13:23:32+00:00" + "time": "2019-12-10T19:21:15+00:00" }, { "name": "zendframework/zend-config", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-config.git", - "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023" + "reference": "012341361ae3cc97a99959e7cb7c9ebd04d49572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/6796f5dcba52c84ef2501d7313618989b5ef3023", - "reference": "6796f5dcba52c84ef2501d7313618989b5ef3023", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/012341361ae3cc97a99959e7cb7c9ebd04d49572", + "reference": "012341361ae3cc97a99959e7cb7c9ebd04d49572", "shasum": "" }, "require": { @@ -3878,8 +3993,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "3.4.x-dev" } }, "autoload": { @@ -3898,25 +4013,25 @@ "zf" ], "abandoned": "laminas/laminas-config", - "time": "2018-04-24T19:26:44+00:00" + "time": "2019-06-08T18:58:54+00:00" }, { "name": "zendframework/zend-console", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-console.git", - "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18" + "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-console/zipball/e8aa08da83de3d265256c40ba45cd649115f0e18", - "reference": "e8aa08da83de3d265256c40ba45cd649115f0e18", + "url": "https://api.github.com/repos/zendframework/zend-console/zipball/95817ae78f73c48026972e350a2ecc31c6d9f9ae", + "reference": "95817ae78f73c48026972e350a2ecc31c6d9f9ae", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", @@ -3932,8 +4047,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev", - "dev-develop": "2.8.x-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { @@ -3952,20 +4067,20 @@ "zf" ], "abandoned": "laminas/laminas-console", - "time": "2018-01-25T19:08:04+00:00" + "time": "2019-02-04T19:48:22+00:00" }, { "name": "zendframework/zend-crypt", - "version": "3.3.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-crypt.git", - "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065" + "reference": "cf23da2731dabdbb916cf888f611fc0f123bd36b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/9c2916faa9b2132a0f91cdca8e95b025c352f065", - "reference": "9c2916faa9b2132a0f91cdca8e95b025c352f065", + "url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/cf23da2731dabdbb916cf888f611fc0f123bd36b", + "reference": "cf23da2731dabdbb916cf888f611fc0f123bd36b", "shasum": "" }, "require": { @@ -4005,20 +4120,20 @@ "zf" ], "abandoned": "laminas/laminas-crypt", - "time": "2018-04-24T22:01:58+00:00" + "time": "2019-05-14T20:19:53+00:00" }, { "name": "zendframework/zend-db", - "version": "2.9.3", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-db.git", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9" + "reference": "312a6efdb6fd28acf9551cac977803407116bf06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-db/zipball/5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", - "reference": "5b4f2c42f94c9f7f4b2f456a0ebe459fab12b3d9", + "url": "https://api.github.com/repos/zendframework/zend-db/zipball/312a6efdb6fd28acf9551cac977803407116bf06", + "reference": "312a6efdb6fd28acf9551cac977803407116bf06", "shasum": "" }, "require": { @@ -4029,7 +4144,7 @@ "phpunit/phpunit": "^5.7.25 || ^6.4.4", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-hydrator": "^1.1 || ^2.1", + "zendframework/zend-hydrator": "^1.1 || ^2.1 || ^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" }, "suggest": { @@ -4040,8 +4155,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9-dev", - "dev-develop": "2.10-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\Db", @@ -4064,7 +4179,7 @@ "zf" ], "abandoned": "laminas/laminas-db", - "time": "2018-04-09T13:21:36+00:00" + "time": "2019-12-31T17:54:17+00:00" }, { "name": "zendframework/zend-diactoros", @@ -4136,16 +4251,16 @@ }, { "name": "zendframework/zend-dom", - "version": "2.7.1", + "version": "2.7.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-dom.git", - "reference": "ec2c66c2bb0046e895651b24f2ebb83058b9bbca" + "reference": "66b8459e6f2c0d62ff4f4b16f2b44afdfd466aa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-dom/zipball/ec2c66c2bb0046e895651b24f2ebb83058b9bbca", - "reference": "ec2c66c2bb0046e895651b24f2ebb83058b9bbca", + "url": "https://api.github.com/repos/zendframework/zend-dom/zipball/66b8459e6f2c0d62ff4f4b16f2b44afdfd466aa0", + "reference": "66b8459e6f2c0d62ff4f4b16f2b44afdfd466aa0", "shasum": "" }, "require": { @@ -4178,20 +4293,20 @@ "zf" ], "abandoned": "laminas/laminas-dom", - "time": "2018-04-09T20:18:00+00:00" + "time": "2019-06-18T10:36:49+00:00" }, { "name": "zendframework/zend-escaper", - "version": "2.6.0", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-escaper.git", - "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074" + "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/31d8aafae982f9568287cb4dce987e6aff8fd074", - "reference": "31d8aafae982f9568287cb4dce987e6aff8fd074", + "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", + "reference": "3801caa21b0ca6aca57fa1c42b08d35c395ebd5f", "shasum": "" }, "require": { @@ -4224,7 +4339,7 @@ "zf" ], "abandoned": "laminas/laminas-escaper", - "time": "2018-04-25T15:48:53+00:00" + "time": "2019-09-05T20:03:20+00:00" }, { "name": "zendframework/zend-eventmanager", @@ -4283,22 +4398,24 @@ }, { "name": "zendframework/zend-feed", - "version": "2.10.3", + "version": "2.12.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-feed.git", - "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888" + "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/6641f4cf3f4586c63f83fd70b6d19966025c8888", - "reference": "6641f4cf3f4586c63f83fd70b6d19966025c8888", + "url": "https://api.github.com/repos/zendframework/zend-feed/zipball/d926c5af34b93a0121d5e2641af34ddb1533d733", + "reference": "d926c5af34b93a0121d5e2641af34ddb1533d733", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-libxml": "*", "php": "^5.6 || ^7.0", "zendframework/zend-escaper": "^2.5.2", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "phpunit/phpunit": "^5.7.23 || ^6.4.3", @@ -4321,8 +4438,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10.x-dev", - "dev-develop": "2.11.x-dev" + "dev-master": "2.12.x-dev", + "dev-develop": "2.13.x-dev" } }, "autoload": { @@ -4341,20 +4458,20 @@ "zf" ], "abandoned": "laminas/laminas-feed", - "time": "2018-08-01T13:53:20+00:00" + "time": "2019-03-05T20:08:49+00:00" }, { "name": "zendframework/zend-filter", - "version": "2.9.1", + "version": "2.9.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-filter.git", - "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f" + "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", - "reference": "1c3e6d02f9cd5f6c929c9859498f5efbe216e86f", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/d78f2cdde1c31975e18b2a0753381ed7b61118ef", + "reference": "d78f2cdde1c31975e18b2a0753381ed7b61118ef", "shasum": "" }, "require": { @@ -4400,27 +4517,27 @@ "license": [ "BSD-3-Clause" ], - "description": "provides a set of commonly needed data filters", + "description": "Programmatically filter and normalize data and files", "keywords": [ "ZendFramework", "filter", "zf" ], "abandoned": "laminas/laminas-filter", - "time": "2018-12-17T16:00:04+00:00" + "time": "2019-08-19T07:08:04+00:00" }, { "name": "zendframework/zend-form", - "version": "2.14.0", + "version": "2.14.3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-form.git", - "reference": "afab173513e6930aa8ae86cc755e3b2d4874d6f6" + "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-form/zipball/afab173513e6930aa8ae86cc755e3b2d4874d6f6", - "reference": "afab173513e6930aa8ae86cc755e3b2d4874d6f6", + "url": "https://api.github.com/repos/zendframework/zend-form/zipball/0b1616c59b1f3df194284e26f98c81ad0c377871", + "reference": "0b1616c59b1f3df194284e26f98c81ad0c377871", "shasum": "" }, "require": { @@ -4486,20 +4603,20 @@ "zf" ], "abandoned": "laminas/laminas-form", - "time": "2019-01-07T21:38:04+00:00" + "time": "2019-10-04T10:46:36+00:00" }, { "name": "zendframework/zend-http", - "version": "2.9.1", + "version": "2.11.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-http.git", - "reference": "fe80a6bc0b8a632ed878854ee519a2eed4a1e2d0" + "reference": "e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-http/zipball/fe80a6bc0b8a632ed878854ee519a2eed4a1e2d0", - "reference": "fe80a6bc0b8a632ed878854ee519a2eed4a1e2d0", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a", + "reference": "e15e0ce45a2a4f642cd0b7b4f4d4d0366b729a1a", "shasum": "" }, "require": { @@ -4520,8 +4637,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.11.x-dev", + "dev-develop": "2.12.x-dev" } }, "autoload": { @@ -4542,20 +4659,20 @@ "zf" ], "abandoned": "laminas/laminas-http", - "time": "2019-01-22T19:48:43+00:00" + "time": "2019-12-30T20:47:33+00:00" }, { "name": "zendframework/zend-hydrator", - "version": "2.4.1", + "version": "2.4.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-hydrator.git", - "reference": "70b02f4d8676e64af932625751750b5ca72fff3a" + "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/70b02f4d8676e64af932625751750b5ca72fff3a", - "reference": "70b02f4d8676e64af932625751750b5ca72fff3a", + "url": "https://api.github.com/repos/zendframework/zend-hydrator/zipball/2bfc6845019e7b6d38b0ab5e55190244dc510285", + "reference": "2bfc6845019e7b6d38b0ab5e55190244dc510285", "shasum": "" }, "require": { @@ -4580,10 +4697,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-release-1.0": "1.0.x-dev", - "dev-release-1.1": "1.1.x-dev", - "dev-master": "2.4.x-dev", - "dev-develop": "2.5.x-dev" + "dev-release-2.4": "2.4.x-dev" }, "zf": { "component": "Zend\\Hydrator", @@ -4606,28 +4720,32 @@ "zf" ], "abandoned": "laminas/laminas-hydrator", - "time": "2018-11-19T19:16:10+00:00" + "time": "2019-10-04T11:17:36+00:00" }, { "name": "zendframework/zend-i18n", - "version": "2.9.0", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f" + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/6d69af5a04e1a4de7250043cb1322f077a0cdb7f", - "reference": "6d69af5a04e1a4de7250043cb1322f077a0cdb7f", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/84038e6a1838b611dcc491b1c40321fa4c3a123c", + "reference": "84038e6a1838b611dcc491b1c40321fa4c3a123c", "shasum": "" }, "require": { + "ext-intl": "*", "php": "^5.6 || ^7.0", "zendframework/zend-stdlib": "^2.7 || ^3.0" }, + "conflict": { + "phpspec/prophecy": "<1.9.0" + }, "require-dev": { - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-config": "^2.6", @@ -4638,7 +4756,6 @@ "zendframework/zend-view": "^2.6.3" }, "suggest": { - "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", "zendframework/zend-cache": "Zend\\Cache component", "zendframework/zend-config": "Zend\\Config component", "zendframework/zend-eventmanager": "You should install this package to use the events in the translator", @@ -4651,8 +4768,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\I18n", @@ -4675,7 +4792,7 @@ "zf" ], "abandoned": "laminas/laminas-i18n", - "time": "2018-05-16T16:39:13+00:00" + "time": "2019-12-12T14:08:22+00:00" }, { "name": "zendframework/zend-i18n-resources", @@ -4726,16 +4843,16 @@ }, { "name": "zendframework/zend-inputfilter", - "version": "2.9.1", + "version": "2.10.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-inputfilter.git", - "reference": "3dc2ff5a474bce824e2429564daa56b4c6b0d547" + "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/3dc2ff5a474bce824e2429564daa56b4c6b0d547", - "reference": "3dc2ff5a474bce824e2429564daa56b4c6b0d547", + "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/1f44a2e9bc394a71638b43bc7024b572fa65410e", + "reference": "1f44a2e9bc394a71638b43bc7024b572fa65410e", "shasum": "" }, "require": { @@ -4746,7 +4863,7 @@ "zendframework/zend-validator": "^2.11" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.15", "psr/http-message": "^1.0", "zendframework/zend-coding-standard": "~1.0.0" }, @@ -4756,8 +4873,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\InputFilter", @@ -4780,20 +4897,20 @@ "zf" ], "abandoned": "laminas/laminas-inputfilter", - "time": "2019-01-07T17:52:18+00:00" + "time": "2019-08-28T19:45:32+00:00" }, { "name": "zendframework/zend-json", - "version": "3.1.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-json.git", - "reference": "4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c" + "reference": "e9ddb1192d93fe7fff846ac895249c39db75132b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c", - "reference": "4dd940e8e6f32f1d36ea6b0677ea57c540c7c19c", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/e9ddb1192d93fe7fff846ac895249c39db75132b", + "reference": "e9ddb1192d93fe7fff846ac895249c39db75132b", "shasum": "" }, "require": { @@ -4831,7 +4948,7 @@ "zf" ], "abandoned": "laminas/laminas-json", - "time": "2018-01-04T17:51:34+00:00" + "time": "2019-10-09T13:56:13+00:00" }, { "name": "zendframework/zend-loader", @@ -5068,16 +5185,16 @@ }, { "name": "zendframework/zend-mime", - "version": "2.7.1", + "version": "2.7.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-mime.git", - "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2" + "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/52ae5fa9f12845cae749271034a2d594f0e4c6f2", - "reference": "52ae5fa9f12845cae749271034a2d594f0e4c6f2", + "url": "https://api.github.com/repos/zendframework/zend-mime/zipball/c91e0350be53cc9d29be15563445eec3b269d7c1", + "reference": "c91e0350be53cc9d29be15563445eec3b269d7c1", "shasum": "" }, "require": { @@ -5095,8 +5212,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -5109,27 +5226,26 @@ "BSD-3-Clause" ], "description": "Create and parse MIME messages and parts", - "homepage": "https://github.com/zendframework/zend-mime", "keywords": [ "ZendFramework", "mime", "zf" ], "abandoned": "laminas/laminas-mime", - "time": "2018-05-14T19:02:50+00:00" + "time": "2019-10-16T19:30:37+00:00" }, { "name": "zendframework/zend-modulemanager", - "version": "2.8.2", + "version": "2.8.4", "source": { "type": "git", "url": "https://github.com/zendframework/zend-modulemanager.git", - "reference": "394df6e12248ac430a312d4693f793ee7120baa6" + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/394df6e12248ac430a312d4693f793ee7120baa6", - "reference": "394df6e12248ac430a312d4693f793ee7120baa6", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/b2596d24b9a4e36a3cd114d35d3ad0918db9a243", + "reference": "b2596d24b9a4e36a3cd114d35d3ad0918db9a243", "shasum": "" }, "require": { @@ -5139,7 +5255,7 @@ "zendframework/zend-stdlib": "^3.1 || ^2.7" }, "require-dev": { - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-console": "^2.6", "zendframework/zend-di": "^2.6", @@ -5156,8 +5272,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev", - "dev-develop": "2.8-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" } }, "autoload": { @@ -5170,14 +5286,13 @@ "BSD-3-Clause" ], "description": "Modular application system for zend-mvc applications", - "homepage": "https://github.com/zendframework/zend-modulemanager", "keywords": [ "ZendFramework", "modulemanager", "zf" ], "abandoned": "laminas/laminas-modulemanager", - "time": "2017-12-02T06:11:18+00:00" + "time": "2019-10-28T13:29:38+00:00" }, { "name": "zendframework/zend-mvc", @@ -5321,16 +5436,16 @@ }, { "name": "zendframework/zend-mvc-i18n", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-mvc-i18n.git", - "reference": "90e64d1304385cfcf19447b6449514e8a720adfc" + "reference": "7a1b3aca3a8874adb32390d34794cdc525c1c909" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc-i18n/zipball/90e64d1304385cfcf19447b6449514e8a720adfc", - "reference": "90e64d1304385cfcf19447b6449514e8a720adfc", + "url": "https://api.github.com/repos/zendframework/zend-mvc-i18n/zipball/7a1b3aca3a8874adb32390d34794cdc525c1c909", + "reference": "7a1b3aca3a8874adb32390d34794cdc525c1c909", "shasum": "" }, "require": { @@ -5343,6 +5458,7 @@ "zendframework/zend-validator": "^2.6" }, "conflict": { + "phpspec/prophecy": "<1.8.0", "zendframework/zend-mvc": "<3.0.0" }, "require-dev": { @@ -5381,27 +5497,27 @@ "zf" ], "abandoned": "laminas/laminas-mvc-i18n", - "time": "2018-05-01T15:48:40+00:00" + "time": "2019-09-03T20:50:53+00:00" }, { "name": "zendframework/zend-mvc-plugin-flashmessenger", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-mvc-plugin-flashmessenger.git", - "reference": "1af2e2d69500da5ca31868c4817b6b7eb7e1cf47" + "reference": "b66064eb59d3b124a133d259aac3d9dd7cb81706" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-mvc-plugin-flashmessenger/zipball/1af2e2d69500da5ca31868c4817b6b7eb7e1cf47", - "reference": "1af2e2d69500da5ca31868c4817b6b7eb7e1cf47", + "url": "https://api.github.com/repos/zendframework/zend-mvc-plugin-flashmessenger/zipball/b66064eb59d3b124a133d259aac3d9dd7cb81706", + "reference": "b66064eb59d3b124a133d259aac3d9dd7cb81706", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-mvc": "^3.0", "zendframework/zend-session": "^2.8.5", - "zendframework/zend-stdlib": "^2.7 || ^3.0", + "zendframework/zend-stdlib": "^3.2.1", "zendframework/zend-view": "^2.10" }, "conflict": { @@ -5415,8 +5531,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev", - "dev-develop": "1.2.x-dev" + "dev-master": "1.2.x-dev", + "dev-develop": "1.3.x-dev" }, "zf": { "component": "Zend\\Mvc\\Plugin\\FlashMessenger" @@ -5438,20 +5554,20 @@ "zf" ], "abandoned": "laminas/laminas-mvc-plugin-flashmessenger", - "time": "2018-04-30T18:47:56+00:00" + "time": "2019-10-19T22:24:02+00:00" }, { "name": "zendframework/zend-paginator", - "version": "2.8.1", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-paginator.git", - "reference": "fd58828c8280a90f133b9e0af2fe1a7885d47206" + "reference": "2b4d07d9475ed581278a28d065b238a0941402e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-paginator/zipball/fd58828c8280a90f133b9e0af2fe1a7885d47206", - "reference": "fd58828c8280a90f133b9e0af2fe1a7885d47206", + "url": "https://api.github.com/repos/zendframework/zend-paginator/zipball/2b4d07d9475ed581278a28d065b238a0941402e2", + "reference": "2b4d07d9475ed581278a28d065b238a0941402e2", "shasum": "" }, "require": { @@ -5480,8 +5596,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.8.x-dev", + "dev-develop": "2.9.x-dev" }, "zf": { "component": "Zend\\Paginator", @@ -5497,14 +5613,14 @@ "license": [ "BSD-3-Clause" ], - "description": "zend-paginator is a flexible component for paginating collections of data and presenting that data to users.", - "homepage": "https://github.com/zendframework/zend-paginator", + "description": "Paginate collections of data from arbitrary sources", "keywords": [ + "ZendFramework", "paginator", - "zf2" + "zf" ], "abandoned": "laminas/laminas-paginator", - "time": "2018-01-30T15:52:44+00:00" + "time": "2019-08-21T13:31:03+00:00" }, { "name": "zendframework/zend-psr7bridge", @@ -5563,16 +5679,16 @@ }, { "name": "zendframework/zend-router", - "version": "3.2.0", + "version": "3.3.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-router.git", - "reference": "a80a7427afb8f736b9aeeb341a78dae855849291" + "reference": "b113a4cfd910ee4723079fa58a9bcf3198631620" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-router/zipball/a80a7427afb8f736b9aeeb341a78dae855849291", - "reference": "a80a7427afb8f736b9aeeb341a78dae855849291", + "url": "https://api.github.com/repos/zendframework/zend-router/zipball/b113a4cfd910ee4723079fa58a9bcf3198631620", + "reference": "b113a4cfd910ee4723079fa58a9bcf3198631620", "shasum": "" }, "require": { @@ -5580,7 +5696,7 @@ "php": "^5.6 || ^7.0", "zendframework/zend-http": "^2.8.1", "zendframework/zend-servicemanager": "^2.7.8 || ^3.3", - "zendframework/zend-stdlib": "^2.7.7 || ^3.1" + "zendframework/zend-stdlib": "^3.2.1" }, "conflict": { "zendframework/zend-mvc": "<3.0.0" @@ -5596,8 +5712,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev", - "dev-develop": "3.3.x-dev" + "dev-master": "3.3.x-dev", + "dev-develop": "4.0.x-dev" }, "zf": { "component": "Zend\\Router", @@ -5622,20 +5738,20 @@ "zf" ], "abandoned": "laminas/laminas-router", - "time": "2018-08-01T22:24:35+00:00" + "time": "2019-02-26T20:24:12+00:00" }, { "name": "zendframework/zend-serializer", - "version": "2.9.0", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "0172690db48d8935edaf625c4cba38b79719892c" + "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/0172690db48d8935edaf625c4cba38b79719892c", - "reference": "0172690db48d8935edaf625c4cba38b79719892c", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", + "reference": "6fb7ae016cfdf0cfcdfa2b989e6a65f351170e21", "shasum": "" }, "require": { @@ -5644,7 +5760,7 @@ "zendframework/zend-stdlib": "^2.7 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.25 || ^6.4.4", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-math": "^2.6 || ^3.0", "zendframework/zend-servicemanager": "^2.7.5 || ^3.0.3" @@ -5673,27 +5789,27 @@ "license": [ "BSD-3-Clause" ], - "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", + "description": "Serialize and deserialize PHP structures to a variety of representations", "keywords": [ "ZendFramework", "serializer", "zf" ], "abandoned": "laminas/laminas-serializer", - "time": "2018-05-14T18:45:18+00:00" + "time": "2019-10-19T08:06:30+00:00" }, { "name": "zendframework/zend-server", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-server.git", - "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e" + "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-server/zipball/23a2e9a5599c83c05da831cb7c649e8a7809595e", - "reference": "23a2e9a5599c83c05da831cb7c649e8a7809595e", + "url": "https://api.github.com/repos/zendframework/zend-server/zipball/d80c44700ebb92191dd9a3005316a6ab6637c0d1", + "reference": "d80c44700ebb92191dd9a3005316a6ab6637c0d1", "shasum": "" }, "require": { @@ -5728,7 +5844,7 @@ "zf" ], "abandoned": "laminas/laminas-server", - "time": "2018-04-30T22:21:28+00:00" + "time": "2019-10-16T18:27:05+00:00" }, { "name": "zendframework/zend-servicemanager", @@ -5801,28 +5917,28 @@ }, { "name": "zendframework/zend-session", - "version": "2.8.5", + "version": "2.9.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-session.git", - "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b" + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-session/zipball/2cfd90e1a2f6b066b9f908599251d8f64f07021b", - "reference": "2cfd90e1a2f6b066b9f908599251d8f64f07021b", + "url": "https://api.github.com/repos/zendframework/zend-session/zipball/c289c4d733ec23a389e25c7c451f4d062088511f", + "reference": "c289c4d733ec23a389e25c7c451f4d062088511f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-eventmanager": "^2.6.2 || ^3.0", - "zendframework/zend-stdlib": "^2.7 || ^3.0" + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "container-interop/container-interop": "^1.1", "mongodb/mongodb": "^1.0.1", "php-mock/php-mock-phpunit": "^1.1.2 || ^2.0", - "phpunit/phpunit": "^5.7.5 || >=6.0.13 <6.5.0", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.16", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-db": "^2.7", @@ -5841,8 +5957,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.8-dev", - "dev-develop": "2.9-dev" + "dev-master": "2.9.x-dev", + "dev-develop": "2.10.x-dev" }, "zf": { "component": "Zend\\Session", @@ -5858,27 +5974,27 @@ "license": [ "BSD-3-Clause" ], - "description": "manage and preserve session data, a logical complement of cookie data, across multiple page requests by the same client", + "description": "Object-oriented interface to PHP sessions and storage", "keywords": [ "ZendFramework", "session", "zf" ], "abandoned": "laminas/laminas-session", - "time": "2018-02-22T16:33:54+00:00" + "time": "2019-10-28T19:40:43+00:00" }, { "name": "zendframework/zend-soap", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-soap.git", - "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284" + "reference": "8762d79efa220d82529c43ce08d70554146be645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/af03c32f0db2b899b3df8cfe29aeb2b49857d284", - "reference": "af03c32f0db2b899b3df8cfe29aeb2b49857d284", + "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/8762d79efa220d82529c43ce08d70554146be645", + "reference": "8762d79efa220d82529c43ce08d70554146be645", "shasum": "" }, "require": { @@ -5919,7 +6035,7 @@ "zf2" ], "abandoned": "laminas/laminas-soap", - "time": "2018-01-29T17:51:26+00:00" + "time": "2019-04-30T16:45:35+00:00" }, { "name": "zendframework/zend-stdlib", @@ -5970,16 +6086,16 @@ }, { "name": "zendframework/zend-text", - "version": "2.7.0", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-text.git", - "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac" + "reference": "41e32dafa4015e160e2f95a7039554385c71624d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-text/zipball/ca987dd4594f5f9508771fccd82c89bc7fbb39ac", - "reference": "ca987dd4594f5f9508771fccd82c89bc7fbb39ac", + "url": "https://api.github.com/repos/zendframework/zend-text/zipball/41e32dafa4015e160e2f95a7039554385c71624d", + "reference": "41e32dafa4015e160e2f95a7039554385c71624d", "shasum": "" }, "require": { @@ -6015,20 +6131,20 @@ "zf" ], "abandoned": "laminas/laminas-text", - "time": "2018-04-30T14:55:10+00:00" + "time": "2019-10-16T20:36:27+00:00" }, { "name": "zendframework/zend-uri", - "version": "2.6.1", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-uri.git", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f" + "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/3b6463645c6766f78ce537c70cb4fdabee1e725f", - "reference": "3b6463645c6766f78ce537c70cb4fdabee1e725f", + "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/bfc4a5b9a309711e968d7c72afae4ac50c650083", + "reference": "bfc4a5b9a309711e968d7c72afae4ac50c650083", "shasum": "" }, "require": { @@ -6043,8 +6159,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.6.x-dev", - "dev-develop": "2.7.x-dev" + "dev-master": "2.7.x-dev", + "dev-develop": "2.8.x-dev" } }, "autoload": { @@ -6063,29 +6179,31 @@ "zf" ], "abandoned": "laminas/laminas-uri", - "time": "2018-04-30T13:40:08+00:00" + "time": "2019-10-07T13:35:33+00:00" }, { "name": "zendframework/zend-validator", - "version": "2.11.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "f0789b4c4c099afdd2ecc58cc209a26c64bd4f17" + "reference": "b54acef1f407741c5347f2a97f899ab21f2229ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/f0789b4c4c099afdd2ecc58cc209a26c64bd4f17", - "reference": "f0789b4c4c099afdd2ecc58cc209a26c64bd4f17", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/b54acef1f407741c5347f2a97f899ab21f2229ef", + "reference": "b54acef1f407741c5347f2a97f899ab21f2229ef", "shasum": "" }, "require": { "container-interop/container-interop": "^1.1", - "php": "^5.6 || ^7.0", - "zendframework/zend-stdlib": "^2.7.6 || ^3.1" + "php": "^7.1", + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "zendframework/zend-cache": "^2.6.1", "zendframework/zend-coding-standard": "~1.0.0", @@ -6113,8 +6231,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.11.x-dev", - "dev-develop": "2.12.x-dev" + "dev-master": "2.13.x-dev", + "dev-develop": "2.14.x-dev" }, "zf": { "component": "Zend\\Validator", @@ -6130,27 +6248,27 @@ "license": [ "BSD-3-Clause" ], - "description": "provides a set of commonly needed validators", - "homepage": "https://github.com/zendframework/zend-validator", + "description": "Validation classes for a wide range of domains, and the ability to chain validators to create complex validation criteria", "keywords": [ + "ZendFramework", "validator", - "zf2" + "zf" ], "abandoned": "laminas/laminas-validator", - "time": "2018-12-13T21:23:15+00:00" + "time": "2019-12-28T04:07:18+00:00" }, { "name": "zendframework/zend-view", - "version": "2.11.1", + "version": "2.11.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-view.git", - "reference": "0428d6b2a67c7058451394921c90c5576ac5b373" + "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-view/zipball/0428d6b2a67c7058451394921c90c5576ac5b373", - "reference": "0428d6b2a67c7058451394921c90c5576ac5b373", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/4f5cb653ed4c64bb8d9bf05b294300feb00c67f2", + "reference": "4f5cb653ed4c64bb8d9bf05b294300feb00c67f2", "shasum": "" }, "require": { @@ -6225,7 +6343,7 @@ "zf2" ], "abandoned": "laminas/laminas-view", - "time": "2018-12-10T16:37:55+00:00" + "time": "2019-02-19T17:40:15+00:00" }, { "name": "zendframework/zendrest", @@ -6267,16 +6385,16 @@ }, { "name": "zendframework/zendservice-amazon", - "version": "2.3.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/zendframework/ZendService_Amazon.git", - "reference": "95347a55fec8adaace67707fdd66de1ea005b346" + "reference": "0bb035f5b5fe88e443d8dcd96a8415fea2729508" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/ZendService_Amazon/zipball/95347a55fec8adaace67707fdd66de1ea005b346", - "reference": "95347a55fec8adaace67707fdd66de1ea005b346", + "url": "https://api.github.com/repos/zendframework/ZendService_Amazon/zipball/0bb035f5b5fe88e443d8dcd96a8415fea2729508", + "reference": "0bb035f5b5fe88e443d8dcd96a8415fea2729508", "shasum": "" }, "require": { @@ -6299,8 +6417,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev", - "dev-develop": "2.3.x-dev" + "dev-master": "2.3.x-dev", + "dev-develop": "2.4.x-dev" } }, "autoload": { @@ -6323,26 +6441,27 @@ "zf2" ], "abandoned": "aws/aws-sdk-php", - "time": "2017-06-22T01:59:37+00:00" + "time": "2019-02-07T18:15:54+00:00" }, { "name": "zendframework/zendservice-recaptcha", - "version": "3.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/ZendService_ReCaptcha.git", - "reference": "8caf28e3ab8c18d75534c0741ccd6949347d20e8" + "reference": "b21625c54f19ba5be5c90ab9fa167ca075cd1594" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/8caf28e3ab8c18d75534c0741ccd6949347d20e8", - "reference": "8caf28e3ab8c18d75534c0741ccd6949347d20e8", + "url": "https://api.github.com/repos/zendframework/ZendService_ReCaptcha/zipball/b21625c54f19ba5be5c90ab9fa167ca075cd1594", + "reference": "b21625c54f19ba5be5c90ab9fa167ca075cd1594", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "zendframework/zend-http": "^2.5.4", - "zendframework/zend-json": "^2.6.1 || ^3.0" + "zendframework/zend-json": "^2.6.1 || ^3.0", + "zendframework/zend-stdlib": "^3.2.1" }, "require-dev": { "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.5", @@ -6356,8 +6475,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev", - "dev-develop": "3.2.x-dev" + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" } }, "autoload": { @@ -6376,7 +6495,7 @@ "zf" ], "abandoned": "laminas/laminas-recaptcha", - "time": "2018-05-08T17:34:06+00:00" + "time": "2019-02-07T17:41:56+00:00" }, { "name": "zendframework/zendxml", @@ -6682,24 +6801,23 @@ }, { "name": "composer/semver", - "version": "1.4.2", + "version": "1.7.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "url": "https://api.github.com/repos/composer/semver/zipball/647490bbcaf7fc4891c58f47b825eb99d19c377a", + "reference": "647490bbcaf7fc4891c58f47b825eb99d19c377a", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpunit/phpunit": "^4.5 || ^5.0.5" }, "type": "library", "extra": { @@ -6740,28 +6858,29 @@ "validation", "versioning" ], - "time": "2016-08-30T16:08:34+00:00" + "time": "2020-12-03T15:47:16+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.1", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "dc523135366eb68f22268d069ea7749486458562" + "reference": "f27e06cd9675801df441b3656569b328e04aa37c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/dc523135366eb68f22268d069ea7749486458562", - "reference": "dc523135366eb68f22268d069ea7749486458562", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f27e06cd9675801df441b3656569b328e04aa37c", + "reference": "f27e06cd9675801df441b3656569b328e04aa37c", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", + "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "autoload": { @@ -6779,46 +6898,48 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2018-11-29T10:59:02+00:00" + "time": "2021-03-25T17:01:18+00:00" }, { - "name": "doctrine/instantiator", - "version": "1.0.5", + "name": "dmore/chrome-mink-driver", + "version": "2.8.0", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "url": "https://gitlab.com/behat-chrome/chrome-mink-driver.git", + "reference": "f85c8f86ca2e9000119c310577a6942683f7e280" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://gitlab.com/api/v4/projects/behat-chrome%2Fchrome-mink-driver/repository/archive.zip?sha=f85c8f86ca2e9000119c310577a6942683f7e280", + "reference": "f85c8f86ca2e9000119c310577a6942683f7e280", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "behat/mink": "^1.7", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "textalk/websocket": "^1.2.0" }, "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "mink/driver-testsuite": "dev-master", + "phpunit/phpunit": "^5.0.0", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "DMore\\ChromeDriver\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6827,45 +6948,44 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "Dorian More", + "email": "doriancmore@gmail.com" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", - "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14T21:17:01+00:00" + "description": "Mink driver for controlling chrome without selenium", + "time": "2021-04-25T06:49:32+00:00" }, { - "name": "doctrine/lexer", - "version": "v1.0.1", + "name": "doctrine/instantiator", + "version": "1.4.1", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.1 || ^8.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "doctrine/coding-standard": "^9", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, + "type": "library", "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -6874,38 +6994,31 @@ ], "authors": [ { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", "keywords": [ - "lexer", - "parser" + "constructor", + "instantiate" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2022-03-03T08:28:38+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.14.0", + "version": "v2.16.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "b788ea0af899cedc8114dca7db119c93b6685da2" + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/b788ea0af899cedc8114dca7db119c93b6685da2", - "reference": "b788ea0af899cedc8114dca7db119c93b6685da2", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c8afb599858876e95e8ebfcd97812d383fa23f02", + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02", "shasum": "" }, "require": { @@ -6916,18 +7029,15 @@ "ext-tokenizer": "*", "php": "^5.6 || ^7.0", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6", - "symfony/event-dispatcher": "^3.0 || ^4.0", - "symfony/filesystem": "^3.0 || ^4.0", - "symfony/finder": "^3.0 || ^4.0", - "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^3.0 || ^4.0 || ^5.0", + "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0", - "symfony/stopwatch": "^3.0 || ^4.0" - }, - "conflict": { - "hhvm": "*" + "symfony/process": "^3.0 || ^4.0 || ^5.0", + "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" }, "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", @@ -6936,11 +7046,12 @@ "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.1", "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", - "phpunitgoodpractices/traits": "^1.5.1", - "symfony/phpunit-bridge": "^4.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", + "phpunitgoodpractices/traits": "^1.8", + "symfony/phpunit-bridge": "^4.3 || ^5.0", + "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", @@ -6952,11 +7063,6 @@ "php-cs-fixer" ], "type": "application", - "extra": { - "branch-alias": { - "dev-master": "2.14-dev" - } - }, "autoload": { "psr-4": { "PhpCsFixer\\": "src/" @@ -6978,30 +7084,30 @@ "MIT" ], "authors": [ - { - "name": "Dariusz RumiÅ„ski", - "email": "dariusz.ruminski@gmail.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Dariusz RumiÅ„ski", + "email": "dariusz.ruminski@gmail.com" } ], "description": "A tool to automatically fix PHP code style", - "time": "2019-01-04T18:29:47+00:00" + "time": "2019-11-25T22:10:32+00:00" }, { "name": "instaclick/php-webdriver", - "version": "1.4.5", + "version": "1.4.14", "source": { "type": "git", "url": "https://github.com/instaclick/php-webdriver.git", - "reference": "6fa959452e774dcaed543faad3a9d1a37d803327" + "reference": "200b8df772b74d604bebf25ef42ad6f8ee6380a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/6fa959452e774dcaed543faad3a9d1a37d803327", - "reference": "6fa959452e774dcaed543faad3a9d1a37d803327", + "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/200b8df772b74d604bebf25ef42ad6f8ee6380a9", + "reference": "200b8df772b74d604bebf25ef42ad6f8ee6380a9", "shasum": "" }, "require": { @@ -7009,8 +7115,8 @@ "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "^4.8", - "satooshi/php-coveralls": "^1.0||^2.0" + "phpunit/phpunit": "^8.5 || ^9.5", + "satooshi/php-coveralls": "^1.0 || ^2.0" }, "type": "library", "extra": { @@ -7047,38 +7153,42 @@ "webdriver", "webtest" ], - "time": "2017-06-30T04:02:48+00:00" + "time": "2022-04-19T02:06:59+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.7.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", - "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^4.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7092,36 +7202,43 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { "name": "pdepend/pdepend", - "version": "2.5.2", + "version": "2.10.3", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239" + "reference": "da3166a06b4a89915920a42444f707122a1584c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/9daf26d0368d4a12bed1cacae1a9f3a6f0adf239", - "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/da3166a06b4a89915920a42444f707122a1584c9", + "reference": "da3166a06b4a89915920a42444f707122a1584c9", "shasum": "" }, "require": { "php": ">=5.3.7", - "symfony/config": "^2.3.0|^3|^4", - "symfony/dependency-injection": "^2.3.0|^3|^4", - "symfony/filesystem": "^2.3.0|^3|^4" + "symfony/config": "^2.3.0|^3|^4|^5|^6.0", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5|^6.0", + "symfony/filesystem": "^2.3.0|^3|^4|^5|^6.0" }, "require-dev": { - "phpunit/phpunit": "^4.8|^5.7", + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "phpunit/phpunit": "^4.8.36|^5.7.27", "squizlabs/php_codesniffer": "^2.0.0" }, "bin": [ "src/bin/pdepend" ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, "autoload": { "psr-4": { "PDepend\\": "src/main/php/PDepend" @@ -7132,26 +7249,26 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", - "time": "2017-12-13T13:21:38+00:00" + "time": "2022-02-23T07:53:09+00:00" }, { "name": "phar-io/manifest", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0" + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0", - "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", + "reference": "7761fcacf03b4d4f16e7ccb606d4879ca431fcf4", "shasum": "" }, "require": { "ext-dom": "*", "ext-phar": "*", - "phar-io/version": "^1.0.1", + "phar-io/version": "^2.0", "php": "^5.6 || ^7.0" }, "type": "library", @@ -7187,20 +7304,20 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "time": "2017-03-05T18:14:27+00:00" + "time": "2018-07-08T19:23:20+00:00" }, { "name": "phar-io/version", - "version": "1.0.1", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/phar-io/version.git", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", - "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "url": "https://api.github.com/repos/phar-io/version/zipball/45a2ec53a73c70ce41d55cedef9063630abaf1b6", + "reference": "45a2ec53a73c70ce41d55cedef9063630abaf1b6", "shasum": "" }, "require": { @@ -7234,27 +7351,27 @@ } ], "description": "Library for handling version information and constraints", - "time": "2017-03-05T17:38:23+00:00" + "time": "2018-07-08T19:19:57+00:00" }, { "name": "php-cs-fixer/diff", - "version": "v1.3.0", + "version": "v1.3.1", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/diff.git", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756" + "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/78bb099e9c16361126c86ce82ec4405ebab8e756", - "reference": "78bb099e9c16361126c86ce82ec4405ebab8e756", + "url": "https://api.github.com/repos/PHP-CS-Fixer/diff/zipball/dbd31aeb251639ac0b9e7e29405c1441907f5759", + "reference": "dbd31aeb251639ac0b9e7e29405c1441907f5759", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.23 || ^6.4.3", + "phpunit/phpunit": "^5.7.23 || ^6.4.3 || ^7.0", "symfony/process": "^3.3" }, "type": "library", @@ -7268,14 +7385,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, { "name": "SpacePossum" } @@ -7285,7 +7402,7 @@ "keywords": [ "diff" ], - "time": "2018-02-15T16:58:55+00:00" + "time": "2020-10-14T08:39:05+00:00" }, { "name": "phploc/phploc", @@ -7338,31 +7455,35 @@ }, { "name": "phpmd/phpmd", - "version": "2.6.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374" + "reference": "5664b95d484797582f5af9536238deb9ecde58a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/4e9924b2c157a3eb64395460fcf56b31badc8374", - "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/5664b95d484797582f5af9536238deb9ecde58a1", + "reference": "5664b95d484797582f5af9536238deb9ecde58a1", "shasum": "" }, "require": { + "composer/xdebug-handler": "^1.0", "ext-xml": "*", - "pdepend/pdepend": "^2.5", + "pdepend/pdepend": "^2.6", "php": ">=5.3.9" }, "require-dev": { - "phpunit/phpunit": "^4.0", + "easy-doc/easy-doc": "0.0.0 || ^1.3.2", + "gregwar/rst": "^1.0", + "mikey179/vfsstream": "^1.6.4", + "phpunit/phpunit": "^4.8.36 || ^5.7.27", "squizlabs/php_codesniffer": "^2.0" }, "bin": [ "src/bin/phpmd" ], - "type": "project", + "type": "library", "autoload": { "psr-0": { "PHPMD\\": "src/main/php" @@ -7379,20 +7500,20 @@ "homepage": "https://github.com/manuelpichler", "role": "Project Founder" }, - { - "name": "Other contributors", - "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", - "role": "Contributors" - }, { "name": "Marc Würth", "email": "ravage@bluewin.ch", "homepage": "https://github.com/ravage84", "role": "Project Maintainer" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" } ], "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", - "homepage": "http://phpmd.org/", + "homepage": "https://phpmd.org/", "keywords": [ "mess detection", "mess detector", @@ -7400,42 +7521,42 @@ "phpmd", "pmd" ], - "time": "2017-01-20T14:41:10+00:00" + "time": "2019-12-27T11:09:06+00:00" }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -7463,44 +7584,44 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "5.3.2", + "version": "6.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac" + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac", - "reference": "c89677919c5dd6d3b3852f230a663118762218ac", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", + "reference": "807e6013b00af69b6c5d9ceb4282d0393dbb9d8d", "shasum": "" }, "require": { "ext-dom": "*", "ext-xmlwriter": "*", - "php": "^7.0", - "phpunit/php-file-iterator": "^1.4.2", + "php": "^7.1", + "phpunit/php-file-iterator": "^2.0", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-token-stream": "^2.0.1", + "phpunit/php-token-stream": "^3.0", "sebastian/code-unit-reverse-lookup": "^1.0.1", - "sebastian/environment": "^3.0", + "sebastian/environment": "^3.1 || ^4.0", "sebastian/version": "^2.0.1", "theseer/tokenizer": "^1.1" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^7.0" }, "suggest": { - "ext-xdebug": "^2.5.5" + "ext-xdebug": "^2.6.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3.x-dev" + "dev-master": "6.1-dev" } }, "autoload": { @@ -7526,29 +7647,32 @@ "testing", "xunit" ], - "time": "2018-04-06T15:36:58+00:00" + "time": "2018-10-31T16:06:48+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.5", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", - "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", + "reference": "42c5ba5220e6904cbfe8b1a1bda7c0cfdc8c12f5", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -7563,7 +7687,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -7573,7 +7697,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2021-12-02T12:42:26+00:00" }, { "name": "phpunit/php-text-template", @@ -7618,28 +7742,28 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.9", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", - "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/2454ae1765516d20c4ffe103d85a58a9a3bd5662", + "reference": "2454ae1765516d20c4ffe103d85a58a9a3bd5662", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.1-dev" } }, "autoload": { @@ -7654,7 +7778,7 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", + "email": "sebastian@phpunit.de", "role": "lead" } ], @@ -7663,33 +7787,33 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2020-11-30T08:20:02+00:00" }, { "name": "phpunit/php-token-stream", - "version": "2.0.2", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "791198a2c6254db10131eecfe8c06670700904db" + "reference": "9c1da83261628cb24b6a6df371b6e312b3954768" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db", - "reference": "791198a2c6254db10131eecfe8c06670700904db", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9c1da83261628cb24b6a6df371b6e312b3954768", + "reference": "9c1da83261628cb24b6a6df371b6e312b3954768", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": "^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2.4" + "phpunit/phpunit": "^7.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.1-dev" } }, "autoload": { @@ -7712,57 +7836,58 @@ "keywords": [ "tokenizer" ], - "time": "2017-11-27T05:48:46+00:00" + "abandoned": true, + "time": "2021-07-26T12:15:06+00:00" }, { "name": "phpunit/phpunit", - "version": "6.5.13", + "version": "7.5.20", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693" + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693", - "reference": "0973426fb012359b2f18d3bd1e90ef1172839693", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9467db479d1b0487c99733bb1e7944d32deded2c", + "reference": "9467db479d1b0487c99733bb1e7944d32deded2c", "shasum": "" }, "require": { + "doctrine/instantiator": "^1.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", - "myclabs/deep-copy": "^1.6.1", - "phar-io/manifest": "^1.0.1", - "phar-io/version": "^1.0", - "php": "^7.0", + "myclabs/deep-copy": "^1.7", + "phar-io/manifest": "^1.0.2", + "phar-io/version": "^2.0", + "php": "^7.1", "phpspec/prophecy": "^1.7", - "phpunit/php-code-coverage": "^5.3", - "phpunit/php-file-iterator": "^1.4.3", + "phpunit/php-code-coverage": "^6.0.7", + "phpunit/php-file-iterator": "^2.0.1", "phpunit/php-text-template": "^1.2.1", - "phpunit/php-timer": "^1.0.9", - "phpunit/phpunit-mock-objects": "^5.0.9", - "sebastian/comparator": "^2.1", - "sebastian/diff": "^2.0", - "sebastian/environment": "^3.1", + "phpunit/php-timer": "^2.1", + "sebastian/comparator": "^3.0", + "sebastian/diff": "^3.0", + "sebastian/environment": "^4.0", "sebastian/exporter": "^3.1", "sebastian/global-state": "^2.0", "sebastian/object-enumerator": "^3.0.3", - "sebastian/resource-operations": "^1.0", + "sebastian/resource-operations": "^2.0", "sebastian/version": "^2.0.1" }, "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2", - "phpunit/dbunit": "<3.0" + "phpunit/phpunit-mock-objects": "*" }, "require-dev": { "ext-pdo": "*" }, "suggest": { + "ext-soap": "*", "ext-xdebug": "*", - "phpunit/php-invoker": "^1.1" + "phpunit/php-invoker": "^2.0" }, "bin": [ "phpunit" @@ -7770,7 +7895,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "6.5.x-dev" + "dev-master": "7.5-dev" } }, "autoload": { @@ -7796,87 +7921,27 @@ "testing", "xunit" ], - "time": "2018-09-08T15:10:43+00:00" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "5.0.10", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f", - "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f", - "shasum": "" - }, - "require": { - "doctrine/instantiator": "^1.0.5", - "php": "^7.0", - "phpunit/php-text-template": "^1.2.1", - "sebastian/exporter": "^3.1" - }, - "conflict": { - "phpunit/phpunit": "<6.0" - }, - "require-dev": { - "phpunit/phpunit": "^6.5.11" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "abandoned": true, - "time": "2018-08-09T05:50:03+00:00" + "time": "2020-01-08T08:45:45+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", - "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": ">=5.6" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -7901,34 +7966,34 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "time": "2020-11-30T08:15:22+00:00" }, { "name": "sebastian/comparator", - "version": "2.1.3", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", - "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1071dfcef776a57013124ff35e1fc41ccd294758", + "reference": "1071dfcef776a57013124ff35e1fc41ccd294758", "shasum": "" }, "require": { - "php": "^7.0", - "sebastian/diff": "^2.0 || ^3.0", + "php": ">=7.1", + "sebastian/diff": "^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { - "phpunit/phpunit": "^6.4" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -7941,6 +8006,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -7952,10 +8021,6 @@ { "name": "Bernhard Schussek", "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" } ], "description": "Provides the functionality to compare PHP values for equality", @@ -7965,32 +8030,33 @@ "compare", "equality" ], - "time": "2018-02-01T13:46:46+00:00" + "time": "2020-11-30T08:04:30+00:00" }, { "name": "sebastian/diff", - "version": "2.0.1", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd" + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", - "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/14f72dd46eaf2f2293cbe79c93cc0bc43161a211", + "reference": "14f72dd46eaf2f2293cbe79c93cc0bc43161a211", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^6.2" + "phpunit/phpunit": "^7.5 || ^8.0", + "symfony/process": "^2 || ^3.3 || ^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -8003,46 +8069,52 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], "description": "Diff implementation", "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "diff" + "diff", + "udiff", + "unidiff", + "unified diff" ], - "time": "2017-08-03T08:09:46+00:00" + "time": "2020-11-30T07:59:04+00:00" }, { "name": "sebastian/environment", - "version": "3.1.0", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5" + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5", - "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", + "reference": "d47bbbad83711771f167c72d4e3f25f7fcc1f8b0", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.1" }, "require-dev": { - "phpunit/phpunit": "^6.1" + "phpunit/phpunit": "^7.5" + }, + "suggest": { + "ext-posix": "*" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1.x-dev" + "dev-master": "4.2-dev" } }, "autoload": { @@ -8067,29 +8139,29 @@ "environment", "hhvm" ], - "time": "2017-07-01T08:51:00+00:00" + "time": "2020-11-30T07:53:42+00:00" }, { "name": "sebastian/exporter", - "version": "3.1.0", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937" + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937", - "reference": "234199f4528de6d12aaa58b612e98f7d36adb937", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", + "reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/recursion-context": "^3.0" }, "require-dev": { "ext-mbstring": "*", - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -8107,6 +8179,10 @@ "BSD-3-Clause" ], "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, { "name": "Jeff Welch", "email": "whatthejeff@gmail.com" @@ -8115,17 +8191,13 @@ "name": "Volker Dusch", "email": "github@wallbash.com" }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, { "name": "Adam Harvey", "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], "description": "Provides the functionality to export PHP variables for visualization", @@ -8134,27 +8206,31 @@ "export", "exporter" ], - "time": "2017-04-03T13:19:02+00:00" + "time": "2021-11-11T13:51:24+00:00" }, { "name": "sebastian/finder-facade", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/finder-facade.git", - "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f" + "reference": "167c45d131f7fc3d159f56f191a0a22228765e16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", - "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/167c45d131f7fc3d159f56f191a0a22228765e16", + "reference": "167c45d131f7fc3d159f56f191a0a22228765e16", "shasum": "" }, "require": { - "symfony/finder": "~2.3|~3.0|~4.0", - "theseer/fdomdocument": "~1.3" + "php": "^7.1", + "symfony/finder": "^2.3|^3.0|^4.0|^5.0", + "theseer/fdomdocument": "^1.6" }, "type": "library", + "extra": { + "branch-alias": [] + }, "autoload": { "classmap": [ "src/" @@ -8173,7 +8249,8 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", - "time": "2017-11-18T17:31:49+00:00" + "abandoned": true, + "time": "2020-01-16T08:08:45+00:00" }, { "name": "sebastian/global-state", @@ -8228,20 +8305,20 @@ }, { "name": "sebastian/object-enumerator", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5" + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5", - "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", + "reference": "e67f6d32ebd0c749cf9d1dbd9f226c727043cdf2", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=7.0", "sebastian/object-reflector": "^1.1.1", "sebastian/recursion-context": "^3.0" }, @@ -8271,24 +8348,24 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-08-03T12:35:26+00:00" + "time": "2020-11-30T07:40:27+00:00" }, { "name": "sebastian/object-reflector", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "773f97c67f28de00d397be301821b06708fca0be" + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be", - "reference": "773f97c67f28de00d397be301821b06708fca0be", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", + "reference": "9b8772b9cbd456ab45d4a598d2dd1a1bced6363d", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -8316,25 +8393,26 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "time": "2017-03-29T09:07:27+00:00" + "time": "2020-11-30T07:37:18+00:00" }, { "name": "sebastian/phpcpd", - "version": "3.0.1", + "version": "4.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpcpd.git", - "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564" + "reference": "0d9afa762f2400de077b2192f4a9d127de0bb78e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564", - "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/0d9afa762f2400de077b2192f4a9d127de0bb78e", + "reference": "0d9afa762f2400de077b2192f4a9d127de0bb78e", "shasum": "" }, "require": { - "php": "^5.6|^7.0", - "phpunit/php-timer": "^1.0.6", + "ext-dom": "*", + "php": "^7.1", + "phpunit/php-timer": "^2.0", "sebastian/finder-facade": "^1.1", "sebastian/version": "^1.0|^2.0", "symfony/console": "^2.7|^3.0|^4.0" @@ -8345,7 +8423,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -8366,24 +8444,24 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2017-11-16T08:49:28+00:00" + "time": "2018-09-17T17:17:27+00:00" }, { "name": "sebastian/recursion-context", - "version": "3.0.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8" + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", - "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/367dcba38d6e1977be014dc4b22f47a484dac7fb", + "reference": "367dcba38d6e1977be014dc4b22f47a484dac7fb", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=7.0" }, "require-dev": { "phpunit/phpunit": "^6.0" @@ -8404,14 +8482,14 @@ "BSD-3-Clause" ], "authors": [ - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, { "name": "Adam Harvey", "email": "aharvey@php.net" @@ -8419,29 +8497,29 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2017-03-03T06:23:57+00:00" + "time": "2020-11-30T07:34:24+00:00" }, { "name": "sebastian/resource-operations", - "version": "1.0.0", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/31d35ca87926450c44eae7e2611d45a7a65ea8b3", + "reference": "31d35ca87926450c44eae7e2611d45a7a65ea8b3", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0-dev" } }, "autoload": { @@ -8461,7 +8539,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "time": "2020-11-30T07:30:19+00:00" }, { "name": "sebastian/version", @@ -8508,16 +8586,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.4.0", + "version": "3.5.3", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "379deb987e26c7cd103a7b387aea178baec96e48" + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/379deb987e26c7cd103a7b387aea178baec96e48", - "reference": "379deb987e26c7cd103a7b387aea178baec96e48", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", + "reference": "557a1fc7ac702c66b0bbfe16ab3d55839ef724cb", "shasum": "" }, "require": { @@ -8550,83 +8628,25 @@ } ], "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", - "homepage": "http://www.squizlabs.com/php-codesniffer", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", "standards" ], - "time": "2018-12-19T23:57:18+00:00" - }, - { - "name": "symfony/cache-contracts", - "version": "v1.1.7", - "source": { - "type": "git", - "url": "https://github.com/symfony/cache-contracts.git", - "reference": "af50d14ada9e4e82cfabfabdc502d144f89be0a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/af50d14ada9e4e82cfabfabdc502d144f89be0a1", - "reference": "af50d14ada9e4e82cfabfabdc502d144f89be0a1", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/cache": "^1.0" - }, - "suggest": { - "symfony/cache-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Cache\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to caching", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-10-04T21:43:27+00:00" + "time": "2019-12-04T04:46:47+00:00" }, { "name": "symfony/config", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "17c5d8941eb75a03d19bc76a43757738632d87b3" + "reference": "bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/17c5d8941eb75a03d19bc76a43757738632d87b3", - "reference": "17c5d8941eb75a03d19bc76a43757738632d87b3", + "url": "https://api.github.com/repos/symfony/config/zipball/bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f", + "reference": "bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f", "shasum": "" }, "require": { @@ -8648,11 +8668,6 @@ "symfony/yaml": "To use the yaml reference dumper" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" @@ -8677,20 +8692,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/console", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a" + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a700b874d3692bc8342199adfb6d3b99f62cc61a", - "reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a", + "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", "shasum": "" }, "require": { @@ -8702,6 +8717,9 @@ "symfony/dependency-injection": "<3.4", "symfony/process": "<3.3" }, + "provide": { + "psr/log-implementation": "1.0" + }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.3|~4.0", @@ -8711,17 +8729,12 @@ "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log-implementation": "For using the console logger", + "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" @@ -8746,31 +8759,26 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2019-01-04T04:42:43+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "12f86295c46c36af9896cf21db6b6b8a1465315d" + "reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/12f86295c46c36af9896cf21db6b6b8a1465315d", - "reference": "12f86295c46c36af9896cf21db6b6b8a1465315d", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/da3d9da2ce0026771f5fe64cb332158f1bd2bc33", + "reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\CssSelector\\": "" @@ -8784,14 +8792,14 @@ "MIT" ], "authors": [ - { - "name": "Jean-François Simon", - "email": "jeanfrancois.simon@sensiolabs.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" @@ -8799,20 +8807,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2019-01-02T09:30:52+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/debug", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186" + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186", - "reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186", + "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", "shasum": "" }, "require": { @@ -8826,11 +8834,6 @@ "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Debug\\": "" @@ -8855,20 +8858,21 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "abandoned": "symfony/error-handler", + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/dependency-injection", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "928a38b18bd632d67acbca74d0b2eed09915e83e" + "reference": "51d2a2708c6ceadad84393f8581df1dcf9e5e84b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/928a38b18bd632d67acbca74d0b2eed09915e83e", - "reference": "928a38b18bd632d67acbca74d0b2eed09915e83e", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/51d2a2708c6ceadad84393f8581df1dcf9e5e84b", + "reference": "51d2a2708c6ceadad84393f8581df1dcf9e5e84b", "shasum": "" }, "require": { @@ -8897,11 +8901,6 @@ "symfony/yaml": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" @@ -8926,20 +8925,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2019-01-05T12:26:58+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d1cdd46c53c264a2bd42505bd0e8ce21423bd0e2" + "reference": "31fde73757b6bad247c54597beef974919ec6860" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d1cdd46c53c264a2bd42505bd0e8ce21423bd0e2", - "reference": "d1cdd46c53c264a2bd42505bd0e8ce21423bd0e2", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/31fde73757b6bad247c54597beef974919ec6860", + "reference": "31fde73757b6bad247c54597beef974919ec6860", "shasum": "" }, "require": { @@ -8951,6 +8950,7 @@ "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.8|~3.0|~4.0", + "symfony/debug": "~3.4|~4.4", "symfony/dependency-injection": "~3.3|~4.0", "symfony/expression-language": "~2.8|~3.0|~4.0", "symfony/stopwatch": "~2.8|~3.0|~4.0" @@ -8960,11 +8960,6 @@ "symfony/http-kernel": "" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" @@ -8989,31 +8984,26 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2019-01-01T18:08:36+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/finder", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e" + "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e", - "reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e", + "url": "https://api.github.com/repos/symfony/finder/zipball/b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", + "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Finder\\": "" @@ -9038,38 +9028,42 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-11-16T17:02:08+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.10.0", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631" + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", - "reference": "9050816e2ca34a8e916c3a0ae8b9c2fccf68b631", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/bf44a9fd41feaac72b074de600314a93e2ae78e2", + "reference": "bf44a9fd41feaac72b074de600314a93e2ae78e2", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -9093,31 +9087,26 @@ "portable", "shim" ], - "time": "2018-09-21T13:07:52+00:00" + "time": "2022-05-24T11:49:31+00:00" }, { "name": "symfony/process", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c" + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", - "reference": "0d41dd7d95ed179aed6a13393b0f4f97bfa2d25c", + "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" @@ -9142,89 +9131,26 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2019-01-02T21:24:08+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v1.1.8", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/container": "^1.0" - }, - "suggest": { - "symfony/service-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-10-14T12:27:06+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.21", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "af55d31cb58c5452d2c160655fa1968b872a8084" + "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/af55d31cb58c5452d2c160655fa1968b872a8084", - "reference": "af55d31cb58c5452d2c160655fa1968b872a8084", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/298b81faad4ce60e94466226b2abbb8c9bca7462", + "reference": "298b81faad4ce60e94466226b2abbb8c9bca7462", "shasum": "" }, "require": { "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.4-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" @@ -9249,80 +9175,65 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2019-01-01T13:45:19+00:00" + "time": "2020-10-24T10:57:07+00:00" }, { - "name": "symfony/var-exporter", - "version": "v4.3.8", + "name": "textalk/websocket", + "version": "1.4.3", "source": { "type": "git", - "url": "https://github.com/symfony/var-exporter.git", - "reference": "097aa4c02954dabe9d508229be86213723973ac0" + "url": "https://github.com/Textalk/websocket-php.git", + "reference": "6bc13b277bbca67ee1d697dd74e279da1c2df9ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/097aa4c02954dabe9d508229be86213723973ac0", - "reference": "097aa4c02954dabe9d508229be86213723973ac0", + "url": "https://api.github.com/repos/Textalk/websocket-php/zipball/6bc13b277bbca67ee1d697dd74e279da1c2df9ca", + "reference": "6bc13b277bbca67ee1d697dd74e279da1c2df9ca", "shasum": "" }, "require": { - "php": "^7.1.3" + "php": "^7.1", + "psr/log": "^1.0" }, "require-dev": { - "symfony/var-dumper": "^4.1.1" + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^7.0|^8.0|^9.0", + "squizlabs/php_codesniffer": "^3.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, "autoload": { "psr-4": { - "Symfony\\Component\\VarExporter\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "WebSocket\\": "lib" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "ISC" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fredrik Liljegren" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sören Jensen", + "email": "soren@abicart.se" } ], - "description": "A blend of var_export() + serialize() to turn any serializable data structure to plain PHP code", - "homepage": "https://symfony.com", - "keywords": [ - "clone", - "construct", - "export", - "hydrate", - "instantiate", - "serialize" - ], - "time": "2019-11-11T12:48:54+00:00" + "description": "WebSocket client and server", + "time": "2020-12-05T10:11:34+00:00" }, { "name": "theseer/fdomdocument", - "version": "1.6.6", + "version": "1.6.7", "source": { "type": "git", "url": "https://github.com/theseer/fDOMDocument.git", - "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca" + "reference": "5cddd4f9076a9a2b85c5135935bba2dcb3ed7574" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/6e8203e40a32a9c770bcb62fe37e68b948da6dca", - "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/5cddd4f9076a9a2b85c5135935bba2dcb3ed7574", + "reference": "5cddd4f9076a9a2b85c5135935bba2dcb3ed7574", "shasum": "" }, "require": { @@ -9330,6 +9241,9 @@ "lib-libxml": "*", "php": ">=5.3.3" }, + "require-dev": { + "php": ">=7.3" + }, "type": "library", "autoload": { "classmap": [ @@ -9349,20 +9263,21 @@ ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", "homepage": "https://github.com/theseer/fDOMDocument", - "time": "2017-06-30T11:53:12+00:00" + "abandoned": true, + "time": "2022-01-25T23:10:35+00:00" }, { "name": "theseer/tokenizer", - "version": "1.1.0", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b" + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b", - "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9", + "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9", "shasum": "" }, "require": { @@ -9389,58 +9304,7 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "time": "2017-04-07T12:08:54+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9", - "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9", - "shasum": "" - }, - "require": { - "php": "^5.3.3 || ^7.0", - "symfony/polyfill-ctype": "^1.8" - }, - "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2018-12-25T11:19:39+00:00" + "time": "2019-06-13T22:48:21+00:00" } ], "aliases": [], @@ -9453,7 +9317,10 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0.8" + "php": ">=7.1" }, - "platform-dev": [] + "platform-dev": [], + "platform-overrides": { + "php": "7.1" + } } diff --git a/config/vufind/Alma.ini b/config/vufind/Alma.ini index e6dcfde3a2644716aa3d9fb16e2ab631d45473eb..1a5af544591d3008f112563bc080ea3158e65002 100644 --- a/config/vufind/Alma.ini +++ b/config/vufind/Alma.ini @@ -3,7 +3,32 @@ apiBaseUrl = "https://api-eu.hosted.exlibrisgroup.com/almaws/v1" ; An API key configured to allow access to Alma: apiKey = "your-key-here" - +; Timeout in seconds when making HTTP requests to the Alma APIs: +http_timeout = 30 +; Patron login method to use. The following options are available: +; vufind Use VuFind's user database for authentication -- patrons are retrieved +; from Alma without a password (default). This is only useful when +; VuFind is set up to use AlmaDatabase authentication in config.ini. In any +; other setting it would allow login without proper password. +; password Use password authentication with Alma internal users. This method is +; supported with the normal ILS and MultiILS authentication methods in +; VuFind. Note that it will not work with external users in Alma since they +; don't have a password in Alma. +; email Username needs to be a valid email address or other unique identifier for +; the user. An authentication link is sent by email to user's preferred +; email address registered in Alma. This method can be used with both +; internal and external Alma users and it is supported with the normal ILS +; and MultiILS authentication methods in VuFind. +;loginMethod = vufind + +; Translation prefix to use for codes coming from Alma. By default a prefix is not +; used, but a prefix may be used to distinguish the codes from any other +; translations (or other libraries). To use a simple prefix in the default text +; domain: +; translationPrefix = "alma_" +; To use a separate Alma text domain: +; translationPrefix = "Alma::" +;translationPrefix = "Alma::" [Holds] ; HMACKeys - A list of hold form element names that will be analyzed for consistency @@ -28,7 +53,6 @@ extraHoldFields = comments:requiredByDate:pickUpLocation ; 3) a value within the Location IDs returned by getPickUpLocations() defaultPickUpLocation = "" - ; The "NewUser" section defines some default values that are used when creating an account ; in Alma via its API. This is only relevant if you use the authentication method "AlmaDatabase" ; in the "Authentication" section of the "config.ini" file. @@ -36,7 +60,7 @@ defaultPickUpLocation = "" ; Mandatory. The Alma user record type. Usually "PUBLIC". recordType = PUBLIC -; Mandatory. The Alma user account type. Usually this is "INTERNAL" if you use the AlmaDatabase +; Mandatory. The Alma user account type. Usually this is "INTERNAL" if you use the AlmaDatabase ; authentication method. accountType = INTERNAL @@ -45,27 +69,27 @@ status = ACTIVE ; Mandatory. The user group to which the new Alma account should belong. Use the code of one of ; the user groups that are defined in Alma (see "Alma Configuration -> User Management -> User Groups"). -userGroup = +userGroup = ; Mandatory. The type of ID under which the username should be saved to Alma. Log in to the ExLibris developer ; network and check the Alma API documentation for possible values on this site: ; https://developers.exlibrisgroup.com/alma/apis/xsd/rest_user.xsd?tags=POST#user_identifier -idType = +idType = ; Mandatory. The preferred language of the new Alma account. This should normally be the Alma language ; code of your local language (see "Alma Configuration -> General -> Institution Languages"). -preferredLanguage = +preferredLanguage = ; Mandatory. The type of eMail of the users eMail address. Log in to the ExLibris developer network and ; check the Alma API documentation for possible values on this site: ; https://developers.exlibrisgroup.com/alma/apis/xsd/rest_user.xsd?tags=POST#email_types -emailType = +emailType = ; Optional. Set the time period when the Alma account should expire. The given period will be added to the ; point in time of the Alma account creation. Use the DateInterval notation of PHP to express the period. See: ; https://secure.php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters ; If not set, 1 year (P1Y) will be used as default value. -expiryDate = +expiryDate = ; Optional. Set the time period that should be used for the Alma user account purge date. The given period ; will be added to the point in time of the Alma account creation. Use the DateInterval notation of PHP to @@ -75,39 +99,18 @@ expiryDate = purgeDate = -[FulfillmentUnits] -; Specify the association of fulfillment units and its locations. Take the codes from: -; Alma Configuration -> Fulfillment -> Fulfillment Units -> [Choose Fulfillment Unit] -> Fulfillment Unit Locations. -; Tip: Export the list from Alma as Excel and use the CONCATENATE() formula to generate this list. -; Format: FULFILLMENT_UNIT_CODE[] = LOCATION_CODE -; Example: -; STACKS[] = stack1 -; STACKS[] = stack2 -; STACKS[] = stack3 -; LIMITED[] = periodicalroom -; LIMITED[] = musicrefs -; SHORTLOAN[] = office1 -; SHORTLOAN[] = office2 - -[Requestable] -; Specify for which combination of user group and fulfillment unit (see above) the request link -; should be displayed (who is allowed to request what). Define every combination of fulfillment unit -; and user group and assign "N" for "No, not requestable for this user group" or "Y" for "Yes, is -; requestable for this user group". You will find the user group codes here: -; Alma Configuration -> User Management -> User Groups -; Format: FULFILLMENT_UNIT_CODE[USER_GROUP_CODE] = N -; Example: -; STACKS[STAFF] = Y -; STACKS[STUDENT] = Y -; STACKS[GUEST] = Y -; LIMITED[STAFF] = Y -; LIMITED[STUDENT] = N -; LIMITED[GUEST] = N -; SHORTLOAN[STAFF] = Y -; SHORTLOAN[STUDENT] = Y -; SHORTLOAN[GUEST] = N - [Webhook] ; The webhook secret. This must be the same value that was added to the Alma webhook configuration as a secret. secret = YOUR_WEBHOOK_SECRET_FROM_ALMA +[Holdings] +; Max. no. of items displayed in the holdings tab. A paginator is used when there are +; more holdings to display. +itemLimit = "10" +; The digital delivery URL for your Alma instance. Replace at least SOMETHING and +; INSTITUTION with correct values. +;digitalDeliveryUrl = "https://SOMETHING.alma.exlibrisgroup.com/view/delivery/INSTITUTION/%%id%%" +; Inventory types to display from Alma. A colon-separated list. Supported values +; are "physical", "electronic" and "digital". By default all are displayed. +;inventoryTypes = "physical:electronic" + diff --git a/config/vufind/CollectionTabs.ini b/config/vufind/CollectionTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..6d9c078964602648efcc2fe09b2a9596f31c31f3 --- /dev/null +++ b/config/vufind/CollectionTabs.ini @@ -0,0 +1,7 @@ +; This file controls the tab display in the Collection view (as opposed to the +; single record view). The structure is identical to RecordTabs.ini; see the +; comments in that file for details on the meanings of the various settings. +[VuFind\RecordDriver\AbstractBase] +tabs[CollectionList] = CollectionList +tabs[HierarchyTree] = CollectionHierarchyTree +defaultTab = null diff --git a/config/vufind/Demo.ini b/config/vufind/Demo.ini index ca5936a68f65077e753ac862553f9526dfa8c0d6..b4a4fe6db1acbcbc61821ab638bf5d130e5c2b7d 100644 --- a/config/vufind/Demo.ini +++ b/config/vufind/Demo.ini @@ -10,6 +10,17 @@ storageRetrievalRequests = true ; Whether to support ILL requests ILLRequests = true +; Patron login method to use. The following options are available: +; password Normal username+password (the default) +; email Username is an email address +;loginMethod = email + +; Holdings configuration options. +[Holdings] +; Max. no. of items displayed in the holdings tab. A paginator is used when there are +; more holdings to display. (Default = no limit) +;itemLimit = "10" + ; Configuration for retrieving sample records [Records] ; Search backend to pull records from @@ -33,6 +44,11 @@ services[] = 'custom' ; driver. ;historicTransactions = '[{"id":"1234", ... "dueDate": "01/01/2017"}]'; +; This setting may be used to simulate suppressed records (through the +; getSuppressedRecords method): +;suppressed[] = suppressedRecordId1 +;suppressed[] = suppressedRecordId2 + ; This section can be used to create a set of fake users recognized by the ; Demo driver. If it is uncommented, only usernames and passwords listed here ; will be recognized for ILS login. If it is commented out, all username/password diff --git a/config/vufind/EDS.ini b/config/vufind/EDS.ini index 0354ae03da0830828688005d69d8ea4ba56f5c78..5d02f5db9c8235690b405608ff9888ddef5aca67 100644 --- a/config/vufind/EDS.ini +++ b/config/vufind/EDS.ini @@ -32,9 +32,9 @@ default_side_recommend[] = SideFacets:Facets:CheckboxFacets:EDS default_noresults_recommend[] = SwitchType default_noresults_recommend[] = RemoveFilters -; When you filter a search using facets, VuFind will present a checkbox that can -; be used to apply those filters to the next search you perform. This setting -; controls its default state: on (true) or off (false). +; When you filter a search using facets, should VuFind retain your current filters +; on the next search and provide a reset button to clear them (true), or should it +; always perform new searches unfiltered (false)? retain_filters_by_default = true ; This is the timeout in seconds when communicating with the EDS server. @@ -108,6 +108,25 @@ SEARCHMODE = "Search Mode" ; above for more details. [FacetsTop] +; This section controls where facet labels are retrieved from when facets are not +; explicitly configured. +[FacetLabels] +; This setting lists configuration sections containing facet field => label +; mappings. Later values will override earlier values. These mappings will be used +; only when a label is not explicitly configured (i.e. through SideFacets settings). +; If you customize your facet display, be sure to add any new facet configuration +; sections to this list to ensure proper display in search history, etc. +labelSections[] = Advanced_Facets +labelSections[] = FacetsTop +labelSections[] = Facets + +; This setting lists configuration settings defining checkbox facets. If you use +; a custom section to configure additional facets, be sure to add it to this list +; so labels display correctly in history, the advanced search editor, etc. If you +; are using the reverse label => filter format rather than filter => label, you +; should prefix the section name with a ~ character to ensure proper loading. +checkboxSections[] = CheckboxFacets + ; Facet display settings [Results_Settings] ; By default, the side facets will only show 6 facets and then the "show more" @@ -212,14 +231,14 @@ organization_id = "VuFind 2.x from MyUniversity" [List] view=full -; This section controls the behavior of the Autocomplete of EDS +; This section controls the behavior of the Autocomplete of EDS ; If enabled the option "autocomplete" is send for UIDAuth to get the token -; and the url. +; and the url. [Autocomplete] ; Set this to false to disable all autocomplete behavior enabled = true -; Define a default_handler +; Define a default_handler default_handler = Eds ; Auto-submit autocomplete on click or enter @@ -230,7 +249,7 @@ auto_submit = true ; behavior when that search type is selected. (default: Eds:rawqueries) ; These values are available: None, Eds:rawqueries and Eds:holdings ; Use None to disable autocomplete for a specific search type -; Use Eds:holdings for title completion in PubFinder. +; Use Eds:holdings for title completion in PubFinder. ; Use Eds:rawqueries for completion of basic textual queries. [Autocomplete_Types] ;AllFields = Eds:rawqueries diff --git a/config/vufind/FeedbackForms.yaml b/config/vufind/FeedbackForms.yaml index f875cbe20050e989a83e0db0a30c9d6ea6575149..445644fb95644f8639ca358702ad9a9a47a21452 100644 --- a/config/vufind/FeedbackForms.yaml +++ b/config/vufind/FeedbackForms.yaml @@ -24,6 +24,15 @@ # [Feedback] section) # email (string) Recipient email address (default = recipient_email setting from # config.ini [Feedback] section) +# +# Multiple recipients can be configured using a list: +# +# recipient: +# - name: Recipient 1 +# email: recipient1@email +# - name: Recipient 2 +# email: recipient2@email +# # response (string) Reponse after form submit (translation key) # senderInfoRequired (boolean) Require sender to fill out name and email fields # @@ -40,19 +49,29 @@ # required (boolean) Is the element required? # settings (array) HTML attributes as key-value pairs, for example: # - [class, "custom-css-class another-class"] -# type (string) Element type (text|textarea|email|url|select) -# help (string) Element help text (translation key) +# type (string) Element type (text|textarea|email|url|select|radio|checkbox) +# help (string) Element help text (translation key). +# To include HTML formatting, use a translation key ending +# in '_html' here, and define markup in the language files. # -# And for select elements one of: +# And for select and radio elements: # # options (array) List of select values (translation keys) # -# or +# or for select elements alternatively: # # optionGroups (array) List of option groups with keys: # label (string) Group label (translation key) # options (array) List of select values (translation keys) # +# placeholder (string) Placeholder label (translation key). Used to instruct or force +# (when combined with 'required' attribute) the user to make a selection from the +# options-list. Only select elements with 'options' are supported. +# For text, textarea, email and url elements, a placeholder text (translation key) +# can be configured by adding a HTML-attribute via 'settings', for example: +# settings: +# - [placeholder, Please select...] +# #----------------------------------------------------------------------------------- forms: @@ -141,3 +160,11 @@ forms: type: text label: Format required: true + + - name: boolean + type: radio + label: Need Help? + required: true + options: + - Yes + - No diff --git a/config/vufind/HierarchySearch2.ini b/config/vufind/HierarchySearch2.ini new file mode 100644 index 0000000000000000000000000000000000000000..a43ea1b2404f8619c3abadd043be83ad4904080e --- /dev/null +++ b/config/vufind/HierarchySearch2.ini @@ -0,0 +1,6 @@ +[Parent_Config] +relative_path = HierarchyDefault.ini + +[HierarchyTree] +; The source of the hierarchy data -- may be Search2 or XMLFile +treeSource = Search2 diff --git a/config/vufind/Koha.ini b/config/vufind/Koha.ini index aff0187e84e4e529a8250a32af0a36d0b563833d..024c6ad95e0ff945e5e48748074cc7d9dcf0b1e4 100644 --- a/config/vufind/Koha.ini +++ b/config/vufind/Koha.ini @@ -1,4 +1,10 @@ [Catalog] +; These are the settings and credentials used to connect to Koha's MySQL database. +; When using Koha versions earlier than 18.05, the username and password can be found +; in /etc/koha/sites/library/koha-conf.xml. When using Koha 18.05 or newer, it may be +; necessary to create new credentials specifically for access by VuFind, by logging +; in to the MySQL console and running a command like: +; CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password'; host = localhost port = 3306 username = mysqlusername diff --git a/config/vufind/LibGuides.ini b/config/vufind/LibGuides.ini index 9e91e418b0e02314acac90cdbca59e5939d49575..68a8f2bd4e819b63e51ac7a82538742b4ab80d8d 100644 --- a/config/vufind/LibGuides.ini +++ b/config/vufind/LibGuides.ini @@ -16,8 +16,10 @@ baseUrl = https://lgapi.libapps.com/widgets.php ; sets the default number of results per page. limit_options is a comma-separated ; list of numbers to be presented to the end-user. If only one limit is required, ; set default_limit and leave limit_options commented out. -; WARNING: using large limits may require you to raise your PHP memory limits to -; avoid errors. +; WARNING: using large limits may cause problems due to a variety of limitations, +; especially if you support bulk operations (which can cause large URLs/requests). +; If you must support large page sizes, you may need to raise the PHP memory_limit +; and max_input_vars settings and/or adjust the Apache LimitRequestLine setting. default_limit = 20 ;limit_options = 10,20,40,60,80,100 diff --git a/config/vufind/Overdrive.ini b/config/vufind/Overdrive.ini new file mode 100644 index 0000000000000000000000000000000000000000..ac34d13f1ca3ca95626a46825fbdf5ed6a42cd71 --- /dev/null +++ b/config/vufind/Overdrive.ini @@ -0,0 +1,73 @@ +[Overdrive] +; are your records in Marc format? If true, then the driver will mostly default +; to Marc templates. If false, then the driver will assume your records come from +; Overdrive. +isMarc = false; + +; (if isMarc is true) The marc field/subfield that has the overdriveID +overdriveIdMarcField = "037" +overdriveIdMarcSubfield = "a" + +; When to show the Overdrive Content link in the MyResearch Menu. (see noAccessString +; below). +; Note: if you choose accessOnly then the menu item will be hidden when the +; noAccessString is returned from Overdrive's authentication API. In this case "no +; access" means the patron type should not have access to Overdrive, not that there +; is a temporary problem like an expired card. Options: +; - always: show the menu item. +; - accessOnly: only show if the patron has access to overdrive +; - never: never show the item(default) +showMyContent = "never" + +; Whether to show the Overdrive API Troubleshooting menu item in the Admin Menu +showOverdriveAdminMenu = false + +; A substring to check for in the message that Overdrive returns when a patron +; account does not have access to Overdrive. This string will probably be coming +; from your ILS or your own authentication API. You may want to change the message +; that gets displayed in the language translation files when this happens. Other +; messages that come from the API will result in an account problem type message. +noAccessString = "" + +; Enable Consortium Support (true/false). Enable this if this VuFind instance is +; set up for a consortium AND you have some user member libraries that have advantage +; accounts. This will allow the individual user to have availability based on her +; individual library holdings. Leave this off otherwise because there is a little +; more overhead with it on. Default is false. +consortiumSupport = false + +[API] +; You should only have to change these top 5 in most cases. These +; values should be supplied to you by the Overdrive API support team. +clientKey = "YOURLIBKEY" +clientSecret = "yourclientsecret" +productionLibraryID = "" +productionWebsiteID = "" +ILSname = "" + +; set production mode to false to use the integration API during testing; +; set production mode to true to begin using prod URLs and IDs +productionMode = true + +; how long to keep the collection token in the object cache (in seconds); +; the collection token will be refreshed after this amount of time +; default: 60*60*24*7 = 604800 (one week) +tokenCacheLifetime = 604800 + +; Use these in integration mode +integrationLibraryID = "" +integrationWebsiteID = "" + +; you shouldn't have to change anything below unless the API changes +tokenURL = "https://oauth.overdrive.com/token" +patronTokenURL = "https://oauth-patron.overdrive.com/patrontoken" + +; Base Discovery API integration URL +integrationDiscoveryURL = "http://integration.api.overdrive.com" +; Base Circulation API integration URL +integrationCircURL = "http://integration-patron.api.overdrive.com" + +; Base Discovery API Production URL +productionDiscoveryURL = "https://api.overdrive.com" +; Base Circulation API Production URL +productionCircURL = "https://patron.api.overdrive.com" diff --git a/config/vufind/PAIA.ini b/config/vufind/PAIA.ini index 38f6e661c4da13f6f16f45ad0a3338f0ea0a3184..bb45d469ca980f28eec7119a3928940b345e7330 100644 --- a/config/vufind/PAIA.ini +++ b/config/vufind/PAIA.ini @@ -31,6 +31,13 @@ baseUrl = "" ; during form processing. Most users should not need to change this setting. HMACKeys = "id:item_id:doc_id" +; PAIA status which should be mapped to Holds +; status are: 1 = reserved (not available yet but later) +; 2 = ordered (and not yet ready to be picked up) +; 3 = held by patron +; 4 = provided (and ready to be picked up) +status = "1:4" + ; Without customization the PAIA driver will offer to place a storageretrievalrequest ; for items with available service loan and set href for loan. The ; storageretrievalrequest will be performed via PAIA request (technically the same as @@ -43,6 +50,24 @@ HMACKeys = "id:item_id:doc_id" ; during form processing. Most users should not need to change this setting. HMACKeys = id:item_id:doc_id +; PAIA status which should be mapped to StorageRetrievalRequests +; status are: 1 = reserved (not available yet but later) +; 2 = ordered (and not yet ready to be picked up) +; 3 = held by patron +; 4 = provided (and ready to be picked up) +status = "2" + +; All kinds of transactions by a specific patron. Transactions are eg checked out +; items +[Transactions] +; PAIA status which should be mapped to Transactions +; status are: 1 = reserved (not available yet but later) +; 2 = ordered (and not yet ready to be picked up) +; 3 = held by patron +; 4 = provided (and ready to be picked up) +status = "3" + + ; The PAIA driver supports renewals in MyResearch views. The renewal will be ; performed via PAIA renew. ; The pre-defined HMACKeys (id:item_id:doc_id) should suffice to renew an item. No diff --git a/config/vufind/Primo.ini b/config/vufind/Primo.ini index fc39d3362d9e22e9f147f6ea9f903b6bc5ad2ffd..9b0752bb744d3a9bf544f679704d94ea1b030a14 100644 --- a/config/vufind/Primo.ini +++ b/config/vufind/Primo.ini @@ -26,8 +26,10 @@ url = "http://XYZ.hosted.exlibrisgroup.com:1701" ; sets the default number of results per page. limit_options is a comma-separated ; list of numbers to be presented to the end-user. If only one limit is required, ; set default_limit and leave limit_options commented out. -; WARNING: using large limits may require you to raise your PHP memory limits to -; avoid errors. +; WARNING: using large limits may cause problems due to a variety of limitations, +; especially if you support bulk operations (which can cause large URLs/requests). +; If you must support large page sizes, you may need to raise the PHP memory_limit +; and max_input_vars settings and/or adjust the Apache LimitRequestLine setting. default_limit = 20 ;limit_options = 10,20,40,60,80,100 @@ -45,9 +47,9 @@ default_noresults_recommend[] = RemoveFilters ; the Primo API bulkSize = 20 -; When you filter a search using facets, VuFind will present a checkbox that can -; be used to apply those filters to the next search you perform. This setting -; controls its default state: on (true) or off (false). +; When you filter a search using facets, should VuFind retain your current filters +; on the next search and provide a reset button to clear them (true), or should it +; always perform new searches unfiltered (false)? retain_filters_by_default = true ; The filters listed below will be applied to all new searches by default. Omit @@ -92,6 +94,25 @@ domain = Source ; Top facets (not used by default) [FacetsTop] +; This section controls where facet labels are retrieved from when facets are not +; explicitly configured. +[FacetLabels] +; This setting lists configuration sections containing facet field => label +; mappings. Later values will override earlier values. These mappings will be used +; only when a label is not explicitly configured (i.e. through SideFacets settings). +; If you customize your facet display, be sure to add any new facet configuration +; sections to this list to ensure proper display in search history, etc. +labelSections[] = Advanced_Facets +labelSections[] = FacetsTop +labelSections[] = Facets + +; This setting lists configuration settings defining checkbox facets. If you use +; a custom section to configure additional facets, be sure to add it to this list +; so labels display correctly in history, the advanced search editor, etc. If you +; are using the reverse label => filter format rather than filter => label, you +; should prefix the section name with a ~ character to ensure proper loading. +checkboxSections[] = CheckboxFacets + ; Checkbox facets are facets, which are getting displayed as checkboxes ; pcAvailability is a special facet. It's used to show all records found in ; PrimoCentral without checking the local availability against a holdingsfile. @@ -111,6 +132,10 @@ top_rows = 2 top_cols = 3 ; Do we want any facets to be collapsed by default? ;collapsedFacets = * +; Should we show "exclude" links for some or all of the facets? Set to * for +; all facets, use a comma-separated list to show for some of the facets, set +; to false or omit to disable "exclude" links +;exclude = * ; These settings affect the way the facets are displayed [Facet_Settings] diff --git a/config/vufind/RecordTabs.ini b/config/vufind/RecordTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..b2d7b8f3be9af273a16b6428886af2eaea9549b9 --- /dev/null +++ b/config/vufind/RecordTabs.ini @@ -0,0 +1,141 @@ +; This file controls the display of tabs on the Record page for various types of +; records. Each section name matches a record driver class name, and those settings +; will be used when displaying that type of record. If no settings are found for a +; particular class, its parent classes will be checked in turn; thus, you could set +; up global defaults using a [VuFind\RecordDriver\AbstractBase] section. +; +; Within each section, the following settings are supported: +; +; tabs[X] = Y -- This activates a tab, using "X" to identify that tab in the URL, +; and using a service named "Y" loaded from the RecordTab plugin +; manager. The order of tabs entries controls display order. +; defaultTab -- This matches an "X" value from a tabs setting, and controls which +; tab is active by default. If empty, the global default tab setting +; (defaultRecordTab) from config.ini will be used. +; backgroundLoadedTabs[] -- This repeatable setting can be used to identify tabs +; that should be asynchronously loaded in the background to improve +; performance. Use the "X" value from the tabs setting as the id. +[VuFind\RecordDriver\EDS] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\Pazpar2] +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\Primo] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\SolrAuthDefault] +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\SolrAuthMarc] +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\DefaultRecord] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +; Commented out by default because only useful when simpleContainerLinks = true +; in config.ini: +;tabs[ComponentParts] = ComponentParts +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +;tabs[CollectionList] = CollectionList +;tabs[CollectionHierarchyTree] = CollectionHierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewArray +defaultTab = null +;backgroundLoadedTabs[] = UserComments +;backgroundLoadedTabs[] = Details + +[VuFind\RecordDriver\Search2Default] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +; Commented out by default because only useful when simpleContainerLinks = true +; in config.ini: +;tabs[ComponentParts] = ComponentParts +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +;tabs[Search2CollectionList] = Search2CollectionList +;tabs[CollectionHierarchyTree] = CollectionHierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewArray +defaultTab = null +;backgroundLoadedTabs[] = UserComments +;backgroundLoadedTabs[] = Details + +[VuFind\RecordDriver\SolrMarc] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +; Commented out by default because only useful when simpleContainerLinks = true +; in config.ini: +;tabs[ComponentParts] = ComponentParts +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\SolrOverdrive] +tabs[Description] = Description +tabs[Formats] = Formats +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewOverdrive +defaultTab = null + +[VuFind\RecordDriver\Summon] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\WorldCat] +tabs[Holdings] = HoldingsWorldCat +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Details] = StaffViewMARC +defaultTab = null diff --git a/config/vufind/Search2.ini b/config/vufind/Search2.ini index e1ef3905d43bdd06d2fd6ffc57873f71c0f9da1e..a1cb2eea47ce36e738445830d7a5227a548c90a8 100644 --- a/config/vufind/Search2.ini +++ b/config/vufind/Search2.ini @@ -203,6 +203,14 @@ publishDate = "adv_search_year" [ResultsTop] topic_facet = "Suggested Topics" +[FacetLabels] +labelSections[] = Advanced_Facets +labelSections[] = HomePage_Facets +labelSections[] = ResultsTop +labelSections[] = Results +labelSections[] = ExtraFacetLabels +checkboxSections[] = CheckboxFacets + [ExtraFacetLabels] long_lat = "Geographic Search" diff --git a/config/vufind/SierraRest.ini b/config/vufind/SierraRest.ini index 4f0074fddbe7a3f3c537f9a5a2c65051be66a6c0..53ecb7b1278b0aaf3923a4ad3fc8bf22c2baf00e 100644 --- a/config/vufind/SierraRest.ini +++ b/config/vufind/SierraRest.ini @@ -12,15 +12,31 @@ host = "https://sandbox.iii.com/iii/sierra-api" client_key = "something" ; Sierra API client secret client_secret = "very_secret" -; Sierra API version available (defaults to highest one required for full -; functionality in the driver) -;api_version = 5 +; Sierra API version available. Functionality requiring a specific minimum version: +; 5 (default): +; - last pickup date for holds +; 5.1 (technically still v5 but added in a later revision): +; - summary holdings information (especially for serials) +;api_version = 5.1 ; Timeout for HTTP requests http_timeout = 30 ; Redirect URL entered in Sierra for the patron-specific authentication (does not ; need to be a properly working url since the login process is handled completely in ; the driver without user interaction) redirect_uri = "http://localhost/vufind/MyResearch/SierraAuth" +; Set this to true if you are indexing your records with the full .bXXXXXXY bib ID; +; set it to false (the default value) if you are only indexing the numeric ID without +; the .b prefix or checksum suffix. +use_prefixed_ids = false + +; Depending on how Sierra is configured, different values may be used as the username +; and password when the user logs in via CAS. This section defines which POST fields +; are populated with the cat_username and cat_password values stored in VuFind. Legal +; values are "code" (user code -- possibly barcode or unique ID), "name" (user's full +; name) and "pin" (user-defined PIN number). +[Authentication] +username_field = "code" +password_field = "pin" ; This section is used to define library codes and named values which are used by the ; system to indicate the location at which a hold is to be collected. Sierra REST API @@ -77,7 +93,21 @@ title_hold_bib_levels = a:b:m:d ; This section allows modification of the default mapping from item status codes to ; VuFind item statuses [ItemStatusMappings] -;d = "In Processing" +;! = "On Holdshelf" +;t = "In Transit" +;o = "On Reference Desk" +;k = "In Repair" +;m = "Missing" +;n = "Long Overdue" +;$ = "Lost--Library Applied" +;p = "" +;z = "Claims Returned" +;s = "On Search" +;d = "In Process" +;- = "On Shelf" +; This is a special case -- not an official Sierra code, but a VuFind-specific way +; of defining the "checked out" message text: +;Charged = "Charged" ; Uncomment the following lines to enable password (PIN) change ;[changePassword] @@ -93,3 +123,26 @@ title_hold_bib_levels = a:b:m:d [TransactionHistory] ; By default the loan history is disabled. Uncomment the following line to enable it. ;enabled = true + +[CallNumber] +; By default, the driver will only look in the item record for a call number; if your +; institution relies on bib-level call numbers, uncomment this setting and indicate +; which bib fields to use when the item-level call number is not populated. +;bib_fields = 099ab:090ab:050ab + +; Both MARC field+subfields and Sierra field tags may be used to specify which fields +; are included as textual information from holdings records. Note that Sierra does +; not use MARC tags for all holdings information, and e.g. 'h' may cover several +; fields of which only some are mapped to MARC fields. +[Holdings] +; Holdings fields to include in notes. Default is none. +;notes = "n" +; Holdings fields to include in summary. Default is "h". +;summary = "863abiz:866az:h" +; Holdings fields to include in supplements. Default is none. +;supplements = "867az" +; Holdings fields to include in indexes. Default is none. +;indexes = "868az" +; Whether to sort items by enum/chron (v field) instead of order they are returned +; from Sierra. Default is true. +;sort_by_enum_chron = false diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini index e7126909d1c93388ad029c3e1fb973cbf76124b6..ed878a19d13372bdb19cc8c2311c6dd0d86b4ebb 100644 --- a/config/vufind/Summon.ini +++ b/config/vufind/Summon.ini @@ -22,8 +22,10 @@ default_view = list ; sets the default number of results per page. limit_options is a comma-separated ; list of numbers to be presented to the end-user. If only one limit is required, ; set default_limit and leave limit_options commented out. -; WARNING: using large limits may require you to raise your PHP memory limits to -; avoid errors. +; WARNING: using large limits may cause problems due to a variety of limitations, +; especially if you support bulk operations (which can cause large URLs/requests). +; If you must support large page sizes, you may need to raise the PHP memory_limit +; and max_input_vars settings and/or adjust the Apache LimitRequestLine setting. default_limit = 20 ;limit_options = 10,20,40,60,80,100 @@ -63,9 +65,9 @@ highlighting = true ; available. snippets = true -; When you filter a search using facets, VuFind will present a checkbox that can -; be used to apply those filters to the next search you perform. This setting -; controls its default state: on (true) or off (false). +; When you filter a search using facets, should VuFind retain your current filters +; on the next search and provide a reset button to clear them (true), or should it +; always perform new searches unfiltered (false)? retain_filters_by_default = true ; The filters listed below will be applied to all new searches by default. Omit @@ -147,6 +149,26 @@ PublicationDate = "adv_search_year" ; share year string w/advanced search page [FacetsTop] SubjectTerms = "Suggested Topics" +; This section controls where facet labels are retrieved from when facets are not +; explicitly configured. +[FacetLabels] +; This setting lists configuration sections containing facet field => label +; mappings. Later values will override earlier values. These mappings will be used +; only when a label is not explicitly configured (i.e. through SideFacets settings). +; If you customize your facet display, be sure to add any new facet configuration +; sections to this list to ensure proper display in search history, etc. +labelSections[] = Advanced_Facets +labelSections[] = HomePage_Facets +labelSections[] = FacetsTop +labelSections[] = Facets + +; This setting lists configuration settings defining checkbox facets. If you use +; a custom section to configure additional facets, be sure to add it to this list +; so labels display correctly in history, the advanced search editor, etc. If you +; are using the reverse label => filter format rather than filter => label, you +; should prefix the section name with a ~ character to ensure proper loading. +checkboxSections[] = CheckboxFacets + ; Facet display settings [Results_Settings] ; By default, the side facets will only show 6 facets and then the "show more" @@ -318,3 +340,20 @@ next_prev_navigation = false ; regular Syndetics if necessary. [List] view=full + +; This section controls the behavior of the Autocomplete within Summon. +[Autocomplete] +; Disabled by default. +enabled = false + +; Define a default_handler +default_handler = None + +; Auto-submit autocomplete on click or enter +auto_submit = true + +; In this section, set the key equal to a search type from [Basic_Searches] and +; the value equal to an autocomplete handler in order to customize autocompletion +; behavior when that search type is selected. See searches.ini for a list of +; available handlers. +[Autocomplete_Types] diff --git a/config/vufind/author-classification.ini b/config/vufind/author-classification.ini index 3708e210bcf8850b1b44b176e8cd49d2d8ab9b66..13a6e99f62fccf2afee35a828bef5d79b562dd1c 100644 --- a/config/vufind/author-classification.ini +++ b/config/vufind/author-classification.ini @@ -4,7 +4,7 @@ ; Default relator terms taken from https://www.loc.gov/marc/relators/relaterm.html firstAuthorRoles = adp,aut,cmp,cre,dub,inv secondAuthorRoles = act,anm,ann,arr,acp,ar,ard,aft,aud,aui,aus,bjd,bpd,cll,ctg,chr,cng,clb,clr,cmm,cwt,com,cpl,cpt,cpe,ccp,cnd,cos,cot,coe,cts,ctt,cte,ctb,crp,cst,cov,cur,dnc,dtc,dto,dfd,dft,dfe,dln,dpc,dsr,drt,dis,drm,edt,elt,egr,etr,fac,fld,flm,frg,ilu,ill,ins,itr,ivr,ldr,lsa,led,lil,lit,lie,lel,let,lee,lbt,lgd,ltg,lyr,mrb,mte,msd,mus,nrt,opn,org,pta,pth,prf,pht,ptf,ptt,pte,prt,pop,prm,pro,pmn,prd,prg,pdr,pbd,ppt,ren,rpt,rth,rtm,res,rsp,rst,rse,rpy,rsg,rev,rbr,sce,sad,scr,scl,spy,std,sng,sds,spk,stm,str,stl,sht,ths,trl,tyd,tyg,vdg,voc,wde,wdc,wam -nonCreativeRoles = anl,app,arc,asg,asn,att,auc,aqt,ant,bnd,bdd,blw,bkd,bkp,bsl,cns,cli,col,clt,cmt,con,csl,csp,ctr,cpc,cph,crr,dtm,dte,dgg,dpt,dbp,dst,dnr,elg,eng,evp,exp,fpy,fmo,fnd,gis,hnr,hst,ive,lbr,len,lse,lso,mfp,mfr,mrk,mdc,mod,mon,mcp,orm,oth,own,ppm,pat,pma,plt,prc,prp,pfr,pup,pbl,rcp,rce,red,rps,sec,sgn,spn,stn,srv,tch,tcd,trc,uvp,wit +nonCreativeRoles = anl,app,arc,asg,asn,att,auc,aqt,ant,bnd,bdd,blw,bkd,bkp,bsl,cns,cli,col,clt,cmt,con,csl,csp,ctr,cpc,cph,crr,dtm,dte,dgg,dpt,dbp,dst,dnr,elg,eng,evp,exp,fpy,fmo,fnd,gis,hnr,hst,ive,lbr,len,lse,lso,mfp,mfr,mrk,mdc,mod,mon,mcp,orm,oth,own,ppm,pat,pma,plt,prc,prp,pfr,pup,pbl,rcp,rce,red,rps,sec,sgn,spn,stn,srv,tch,tcd,trc,uvp,wit ; Relator terms from German cataloging guideline RAK - add these to the comma separated ; lists above, if needed. diff --git a/config/vufind/authority.ini b/config/vufind/authority.ini index 39416f005519bbce38f8cde59e3bbd114cd7605c..71290d9f35cd4a593b268f772bd3f464f7387b50 100644 --- a/config/vufind/authority.ini +++ b/config/vufind/authority.ini @@ -38,6 +38,10 @@ general_facet_str_mv = "General" chronological_facet_str_mv = "Chronological" geographic_facet_str_mv = "Geographic" +[FacetLabels] +labelSections[] = Facets +checkboxSections[] = CheckboxFacets + [Autocomplete] enabled = true @@ -46,3 +50,4 @@ AllFields = SolrAuth MainHeading = "SolrAuth:MainHeading" Heading = "SolrAuth:Heading" +[CheckboxFacets] diff --git a/config/vufind/combined.ini b/config/vufind/combined.ini index ff466b4b69575ed66b2a15826f3c9a7ca77926e6..b255c6242d42ba6ac1d5e77c7565e470019d6172 100644 --- a/config/vufind/combined.ini +++ b/config/vufind/combined.ini @@ -42,6 +42,18 @@ ; The order of sections in this file will control the display order of search ; results on screen. +; This section controls the search handler options displayed in combined mode. +; The keys and values should be the same. When performing searches, the code will +; attempt to find a search option in each target backend whose description matches +; the key/value pair here. If no match is found, the default handler will be used. +; Combined search handlers are disabled by default, since this matching may not be +; applicable to all backends and may create a confusing user experience. +[Basic_Searches] +;All Fields = "All Fields" +;Title = Title +;Author = Author +;Subject = Subject + ; This section controls the behavior of the Combined/Home screen. [HomePage] ; Content blocks can be selected from the list in searches.ini. diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 08faaf8aec16bbddf169202824d44f44aff31ca9..b804c92fb9b49a4703e8b82bc15d5a48d67ff053 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -24,6 +24,9 @@ autoConfigure = true ; Base URL is normally auto-detected, but this setting is used when autodetection is ; not possible (i.e. during sitemap generation at the command line). url = http://library.myuniversity.edu/vufind +; Set to true if VuFind is behind a reverse proxy (typically Apache with mod_proxy), +; make sure your reverse proxy sets the necessary headers. +;reverse_proxy = true email = support@myuniversity.edu title = "Library Catalog" ; This is the default theme for non-mobile devices (or all devices if mobile_theme @@ -94,10 +97,13 @@ defaultModule = Search defaultLoggedInModule = MyResearch ; When defaultLoggedInModule is used, this action will be triggered (default = Home) ;defaultLoggedInAction = Home +; The search backend that VuFind will use in search boxes when nothing else is +; specified (e.g. on user account pages, search history, etc.). Default = Solr +;defaultSearchBackend = Solr ; The route VuFind will send users to following a log out operation. Set to false ; or omit to attempt to retain the user's current context after log out. ;logOutRoute = home -; This tab will show by default when a record is viewed: +; Default tab to display when a record is viewed (see also RecordTabs.ini): defaultRecordTab = Holdings ; Hide the holdings tab if no holdings are available from the ILS; note that this ; feature requires your ILS driver to support the hasHoldings() method. @@ -145,10 +151,10 @@ allowSavedSearches = true ; features (such as display of hierarchies). nonJavascriptSupportEnabled = false ; Generator value to display in an HTML header <meta> tag: -generator = "VuFind 5.1.1" +generator = "VuFind 6.1.2" ; This section allows you to configure the mechanism used for storing user -; sessions. Available types: File, Memcache, Database. +; sessions. Available types: File, Memcache, Database, Redis. ; Some of the settings below only apply to specific session handlers; ; such settings are named with an obvious prefix. Non-prefixed settings ; are global to all handlers. @@ -166,6 +172,15 @@ secure = false ;memcache_host = localhost ;memcache_port = 11211 ;memcache_connection_timeout = 1 +; +; Settings related to Redis-based sessions; default values are listed below +;redis_host = localhost +;redis_port = 6379 +;redis_connection_timeout = 0.5 +;redis_db = 0 +;redis_auth = some_secret_password +;redis_version = 3 +;redis_standalone = true ; This section controls how VuFind creates cookies (to store session IDs, bookbag ; contents, theme/language settings, etc.) @@ -199,6 +214,7 @@ session_name = VUFIND_SESSION ; - DAIA (using either XML or JSON API) ; - Demo (fake ILS driver returning complex responses) ; - Evergreen +; - Folio ; - Horizon (basic database access only) ; - HorizonXMLAPI (more features via API) ; - Innovative (for INNOPAC; see also Sierra/SierraRest) @@ -329,11 +345,46 @@ title_level_holds_mode = "disabled" ; Whether to display the item barcode for each loan. Default is false. ;display_checked_out_item_barcode = true +; This section controls features related to user accounts +[Account] +; Allow the user to set a home library through the Profile screen, which will +; override ILS-provided default pickup locations throughout the system. +set_home_library = true + +; Allow the user to "subscribe" to search history entries in order to receive +; email notifications of new search results. +schedule_searches = false + +; Should we always send a scheduled search email the first time we run notices +; after a user has subscribed (true), or should we only send an email when there +; is actually something new (false, default) +force_first_scheduled_email = false + +; When schedule_searches is set to true, you can customize the schedule frequencies +; here -- just use the number of days between notifications in the brackets. Labels +; will be run through the translator. +;scheduled_search_frequencies[0] = schedule_none +;scheduled_search_frequencies[1] = schedule_daily +;scheduled_search_frequencies[7] = schedule_weekly + ; This section allows you to determine how the users will authenticate. ; You can use an LDAP directory, the local ILS (or multiple ILSes through -; the MultiILS option), the VuFind database (Database), AlmaDatabase (combination -; of VuFind database and Alma account), Shibboleth, SIP2, CAS, Facebook or some -; combination of these (via the MultiAuth or ChoiceAuth options). +; the MultiILS option), the VuFind database (Database), a hard-coded list of +; access passwords (PasswordAccess), AlmaDatabase (combination +; of VuFind database and Alma account), Shibboleth, SIP2, CAS, Facebook, Email or +; some combination of these (via the MultiAuth or ChoiceAuth options). +; +; The Email method is special; it is intended to be used through ChoiceAuth in +; combination with Database authentication (or any other method that reliably stores +; the user's email address) to make it possible to log in by receiving an +; authentication link at the email address stored in VuFind's database. Email is +; also supported as the primary authentication mechanism for some ILS drivers (e.g. +; Alma). In these cases, ChoiceAuth is not needed, and ILS should be configured as +; the Authentication method; see the ILS driver's configuration for possible options. +; +; Also note that the Email method stores hashes in your database's auth_hash table. +; You should run the "php $VUFIND_HOME/public/index.php util expire_auth_hashes" +; utility periodically to clean out old data in this table. [Authentication] ;method = LDAP ;method = ILS @@ -346,6 +397,8 @@ method = Database ;method = ChoiceAuth ;method = MultiILS ;method = Facebook +;method = PasswordAccess +;method = Email ; This setting only applies when method is set to ILS. It determines which ; field of the ILS driver's patronLogin() return array is used as the username @@ -380,9 +433,21 @@ recover_interval = 60 ; Default: Two weeks recover_hash_lifetime = 1209600 +; Allow users to set change their email address (if supported by Auth method). +; When turning this on, it is also strongly recommended to turn on verify_email +; below. +change_email = false + ; Allow users to set change their passwords (if supported by Auth method) change_password = true +; Force users to verify their email address before being able to log in +; (only if method=Database) or make changes to it (if change_email=true). +; If you wish to customize the email messages used by the system, see the +; translation strings starting with verify and change_notification, as well as +; the notify-email-change.phtml and verify-email.phtml Email templates. +verify_email = false + ; Set this to false if you would like to store catalog passwords in plain text encrypt_ils_password = false @@ -392,7 +457,7 @@ ils_encryption_key = false ; This is the algorithm used to encrypt and decrypt catalog passwords. ; A symmetrical encryption algorithm must be used. -; You can use mcrypt_list_algorithms() to see available options on your system. +; You can use openssl_get_cipher_methods() to see available options on your system. ; Common choices: blowfish (default), aes ; If you want to convert from one algorithm to another, run this from $VUFIND_HOME: ; php public/index.php util switch_db_hash oldhash:oldkey (or none) newhash:newkey @@ -488,10 +553,17 @@ host = localhost port = 25 ;username = user ;password = pass +; The server name to report to the upstream mail server when sending mail. +;name = vufind.myuniversity.edu ; If a login is required you can define which protocol to use for securing the ; connection. If no explicit protocol ('tls' or 'ssl') is configured, a protocol ; based on the configured port is chosen (587 -> tls, 487 -> ssl). ;secure = tls +; This setting enforces a limit (in seconds) on the lifetime of an SMTP +; connection, which can be useful when sending batches of emails, since it can +; help avoid errors caused by server timeouts. Comment out the setting to disable +; the limit. +connection_time_limit = 60 ; Uncomment this setting to disable outbound mail but simulate success; this ; is useful for interface testing but should never be used in production! ;testOnly = true @@ -515,6 +587,8 @@ disable_from = false ; From field override. Setting this allows keeping the "from" field in email forms ; but will only use it as a reply-to address. The address defined here is used as the ; actual "from" address. +; Note: If a feature explicitly sets a different reply-to address (for example, +; Feedback forms), the original from address will NOT override that reply-to value. ;override_from = "no-reply@myuniversity.edu" ; Being a special case of mail message, sending record results via SMS ("Text this") @@ -523,6 +597,10 @@ disable_from = false ; configuration options. sms = enabled +; Set this value to "database" to shorten links sent via email/SMS and +; store its path in the database (default "none"). +url_shortener = none + ; This section needs to be changed to match your database connection information [Database] ; Connection string format is [platform]://[username]:[password]@[host]:[port]/[db] @@ -543,6 +621,16 @@ database = mysql://root@localhost/vufind ; for compatibility with existing VuFind 1.x installations. ;charset = utf8 +; Reduce access to a set of single passwords +; This is only used when Authentication method is PasswordAccess. See above. +; Recommended to be used in conjunction with very restricted permissions.ini settings +; and with most social settings disabled +;[PasswordAccess] +; access_user is a map of users to passwords +; entering a correct password will login as that user +;access_user[user] = password +;access_user[admin] = superpassword + ; LDAP is optional. This section only needs to exist if the ; Authentication Method is set to LDAP. When LDAP is active, ; host, port, basedn and username are required. @@ -649,7 +737,6 @@ database = mysql://root@localhost/vufind ; Required: CAS login URL. ;login = https://cas.myuniversity.edu/cas/login - ; Required: CAS logout URL. ;logout = https://cas.myuniversity.edu/cas/logout @@ -661,6 +748,11 @@ database = mysql://root@localhost/vufind ; defaultLoggedInModule from [Site] section will be used). ;target = http://lib.myuniversity.edu/vufind/MyResearch/Home +; Optional: protocol to follow (legal values include CAS_VERSION_1_0, +; CAS_VERSION_2_0, CAS_VERSION_3_0 and SAML_VERSION_1_1; default is +; SAML_VERSION_1_1) +;protocol = SAML_VERSION_1_1 + ; Some or all of the following entries may be uncommented to map CAS ; attributes to user database columns: ;cat_username = acctSyncUserID @@ -960,12 +1052,12 @@ timeout = 10 url = "https://api.booksite.com" ;key = "Key" -; You can change the base Content Cafe URL used by the content services here. Most -; users will not need to change this setting. You also need to set your password, -; "pw". Note that Content Cafe is a subscription service from Baker & Taylor. +; Content Cafe is a subscription service from Baker & Taylor. If you are using this +; service (see the [Content] section above for details), you MUST uncomment and set +; the password (pw) setting. You may also change the API base URL (url) if needed. [Contentcafe] -url = "http://contentcafe2.btol.com" -pw = "Password" +;url = "http://contentcafe2.btol.com" +;pw = "Password" ; Summon is optional; this section is used for your API credentials. apiId is the ; short, human-readable identifier for your Summon account; apiKey is the longer, @@ -1029,10 +1121,14 @@ pw = "Password" [DOI] ; This setting controls whether or not DOI-based links are enabled, and which ; API is used to fetch the data. Currently supported options: BrowZine (requires -; credentials to be configured in BrowZine.ini) or false (to disable). Disabled +; credentials to be configured in BrowZine.ini), Unpaywall or false (to disable). Disabled ; by default. ;resolver = BrowZine +;unpaywall_api_url = "https://api.unpaywall.org/v2" +; Unpaywall needs an email adress, see https://unpaywall.org/products/api +;unpaywall_email = "your@email.org" + ; The following settings control where DOI-based links are displayed: show_in_results = true ; include in search results show_in_record = false ; include in core record metadata @@ -1057,8 +1153,8 @@ rfr_id = vufind.svn.sourceforge.net ; By specifying your link resolver type, you can allow VuFind to optimize its ; OpenURLs for a particular platform. Current legal values: "sfx", "360link", -; "EZB", "Redi," "demo" or "generic" (default is "generic" if commented out; "demo" -; generates fake values for use in testing the embed setting below). +; "EZB", "Redi", "Alma", "demo" or "generic" (default is "generic" if commented out; +; "demo" generates fake values for use in testing the embed setting below). ;resolver = sfx ; If you want OpenURL links to open in a new window, set this setting to the @@ -1170,6 +1266,10 @@ url = https://www.myendnoteweb.com/EndNoteWeb.html ; ; If admin_email is not set, the main email under [Site] will be used instead. ; +; page_size may be used to specify the number of records returned per request. +; Default is 100. A higher number may improve overall harvesting performance, but +; will also make a single response page larger and slower to produce. +; ; If set_field is set, the named Solr field will be used to generate sets on ; your OAI-PMH server. If it is not set, sets will not be supported. ; @@ -1179,18 +1279,34 @@ url = https://www.myendnoteweb.com/EndNoteWeb.html ; for your set queries. set_query names will trump set_field values when ; there are collisions. ; +; default_query may be used to specify a filter for the default set, i.e. records +; returned when a set is not specified. +; ; If vufind_api_format_fields is set, the listed fields (as defined in ; SearchApiRecordFields.yaml) are returned when metadata prefix ; "oai_vufind_json" is used. ; +; record_format_filters allows mapping from requested OAI metadataPrefix to query +; filters. They can be used e.g. to limit results to records that can be returned in +; the requested format. +; +; delete_lifetime controls how many days' worth of deleted records to include in +; responses. Records deleted before the cut-off will not be included in responses. +; Omit this setting to return all deleted records. This can be useful for long-lived +; systems with many deleted records, to prevent full harvests from becoming unwieldy. +; ;[OAI] ;identifier = myuniversity.edu ;repository_name = "MyUniversity Catalog" ;admin_email = oai@myuniversity.edu +;page_size = 1000 ;set_field = "format" ;set_query['eod_books'] = "institution:kfu AND publishDate:[1911 TO 1911]" ;set_query['eod_ebooks'] = "format:eBook" +;default_query = "institution:kfu" ;vufind_api_format_fields = "id,authors,cleanIsbn,cleanIssn,formats,title" +;record_format_filters[marc21] = "record_format:marc" +;delete_lifetime = 365 ; Proxy Server is Optional. [Proxy] @@ -1351,6 +1467,8 @@ ar = "Arabic" bn = "Bengali" gl = "Galician" vi = "Vietnamese" +hr = "Croatian" +hi = "Hindi" ; This section contains special cases for languages such as right-to-left support [LanguageSettings] @@ -1576,7 +1694,8 @@ HMACkey = mySuperSecretValue ; link to the respective collections page rather than the record page ; (default = false). ;collections = true -; Control default tab of Collection view (default = CollectionList) +; Control default tab of Collection view (default = CollectionList); see also +; CollectionTabs.ini. ;defaultTab = CollectionList ; This controls where data is retrieved from to build the Collections/Home page. ; It can be set to Index (use the Solr index) or Alphabetic (use the AlphaBrowse @@ -1591,6 +1710,11 @@ HMACkey = mySuperSecretValue ;browseDelimiter = "{{{_ID_}}}" ; This controls the page size within the Collections/Home page (default = 20). ;browseLimit = 20 +; List of record routes that are converted to collection routes (used to map +; route names when a record identifies itself as a collection and the collections +; setting above is true). +route[record] = collection +route[search2record] = search2collection ; This section addresses hierarchical records in the Solr index [Hierarchy] @@ -1609,7 +1733,8 @@ treeSearchLimit = 100 ; Whether hierarchy fields are used for linking between container records and their ; children (default = false). This is an alternative to the full collections support ; (see the [Collections] section), so only one of them should be enabled -; at a time e.g. unless custom record drivers are used. +; at a time e.g. unless custom record drivers are used. When using this setting, +; you may also wish to enable the ComponentParts tab in RecordTabs.ini. ;simpleContainerLinks = true ; This section will be used to configure the feedback module. @@ -1657,6 +1782,9 @@ treeSearchLimit = 100 ; the string, for example "SiteA|Backend|Search Terms." Most users will want to ; leave this disabled. ;searchPrefix = "SiteA|" +; Uncomment the following setting to disable cookies for privacy reasons. +; see https://matomo.org/faq/general/faq_157/ for more information. +;disableCookies = true ; Uncomment portions of this section to activate tabs in the search box for switching ; between search modules. Keys are search backend names, values are labels for use in @@ -1714,7 +1842,7 @@ treeSearchLimit = 100 ; Use * for all supported forms ; Note: when "feedback" is active, Captcha can be conditionally disabled on a ; form-by-form basis with the useCaptcha setting in FeedbackForms.yaml. -;forms = changePassword, email, newAccount, passwordRecovery, sms +;forms = changeEmail, changePassword, email, newAccount, passwordRecovery, sms ; This section can be used to display default text inside the search boxes, useful diff --git a/config/vufind/facets.ini b/config/vufind/facets.ini index cfa85dd9bacef3447f945487c3454d45adf31e46..4dae529e5c8cd187dd88f9f090491fd318c80ad6 100644 --- a/config/vufind/facets.ini +++ b/config/vufind/facets.ini @@ -23,6 +23,27 @@ publishDate = "adv_search_year" ; share year string w/advanced search pa [ResultsTop] topic_facet = "Suggested Topics" +; This section controls where facet labels are retrieved from when facets are not +; explicitly configured. +[FacetLabels] +; This setting lists configuration sections containing facet field => label +; mappings. Later values will override earlier values. These mappings will be used +; only when a label is not explicitly configured (i.e. through SideFacets settings). +; If you customize your facet display, be sure to add any new facet configuration +; sections to this list to ensure proper display in search history, etc. +labelSections[] = Advanced +labelSections[] = HomePage +labelSections[] = ResultsTop +labelSections[] = Results +labelSections[] = ExtraFacetLabels + +; This setting lists configuration settings defining checkbox facets. If you use +; a custom section to configure additional facets, be sure to add it to this list +; so labels display correctly in history, the advanced search editor, etc. If you +; are using the reverse label => filter format rather than filter => label, you +; should prefix the section name with a ~ character to ensure proper loading. +checkboxSections[] = CheckboxFacets + ; This section is used to specify labels for facets that may be applied by parts ; of VuFind other than the facet lists defined in this file (for example, the ; hierarchical browse of the BrowseController, or the Geographic Search). @@ -47,14 +68,29 @@ dateRange[] = publishDate ; (see https://wiki.apache.org/solr/HierarchicalFaceting but note that we always ; use a trailing slash to avoid ambiguities) ;hierarchical[] = building -; Sort options for hierarchical facets: -; How hierarchical facets are sorted. Default is result count, but alternative ways -; can be specified: -; top = Sort the top level list alphabetically, others by result count (useful e.g. -; for a large number of building facets where top level is organization and -; second level the library branch) -; all = Sort all levels alphabetically + +; General sort options for hierarchical facets (Home page, Advanced Search and +; SideFacets). +; +; You can set a general default setting with * and set field-specific overrides +; using field names (see example below). +; +; Available options: +; top = Sort the top level list alphabetically, others by result count (useful e.g. +; for a large number of building facets where top level is organization and +; second level the library branch) +; all = Sort all levels alphabetically +; count = Sort all levels by count +; +; Note: this section may be overridden for HomePage and Advanced search facets (see +; hierarchicalFacetSortOptions in HomePage_Settings and Advanced_Settings below). +; +; By default, if no settings are configured in this file, the default sort will be +; 'count' for SideFacets values, 'all' for HomePage values, and 'top' for Advanced +; values. +;hierarchicalFacetSortOptions[*] = all ;hierarchicalFacetSortOptions[building] = top + ; How hierarchical facet values are displayed in the records: ; single = Display only the deepest level (default) ; full = Display full hierarchy for each entry @@ -73,6 +109,22 @@ dateRange[] = publishDate [CheckboxFacets] ;edition:1st* = "First Edition" ; Contrived hypothetical example +; Available sort options when listing all facets from Sidefacets. +; +; Each configuration option targets a search class and a facet field. +; All facet fields for a search class can be targeted using the wildcard '*'. +; Sort options are given as a comma-separated list of "<sort-field>=<label>" entries, +; where <sort-field> is either 'count' or 'index' and <label> the translation +; key for the option. +[AvailableFacetSortOptions] +; By default all Solr facets can be sorted by count and alphabetically. + +; Example: sort Solr author_facet by count only. +; Solr[author_facet] = "count=sort_count" + +; Example: sort Solr author_facet only alphabetically +; Solr[author_facet] = "index=sort_alphabetic" + ; These settings affect the way the [Results] facets are displayed ; If using facets at the top of search results you have more room for text. [Results_Settings] @@ -189,6 +241,11 @@ translated_facets[] = callnumber-first:CallNumberFirst ;delimited_facets[] = author_id_str ;delimited_facets[] = "author_id_str|:::" +; Sort overrides for Advanced search hierarchical facets. See the comments +; above the SpecialFacets > hierarchicalFacetSortOptions setting for details. +;hierarchicalFacetSortOptions[*] = all +;hierarchicalFacetSortOptions[building] = top + ; These facets will be displayed on the Home Page when FacetList is turned on in ; the content setting of the [HomePage] section of searches.ini. If this section ; is omitted, the [Advanced] section will be used instead. @@ -206,6 +263,11 @@ format = Format ; of the homepage facet lists, we may not display all loaded values for every facet facet_limit = 20 +; Sort overrides for HomePage search hierarchical facets. See the comments +; above the SpecialFacets > hierarchicalFacetSortOptions setting for details. +;hierarchicalFacetSortOptions[*] = all +;hierarchicalFacetSortOptions[building] = top + [Visual_Settings] ; Which two facetable fields should be used for creating the visual results? ; See VisualFacets recommendation module in searches.ini for more details. diff --git a/config/vufind/metadata.ini b/config/vufind/metadata.ini new file mode 100644 index 0000000000000000000000000000000000000000..df8d4cec422861dc03f84b5926a2333d8768ae26 --- /dev/null +++ b/config/vufind/metadata.ini @@ -0,0 +1,14 @@ +; This file controls the export of HTML meta headers on the Record page. +; +; The amount of exported metadata depends on field availability +; as well as the implementation of the chosen RecordDriver. +; (e.g. \VuFind\RecordDriver\DefaultRecord implements getContainerTitle(), +; but in case of SolrMarc the referenced "container_title" field +; is not indexed by default). +; +[Vocabularies] +;VuFind\RecordDriver\SolrMarc[] = BEPress +;VuFind\RecordDriver\SolrMarc[] = DublinCore +;VuFind\RecordDriver\SolrMarc[] = Eprints +;VuFind\RecordDriver\SolrMarc[] = HighwirePress ; recommended for Google Scholar +;VuFind\RecordDriver\SolrMarc[] = PRISM diff --git a/config/vufind/permissionBehavior.ini b/config/vufind/permissionBehavior.ini index 550838fb0c9fecda480485c6388b1e286a961c29..3390fd023d82bca3924145c1bd4b97e94b793366 100644 --- a/config/vufind/permissionBehavior.ini +++ b/config/vufind/permissionBehavior.ini @@ -66,6 +66,18 @@ defaultDeniedControllerBehavior = "promptLogin" ; (False means "use the default behavior defined by the template"). defaultDeniedTemplateBehavior = false +; This setting can be used to override access permissions for controllers. You can +; set a controller class name inside the brackets to explicitly override that +; controller's access permission (and that of any of its children, unless more +; specific permissions are set for those subclasses), whether or not it has a +; default defined in the class. You can use * inside the brackets to set a default +; access permission that is applied to all controllers that do not have an +; internally-defined default (the MyResearchController is also excluded from +; this setting to avoid infinite login redirects). This can be useful if you +; wish to password-protect your entire site. +;controllerAccess[*] = access.VuFindInterface +;controllerAccess[VuFind\Controller\SearchController] = access.SearchResults + ; Example configuration for non-standard favorites permission behavior: ;[feature.Favorites] ;deniedTemplateBehavior = "showMessage:Login for Favorites" diff --git a/config/vufind/reserves.ini b/config/vufind/reserves.ini index e8ad3aee30821b71e699fcdd27f53fd98aabaff0..6341d532692c006e5b9860b4e605b62185805ca4 100644 --- a/config/vufind/reserves.ini +++ b/config/vufind/reserves.ini @@ -24,6 +24,10 @@ department_str = "Department" instructor_str = "Instructor" course_str = "Course" +[FacetLabels] +labelSections[] = Facets +checkboxSections[] = CheckboxFacets + [Autocomplete] enabled = true diff --git a/config/vufind/searchbox.ini b/config/vufind/searchbox.ini index 43afb28473b97623595e238db64f702b82d2a7e1..19f8e0a488b51d976efed98a7f086fbea7a8f5fa 100644 --- a/config/vufind/searchbox.ini +++ b/config/vufind/searchbox.ini @@ -10,6 +10,15 @@ combinedHandlers = false ; [AlphaBrowse] section of config.ini. includeAlphaBrowse = false +; If includeAlphaBrowse is true, this label will be used to group the browse values +; together in the drop-down (see group[] setting below). Set to false for no label. +alphaBrowseGroup = false + +; If combinedHandlers is true, and a search option is selected which falls outside +; of the defined options in [CombinedHandlers] below, this group label will be +; applied to the option (see group[] setting below; set to false for no label). +defaultGroupLabel = false + ; This section controls the "combined handlers" drop-down. It must contain groups ; of settings with the following keys: ; @@ -18,16 +27,30 @@ includeAlphaBrowse = false ; the user's search terms will be appended to the end of the URL in ; "External" mode. You may use a %%lookfor%% placeholder string in the ; URL to force the query to be injected at a specific position. -; label[] = Label for this value (subject to translation) +; label[] = Label for this value (subject to translation); when using "VuFind" type +; searches, if this is a non-empty value, it will override the first +; label provided by the search options, and subsequent options will be +; indented beneath it. If you use an empty value, all options will use +; their existing default labels, and no identation will be applied. This +; allows two different approaches to grouping related options, with or +; without option groups (see group[] below). +; group[] = Option group label to wrap around the option(s) represented here; if +; multiple External targets have the same group value, they will be +; clustered together. If you want a single group label for a VuFind +; target, you may consider leaving that target's label[] blank to avoid +; unwanted double indenting. Set to false for no grouping. [CombinedHandlers] type[] = VuFind target[] = Solr label[] = Catalog +group[] = false type[] = VuFind target[] = Summon label[] = Summon +group[] = false type[] = External target[] = "http://www.google.com/search?q=" label[] = Google +group[] = "Other Sites" diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini index ff183ddf4e980e2442a726c393a66b597678c29c..94510ff3d201817b33448575d65e1eafe356024a 100644 --- a/config/vufind/searches.ini +++ b/config/vufind/searches.ini @@ -21,8 +21,10 @@ default_view = list ; sets the default number of results per page. limit_options is a comma-separated ; list of numbers to be presented to the end-user. If only one limit is required, ; set default_limit and leave limit_options commented out. -; WARNING: using large limits may require you to raise your PHP memory limits to -; avoid errors. +; WARNING: using large limits may cause problems due to a variety of limitations, +; especially if you support bulk operations (which can cause large URLs/requests). +; If you must support large page sizes, you may need to raise the PHP memory_limit +; and max_input_vars settings and/or adjust the Apache LimitRequestLine setting. default_limit = 20 ;limit_options = 10,20,40,60,80,100 @@ -61,6 +63,7 @@ default_side_recommend[] = SideFacets:Results:CheckboxFacets ; decrease server load especially with larger indexes. ;default_side_recommend[] = SideFacetsDeferred:Results:CheckboxFacets ;default_noresults_recommend[] = SwitchTab +;default_noresults_recommend[] = RecommendLinks default_noresults_recommend[] = SwitchType default_noresults_recommend[] = SwitchQuery:::fuzzy default_noresults_recommend[] = SpellingSuggestions @@ -82,9 +85,9 @@ highlighting = true ; For control over snippet captions, see the [Snippet_Captions] section below. snippets = true -; When you filter a search using facets, VuFind will present a checkbox that can -; be used to apply those filters to the next search you perform. This setting -; controls its default state: on (true) or off (false). +; When you filter a search using facets, should VuFind retain your current filters +; on the next search and provide a reset button to clear them (true), or should it +; always perform new searches unfiltered (false)? retain_filters_by_default = true ; The filters listed below will be applied to all new searches by default. Omit @@ -336,7 +339,8 @@ CallNumber = callnumber-sort ; terms while "__mode__:usefor" would show only main headings from records ; whose "use for" headings match the current search. You can use "__mode__:*" ; to turn on all options (but this is default behavior if no __mode__ is -; set). Filtering is optional. +; set). Filtering is optional. A special field name of "__header__" may be +; used to customize the header text above the results (default = "See also"). ; Channels ; Display a link to the Channeled Browse functionality leading to channels of ; records related to the current search. @@ -372,10 +376,21 @@ CallNumber = callnumber-sort ; display a link to resolve it. If it makes up the entire query, redirect to ; the resolver automatically. [prefix] is the URL of the resolver to which ; the DOI itself will be appended. +; ExternalSearch:[link label]:[url template] +; Display a link to an external search system. The contents of the <a> tag +; will be [link label] (which will be run through the translator -- use a +; translation string if you need to display a string containing a colon). +; The href will be [url template] with %%lookfor%% replaced with the current +; search's lookfor parameter. If you omit the %%lookfor%% placeholder, the +; search terms will simply be appended to the end of the URL template. ; Libraryh3lp:[type]:[id]:[skin] ; Display a chat box for the Libraryh3lp service. [type] indicats the type ; of chat being used (either "queue" or "user"). [id] is the name of the ; queue or user. [skin] is optional and specifies a skin number to use. +; RecommendLinks:[ini section]:[ini name] +; Display a list of recommended links, taken from [ini section] in +; [ini name], where the section is a mapping of label => URL. [ini name] +; defaults to searches.ini, and [ini section] defaults to RecommendLinks. ; RemoveFilters ; Suggests removing facet filters to retrieve more results. ; SwitchQuery:[backend]:[opt-out checks to skip]:[opt-in checks to add] @@ -427,6 +442,11 @@ CallNumber[] = RemoveFilters top[] = AuthorInfo side[] = "ExpandFacets:Author" +; This is the default section used for setting up links for the RecommendLinks +; recommendation module (see above for details). +;[RecommendLinks] +;Library of Congress Online Catalog = http://catalog.loc.gov/ + ; This section controls the "New Items" search. [NewItem] ; New item information can be retrieved from Solr or from the ILS; this setting @@ -473,6 +493,11 @@ sort = "last_indexed desc" ; ; The available autocomplete handlers are: ; +; Eds:[Mode] +; Use the EBSCO Discovery Service autocomplete handler (only intended to be +; used in the context of the EDS backend). [Mode] can be either "holdings" for +; title completion in PubFinder or "rawqueries" for completion of basic textual +; queries. ; None ; Do not provide any suggestions. You should use this handler if you want to ; disable suggestions for one search type while still providing suggestions @@ -515,7 +540,7 @@ auto_submit = true [Autocomplete_Types] Title = "Solr:Title" JournalTitle = "Solr:JournalTitle" -Author = "Solr:Author:author,author2" +Author = "Solr:Author:author,author2,author_corporate" Subject = "Solr:Subject:topic,genre,geographic,era" CallNumber = "SolrCN" ISN = "Solr:ISN:isbn,issn" diff --git a/config/vufind/searchspecs.yaml b/config/vufind/searchspecs.yaml index eaf01482b286f0cd8b8c883b67619218186a303b..a3fd4a6ead7779825888e36e10f64412a7f0799d 100644 --- a/config/vufind/searchspecs.yaml +++ b/config/vufind/searchspecs.yaml @@ -4,7 +4,8 @@ # Format is: # searchType: # # CustomMunge is an optional section to define custom pre-processing of -# # user input. See below for details of munge actions. +# # user input when Dismax does not apply. See below for details of +# # munge actions. # CustomMunge: # MungeName1: # - [action1, actionParams] @@ -12,6 +13,15 @@ # - [action3, actionParams] # MungeName2: # - [action1, actionParams] +# # While CustomMunge only applies to Lucene search, DismaxMunge is a +# # counterpart that applies to Dismax and eDismax queries. DismaxMunge +# # contains a chain of custom munge operations as described below. Note +# # that using this function is NOT RECOMMENDED if you can accomplish your +# # goals more elegantly through Solr schema adjustments, but it may be +# # useful in some situations, such as when you are connecting to a Solr +# # instance that is beyond your direct control. Here is an example stanza: +# DismaxMunge: +# - [preg_replace, '/^.*$/', '"$1"'] # # DismaxFields is optional and defines the fields sent to the Dismax handler # # when we are able to use it. QueryFields will be used for advanced # # searches that Dismax cannot support. QueryFields is always used if no @@ -114,7 +124,7 @@ # #----------------------------------------------------------------------------------- # -# Munge types are based on the original Solr.php code, and consist of: +# Munge types are string manipulation rules, and consist of: # # onephrase: eliminate all quotes and do it as a single phrase. # testing "one two" @@ -145,6 +155,7 @@ # [preg_replace, pattern, replacement] - Perform a regular expression replace # using the preg_replace() PHP function. If you use backreferences in your # replacement phrase, be sure to escape dollar signs (i.e. \$1, not $1). +# [ucfirst] - Uppercase the first letter of each word in the string # [uppercase] - Convert string to uppercase # # See the CallNumber search below for an example of custom munging in action. diff --git a/config/vufind/website.ini b/config/vufind/website.ini index 03f9f3a73773404f7f5106c34fcf792d1e792663..011fa32cce9cd4c6aefba80cc6376d8eb835bcd7 100644 --- a/config/vufind/website.ini +++ b/config/vufind/website.ini @@ -25,6 +25,10 @@ category = "Category" linktype = "Link Type" subject = "Subject" +[FacetLabels] +labelSections[] = Facets +checkboxSections[] = CheckboxFacets + [Results_Settings] ; By default, how many values should we show for each facet? (-1 for no limit) facet_limit = 30 diff --git a/devops/docker/composer/Dockerfile b/devops/docker/composer/Dockerfile index 88483ea7d1b600dd05990993d1eae951ffe764e6..6bb77fbecc8f6704bf4c9f8724ac6ad3ff7d5909 100644 --- a/devops/docker/composer/Dockerfile +++ b/devops/docker/composer/Dockerfile @@ -17,6 +17,7 @@ # @license https://opensource.org/licenses/GPL-3.0 GNU GPLv3 FROM composer:1.9.3 -RUN apk add --no-cache freetype-dev libxml2-dev \ +RUN apk add --no-cache freetype-dev libxml2-dev icu-dev \ && docker-php-ext-install gd \ - && docker-php-ext-install soap \ No newline at end of file + && docker-php-ext-install soap \ + && docker-php-ext-install intl \ No newline at end of file diff --git a/harvest/oai.ini b/harvest/oai.ini index 7fc22feb9d6caef45b0ff5b3123db8588bea8b08..4cefe391cdbd4dc1ecaefdc042d2630999f16b8d 100644 --- a/harvest/oai.ini +++ b/harvest/oai.ini @@ -49,11 +49,12 @@ ; combined into a fewer number of files (this is determined by the OAI server's ; response chunk size). The default setting (false) will result in a new file being ; created for each record. Note that this function is primarily intended for -; harvesting MARC records; many of the example XSLT transformations for other +; harvesting MARC records; many of VuFind's example XSLT transformations for other ; types of metadata are designed to process one record at a time and will not -; work with this setting. However, it may be possible to revise the XSLT to handle -; batches of records, and such improvements would be welcomed as contributions to -; future releases of VuFind. +; work with this setting. However, it is possible to revise the XSLT to handle +; batches of records. Starting with VuFind 6.0, a demo .xsl for OJS that can handle +; combined records within a <collection> tag has been included as an example +; and can be found at $VUFIND_HOME/import/xsl/ojs-multirecord.xsl. ; ; combineRecordsTag may be used to supply a beginning and ending XML tag (if ; combinedRecords is set to true) which will be used to wrap the set of diff --git a/import-marc-auth.bat b/import-marc-auth.bat index e6b0004ac1e8113a8329c373b4346fce7dda9868..8112207bbda5db49ace322db049ccaf4ce40e94c 100644 --- a/import-marc-auth.bat +++ b/import-marc-auth.bat @@ -13,9 +13,12 @@ rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat rem If env.bat doesn't exist, the user hasn't run the installer yet. -echo ERROR: env.bat does not exist -- could not set up environment. +echo WARNING: env.bat does not exist -- trying default environment settings. echo Please run "php install.php" to correct this problem. -goto end +rem Extract path from current batch file and trim trailing slash: +set VUFIND_HOME=%~dp0% +set VUFIND_HOME=%VUFIND_HOME:~0,-1% +goto vufindhomefound :useenvbat call env > nul if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound @@ -28,6 +31,7 @@ if not exist %VUFIND_LOCAL_DIR%\import\import_auth.properties goto nolocalproper set PROPERTIES_FILE=%VUFIND_LOCAL_DIR%\import\import_auth.properties goto propertiesfound :nolocalproperties +echo WARNING: VUFIND_LOCAL_DIR environment variable is not set. Is this intentional? set PROPERTIES_FILE=%VUFIND_HOME%\import\import_auth.properties :propertiesfound diff --git a/import-marc.bat b/import-marc.bat index c4322107e00484ff397fce441752e910a5fe71a6..2a2bcc963813d4f9bc01174c32d962c09fa038ce 100644 --- a/import-marc.bat +++ b/import-marc.bat @@ -60,7 +60,7 @@ set EXTRA_SOLRMARC_SETTINGS=%EXTRA_SOLRMARC_SETTINGS% -Dsolr.core.name=%SOLRCOR rem ################################################## rem # Set VUFIND_HOME rem ################################################## -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat @@ -73,7 +73,7 @@ set VUFIND_HOME=%VUFIND_HOME:~0,-1% goto vufindhomefound :useenvbat call env > nul -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound @@ -81,11 +81,11 @@ goto end rem ##################################################### rem # Build java command rem ##################################################### -if not (!%JAVA_HOME%!)==(!!) goto javahomefound +if not "!%JAVA_HOME%!"=="!!" goto javahomefound set JAVA=java goto javaset :javahomefound -set JAVA=%JAVA_HOME%\bin\java +set JAVA="%JAVA_HOME%\bin\java" :javaset rem ################################################## diff --git a/index-alphabetic-browse.bat b/index-alphabetic-browse.bat index ceccbb467fafb7b957ec515f1201899d24284eae..30e92f13da59efa95e6cfb141cc94078f7bcbed6 100644 --- a/index-alphabetic-browse.bat +++ b/index-alphabetic-browse.bat @@ -12,7 +12,7 @@ goto end rem ################################################## rem # Set SOLR_HOME rem ################################################## -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat @@ -25,22 +25,22 @@ set VUFIND_HOME=%VUFIND_HOME:~0,-1% goto vufindhomefound :useenvbat call env > nul -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound -if not (!%SOLR_HOME%!)==(!!) goto solrhomefound +if not "!%SOLR_HOME%!"=="!!" goto solrhomefound set SOLR_HOME=%VUFIND_HOME%\solr\vufind :solrhomefound rem ##################################################### rem # Build java command rem ##################################################### -if not (!%JAVA_HOME%!)==(!!) goto javahomefound +if not "!%JAVA_HOME%!"=="!!" goto javahomefound set JAVA=java goto javaset :javahomefound -set JAVA=%JAVA_HOME%\bin\java +set JAVA="%JAVA_HOME%\bin\java" :javaset cd %VUFIND_HOME%\import @@ -91,7 +91,7 @@ set args="%bib_index%" "%field%" "%auth_index%" "%browse%.tmp" :skipauth rem Extract lines from Solr -java %jvmopts% -Dfile.encoding="UTF-8" -Dfield.preferred=heading -Dfield.insteadof=use_for -cp %CLASSPATH% PrintBrowseHeadings %args% +%JAVA% %jvmopts% -Dfile.encoding="UTF-8" -Dfield.preferred=heading -Dfield.insteadof=use_for -cp %CLASSPATH% PrintBrowseHeadings %args% rem Sort lines sort %browse%.tmp /o sorted-%browse%.tmp /rec 65535 @@ -100,7 +100,7 @@ rem Remove duplicate lines php %VUFIND_HOME%\util\dedupe.php "sorted-%browse%.tmp" "unique-%browse%.tmp" rem Build database file -java -Dfile.encoding="UTF-8" -cp %CLASSPATH% CreateBrowseSQLite "unique-%browse%.tmp" "%browse%_browse.db" +%JAVA% -Dfile.encoding="UTF-8" -cp %CLASSPATH% CreateBrowseSQLite "unique-%browse%.tmp" "%browse%_browse.db" del /q *.tmp > nul diff --git a/languages/CallNumberFirst/ca.ini b/languages/CallNumberFirst/ca.ini new file mode 100644 index 0000000000000000000000000000000000000000..e7a3ff2ff453238e53fc874abf1fad8be3ea07f3 --- /dev/null +++ b/languages/CallNumberFirst/ca.ini @@ -0,0 +1,2 @@ +A - General Works = "A - Treballs generalss" +B - Philosophy, Psychology, Religion = "B - Filosofia, Psicologia, Religió" diff --git a/languages/CallNumberFirst/hi.ini b/languages/CallNumberFirst/hi.ini new file mode 100644 index 0000000000000000000000000000000000000000..59780d405644f099ac6df4144af72af9cded7909 --- /dev/null +++ b/languages/CallNumberFirst/hi.ini @@ -0,0 +1,21 @@ +A - General Works = "A - सामानà¥à¤¯ विषय " +B - Philosophy, Psychology, Religion = "B – दरà¥à¤¶à¤¨ शासà¥à¤¤à¥à¤°, मनोविजà¥à¤žà¤¾à¤¨ शासà¥à¤¤à¥à¤°, धरà¥à¤® शासà¥à¤¤à¥à¤° " +C - Historical Sciences = "C - इतिहास विजà¥à¤žà¤¾à¤¨" +D - World History = "D - वैशà¥à¤µà¤¿à¤• इतिहास " +E - United States History = "E - अमेरिका का इतिहास " +F - General American History = "F - सामानà¥à¤¯ इतिहास : अमेरिका " +G - Geography, Anthropology, Recreation = "G - à¤à¥‚गोल, मानव विजà¥à¤žà¤¾à¤¨, मनोरंजन " +H - Social Science = "H - समाज शासà¥à¤¤à¥à¤° " +J - Political Science = "J – राजनीति शासà¥à¤¤à¥à¤° " +K - Law = "K - कानून" +L - Education = "L - शिकà¥à¤·à¤¾ शासà¥à¤¤à¥à¤° " +M - Music = "M - संगीत" +N - Fine Arts = "N - ललित कला" +P - Language and Literature = "P - à¤à¤¾à¤·à¤¾ à¤à¤µà¤‚ साहितà¥à¤¯" +Q - Science = "Q - विजà¥à¤žà¤¾à¤¨ " +R - Medicine = "R - चिकितà¥à¤¸à¤¾ विजà¥à¤žà¤¾à¤¨" +S - Agriculture = "S - कृषि" +T - Technology = "T – पà¥à¤°à¥Œà¤¦à¥à¤¯à¥‹à¤—िकी" +U - Military Science = "U - सैनà¥à¤¯ विजà¥à¤žà¤¾à¤¨" +V - Naval Science = "V - नौसेना विजà¥à¤žà¤¾à¤¨" +Z - Library Science = "Z - पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ विजà¥à¤žà¤¾à¤¨" diff --git a/languages/CallNumberFirst/hr.ini b/languages/CallNumberFirst/hr.ini new file mode 100644 index 0000000000000000000000000000000000000000..6371917b18482e375886d134341d8ea2ef77e550 --- /dev/null +++ b/languages/CallNumberFirst/hr.ini @@ -0,0 +1,22 @@ +; - Translation courtesy of Milo Ivir <mail@milotype.de> +A - General Works = "A - Opća graÄ‘a" +B - Philosophy, Psychology, Religion = "B - Filozofija, Psihologija, Religija" +C - Historical Sciences = "C - Povijesne znanosti" +D - World History = "D - Svjetska povijest" +E - United States History = "E - Povijest SAD-a" +F - General American History = "F - Opća ameriÄka povijest" +G - Geography, Anthropology, Recreation = "G - Geografija, Antropologija, Rekreacija" +H - Social Science = "H - DruÅ¡tvene znanosti" +J - Political Science = "J - PolitiÄke znanosti" +K - Law = "K - Pravo" +L - Education = "L - Obrazovanje" +M - Music = "M - Glazba" +N - Fine Arts = "N - Umjetnost" +P - Language and Literature = "P - Jezik i literatura" +Q - Science = "Q - Znanost" +R - Medicine = "R - Medicina" +S - Agriculture = "S - Poljoprivreda" +T - Technology = "T - Tehnologija" +U - Military Science = "U - Vojna znanost" +V - Naval Science = "V - Pomorstvo" +Z - Library Science = "Z - Bibliotekartsvo" diff --git a/languages/CreatorRoles/hi.ini b/languages/CreatorRoles/hi.ini new file mode 100644 index 0000000000000000000000000000000000000000..3c20a57f6df9689daad55f154ee33ec7922dfbe0 --- /dev/null +++ b/languages/CreatorRoles/hi.ini @@ -0,0 +1,346 @@ +abr = "सारांशकरà¥à¤¤à¤¾" +acp = "आरà¥à¤Ÿ कॉपीसà¥à¤Ÿ" +act = "अà¤à¤¿à¤¨à¥‡à¤¤à¤¾" +adi = "कला निरà¥à¤¦à¥‡à¤¶à¤•" +adp = "अनà¥à¤•à¥‚लक" +Adressat = "पतà¥à¤° पानेवाला" +adressat = "पतà¥à¤° पानेवाला" +aft = "ऑथरवरà¥à¤¡, कोलोफॉन आदि के लेखक।" +angebl hrsg = "संदिगà¥à¤§ पà¥à¤°à¤•à¤¾à¤¶à¤•" +angebl komp = "संदिगà¥à¤§ लेखक / संगीतकार" +Angebl Verf = "संदिगà¥à¤§ पà¥à¤°à¤•à¤¾à¤¶à¤•" +angebl verf = "संदिगà¥à¤§ पà¥à¤°à¤•à¤¾à¤¶à¤•" +angebl übers = "संदिगà¥à¤§ अनà¥à¤µà¤¾à¤¦à¤•" +animation = "à¤à¤¨à¤¿à¤®à¥‡à¤Ÿà¤°" +anl = "विशà¥à¤²à¥‡à¤·à¤•" +anm = "सजीव बनाने वाला" +ann = "वà¥à¤¯à¤¾à¤–à¥à¤¯à¤¾à¤•à¤¾à¤°" +ant = "गà¥à¤°à¤‚थ सूची पूरà¥à¤µà¤µà¤¤à¥" +ape = "à¤à¤ªà¥€à¤²à¥€" +apl = "अपीलकरà¥à¤¤à¤¾" +app = "आवेदक" +aqt = "उदà¥à¤§à¤°à¤£ या पाठसार में लेखक" +arc = "वासà¥à¤¤à¥à¤•à¤¾à¤°" +ard = "कलातà¥à¤®à¤• निरà¥à¤¦à¥‡à¤¶à¤•" +arr = "पà¥à¤°à¤¬à¤¨à¥à¤§ करनेवाला" +art = "कलाकार" +asg = "संपतà¥à¤¤à¤¿-à¤à¤¾à¤—ी" +asn = "संबदà¥à¤§ नाम" +ato = "ऑटोगà¥à¤°à¤¾à¤«à¤°" +att = "दिया गया नाम" +auc = "नीलामकरà¥à¤¤à¤¾" +aud = "संवाद लेखक" +Auftraggeber = "पकà¥à¤·à¤•à¤¾à¤°" +aui = "परिचय का लेखक, आदि।" +aus = "पटकथा लेखक" +aut = "लेखक" +bdd = "बाइंडिंग डिजाइनर" +Bearb = "संपादक" +bearb = "संपादक" +Begr = "पà¥à¤°à¤¾à¤¯à¥‹à¤œà¤•" +begr = "पà¥à¤°à¤¾à¤¯à¥‹à¤œà¤•" +Beiträger = "योगदानकरà¥à¤¤à¤¾" +beiträger = "साहितà¥à¤¯à¤¿à¤• योगदानकरà¥à¤¤à¤¾" +beiträger k = "कलातà¥à¤®à¤• योगदानकरà¥à¤¤à¤¾" +beiträger m = "संगीत योगदानकरà¥à¤¤à¤¾" +bjd = "बà¥à¤•à¤œà¥ˆà¤•à¥‡à¤Ÿ डिजाइनर" +bkd = "पà¥à¤¸à¥à¤¤à¤• डिजाइनर" +bkp = "पà¥à¤¸à¥à¤¤à¤• निरà¥à¤®à¤¾à¤¤à¤¾" +blw = "पà¥à¤°à¤¶à¤¸à¥à¤¤à¤¿ लेखक" +bnd = "जिलà¥à¤¦à¤¸à¤¾à¤œà¤¼" +bpd = "बà¥à¤•à¤ªà¥à¤²à¥‡à¤Ÿ डिजाइनर" +brd = "पà¥à¤°à¤¸à¤¾à¤°à¤£à¤•à¤°à¥à¤¤à¤¾" +brl = "बà¥à¤°à¥‡à¤² à¤à¤®à¥à¤¬à¥‰à¤¸à¤°" +bsl = "पà¥à¤¸à¥à¤¤à¤•à¤µà¤¿à¤•à¥à¤°à¥‡à¤¤à¤¾" +bühnenbild = "सेट डिजाइनर" +cas = "कासà¥à¤Ÿà¤°" +ccp = "संकलà¥à¤ªà¤¨à¤¾ करने वाला" +choreinstud = "कà¥à¤µà¤¾à¤¯à¤° पूरà¥à¤µà¤¾à¤à¥à¤¯à¤¾à¤¸" +choreogr = "नृतà¥à¤¯à¤°à¤šà¤¨à¤¾-कार" +chr = "नृतà¥à¤¯à¤°à¤šà¤¨à¤¾-कार" +clb = "सहकरà¥à¤®à¥€" +cli = "पकà¥à¤·à¤•à¤¾à¤°" +cll = "सà¥à¤²à¥‡à¤–क" +clr = "कलरिसà¥à¤Ÿ" +clt = "कोलोटाइपर" +cmm = "समीकà¥à¤·à¤•" +cmp = "संगीतकार" +cmt = "संगीतकार" +cnd = "संवाहक" +cng = "छायाकार" +cns = "नियंतà¥à¤°à¤•" +coe = "पà¥à¤°à¤¤à¤¿à¤¯à¥‹à¤—ी-à¤à¤ªà¥€à¤²à¥€" +col = "संगà¥à¤°à¤¹à¤•à¤°à¥à¤¤à¥à¤¤à¤¾" +com = "संकलक" +con = "संरकà¥à¤·à¤•" +cor = "संगà¥à¤°à¤¹ पंजीयक" +cos = "पà¥à¤°à¤¤à¤¿à¤¯à¥‹à¤—ी" +cot = "पà¥à¤°à¤¤à¤¿à¤¯à¥‹à¤—ी-अपीलारà¥à¤¥à¥€" +cou = "नà¥à¤¯à¤¾à¤¯à¤¾à¤²à¤¯-शासित" +cov = "कवर डिजाइनर" +cpc = "कॉपीराइट दावेदार" +cpe = "शिकायतकरà¥à¤¤à¤¾-à¤à¤ªà¥€à¤²à¥€" +cph = "सà¥à¤µà¤¤à¥à¤µà¤¾à¤§à¤¿à¤•à¤¾à¤°à¥€" +cpl = "शिकायतकरà¥à¤¤à¤¾" +cpt = "शिकायतकरà¥à¤¤à¤¾-अपीलारà¥à¤¥à¥€" +cre = "रचनाकार" +crp = "संवाददाता" +crr = "पतà¥à¤°à¤¾à¤µà¥‡à¤•à¥à¤·à¤•" +crt = "नà¥à¤¯à¤¾à¤¯à¤¾à¤²à¤¯ पतà¥à¤°à¤•à¤¾à¤°" +csl = "सलाहकार" +csp = "à¤à¤• परियोजना के लिठसलाहकार" +cst = "कॉसà¥à¤Ÿà¥à¤¯à¥‚म डिजाइनर" +ctb = "अंशदाता" +cte = "कनà¥à¤Ÿà¥‡à¤¸à¥à¤Ÿà¥€-à¤à¤ªà¥€à¤²à¥€ " +ctg = "मानचितà¥à¤°à¤•à¤¾à¤°" +ctr = "ठेकेदार" +cts = "कनà¥à¤Ÿà¥‡à¤¸à¥à¤Ÿà¥€" +ctt = "कनà¥à¤Ÿà¥‡à¤¸à¥à¤Ÿà¥€ अपीलारà¥à¤¥à¥€ " +cur = "संगà¥à¤°à¤¹à¤¾à¤§à¥à¤¯à¤•à¥à¤·" +cwt = "लिखित पाठके लिठटीकाकार" +darst = "अà¤à¤¿à¤¨à¥‡à¤¤à¤¾" +dbp = "वितरण सà¥à¤¥à¤¾à¤¨" +dfd = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€" +dfe = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€-à¤à¤ªà¥€à¤²à¥€ " +dft = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€-अपीलारà¥à¤¥à¥€" +dgg = "डिगà¥à¤°à¥€ देने वाली संसà¥à¤¥à¤¾" +dgs = "उपाधि परà¥à¤¯à¤µà¥‡à¤•à¥à¤·à¤•" +dir = "संवाहक" +dis = "डिसà¥à¤¸à¥‡à¤°à¤Ÿà¥‡à¤¨à¥à¤Ÿ" +dln = "आलेखक" +dnc = "नरà¥à¤¤à¤•" +dnr = "दाता" +dpc = "चितà¥à¤°à¤¿à¤¤" +dpt = "जमाकरà¥à¤¤à¤¾" +drehbuch = "पटकथा लेखक" +drm = "रचयिता" +drt = "निरà¥à¤¦à¥‡à¤¶à¤•" +dsr = "डिजाइनर" +dst = "वितरक" +dtc = "डेटा योगदानकरà¥à¤¤à¤¾" +dte = "डेडिकेटी" +dtm = "डाटा पà¥à¤°à¤¬à¤‚धक" +dto = " डेडिकेटर" +dub = "संदिगà¥à¤§ लेखक" +edc = "संकलन का संपादक" +edm = "चलती छवि के संपादक" +edt = "संपादक" +egr = "उतà¥à¤•à¥€à¤°à¥à¤£à¤•" +elg = "विदà¥à¤¯à¥à¤¤à¥à¤•à¤¾à¤°" +elt = "à¤à¤²à¥‡à¤•à¥à¤Ÿà¥à¤°à¥‹à¤Ÿà¤¾à¤‡à¤ªà¤°" +eng = "अà¤à¤¿à¤¯à¤¾à¤¨à¥à¤¤à¤¾" +enj = "अधिकार कà¥à¤·à¥‡à¤¤à¥à¤°" +etr = "उतà¥à¤•à¥€à¤°à¥à¤£à¤•" +evp = "पà¥à¤°à¤¸à¤‚ग सà¥à¤¥à¤²" +exp = "विशेषजà¥à¤ž" +fac = "अनà¥à¤²à¤¿à¤ªà¤¿à¤•à¤°à¥à¤¤à¤¾" +fds = "फिलà¥à¤® वितरक" +fld = "कà¥à¤·à¥‡à¤¤à¥à¤° निदेशक" +flm = "फिलà¥à¤® संपादक" +fmd = "फिलà¥à¤® निरà¥à¤¦à¥‡à¤¶à¤•" +fmk = "फिलà¥à¤® निरà¥à¤®à¤¾à¤¤à¤¾" +fmo = "à¤à¥‚तपूरà¥à¤µ सà¥à¤µà¤¾à¤®à¥€" +fmp = "चलचितà¥à¤° निरà¥à¤®à¤¾à¤¤à¤¾" +fnd = "पà¥à¤°à¤¾à¤¯à¥‹à¤œà¤•" +Forts = "सिलसिला" +fotogr = "फोटोगà¥à¤°à¤¾à¤«à¤°" +fpy = "पà¥à¤°à¤¥à¤® पकà¥à¤·" +frg = "जालसाज" +gis = "à¤à¥Œà¤—ोलिक सूचना विशेषजà¥à¤ž" +grt = "गà¥à¤°à¤¾à¤«à¤¿à¤• तकनीशियन" +hg = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +his = "मेजबान संसà¥à¤¥à¤¾" +hnr = "समà¥à¤®à¤¾à¤¨à¤¿à¤¤" +Hrsg = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +hrsg = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +hst = "मेज़बान" +Ill = "वà¥à¤¯à¤¾à¤–à¥à¤¯à¤¾à¤¤à¤¾" +ill = "वà¥à¤¯à¤¾à¤–à¥à¤¯à¤¾à¤¤à¤¾" +ilu = " इलà¥à¤¯à¥à¤®à¤¿à¤¨à¥‡à¤Ÿà¤°" +ins = "उतà¥à¤•à¥€à¤°à¥à¤£à¤•" +inszenierung = "मंच पà¥à¤°à¤¬à¤‚धक" +interpr = "à¤à¤¾à¤·à¤¾à¤‚तरकार" +interpret = "à¤à¤¾à¤·à¤¾à¤‚तरकार" +interviewer = "साकà¥à¤·à¤¾à¤¤à¥à¤•à¤¾à¤°à¤•à¤°à¥à¤¤à¤¾" +interviewter = "साकà¥à¤·à¤¾à¤¤à¥à¤•à¤¾à¤°à¤¦à¤¾à¤¤à¤¾" +inv = "आविषà¥à¤•à¤¾à¤°à¤•" +isb = "जारी निकाय" +itr = "वादक" +ive = "साकà¥à¤·à¤¾à¤¤à¥à¤•à¤¾à¤°à¤¦à¤¾à¤¤à¤¾" +ivr = "साकà¥à¤·à¤¾à¤¤à¥à¤•à¤¾à¤°à¤•à¤°à¥à¤¤à¤¾" +jud = "नà¥à¤¯à¤¾à¤¯à¤¾à¤§à¥€à¤¶" +jug = "कà¥à¤·à¥‡à¤¤à¥à¤°à¤¾à¤§à¤¿à¤•à¤¾à¤° शासित" +kad = " गीतकार" +kamera = "कैमरा" +kartograph = "मानचितà¥à¤°à¤•à¤¾à¤°" +komm = "à¤à¤¾à¤·à¥à¤¯à¤•à¤¾à¤°" +Komment = "à¤à¤¾à¤·à¥à¤¯à¤•à¤¾à¤°" +Komp = "संगीतकार" +komp = "संगीतकार" +Korresp = "संवाददाता" +kostüm = "कॉसà¥à¤Ÿà¥à¤¯à¥‚म डिजाइनर" +lbr = "पà¥à¤°à¤¯à¥‹à¤—शाला" +lbt = "गीतकार" +ldr = "पà¥à¤°à¤¯à¥‹à¤—शाला निदेशक" +led = "नेतृतà¥à¤µ" +lee = "लिबेली-à¤à¤ªà¥€à¤²à¥€" +lel = "लिबेली" +len = "ऋणदाता" +let = "लेबेली -अपीलारà¥à¤¥à¥€" +lgd = "लाइटिंग डिज़ाइनर" +lie = "लिबेलैंट à¤à¤ªà¥€à¤²à¥€" +lil = "लिबेलेंट" +lit = "लिबेलेंट अपीलकरà¥à¤¤à¤¾" +lsa = "परिदृशà¥à¤¯ वासà¥à¤¤à¥à¤•à¤¾à¤°" +lse = "अनà¥à¤œà¥à¤žà¤ªà¥à¤¤à¤¿à¤§à¤¾à¤°à¥€" +lso = "अनà¥à¤œà¥à¤žà¤ªà¥à¤¤à¤¿à¤¦à¤¾à¤¤à¤¾" +ltg = "लिथो-मà¥à¤¦à¥à¤°à¥à¤•" +lyr = "गीतकार" +mcp = "गीतकार" +mdc = "मेटाडाटा संपरà¥à¤•" +med = "माधà¥à¤¯à¤®" +mfp = "निरà¥à¤®à¤¾à¤£ सà¥à¤¥à¤¾à¤¨" +mfr = "निरà¥à¤®à¤¾à¤£" +mitarb = "सहयोगी" +mod = "मॉडरेटर" +moderation = "संतà¥à¤²à¤¨" +mon = "मॉनिटर" +mrb = "मारà¥à¤¬à¥à¤²à¤°" +mrk = "मारà¥à¤•à¤…प संपादक" +msd = "संगीत निरà¥à¤¦à¥‡à¤¶à¤•" +mte = "धातà¥-उकेरक" +mtk = "मिनटà¥à¤¸ लिखने वाला" +mus = "संगीतकार" +mutmassl Verf = "विखà¥à¤¯à¤¾à¤¤ लेखक" +mutmassl VerfKomp = "विखà¥à¤¯à¤¾à¤¤ लेखक / संगीतकार" +mutmaßl hrsg = "विखà¥à¤¯à¤¾à¤¤ पà¥à¤°à¤•à¤¾à¤¶à¤•" +Mutmaßl Verf = "विखà¥à¤¯à¤¾à¤¤ लेखक" +mutmaßl verf = "विखà¥à¤¯à¤¾à¤¤ लेखक" +mutmaßl übers = "विखà¥à¤¯à¤¾à¤¤ अनà¥à¤µà¤¾à¤¦à¤•" +Nachr = "ऑथरवरà¥à¤¡, कोलोफॉन आदि के लेखक।" +nrt = "वरà¥à¤£à¤¨à¤•à¤°à¥à¤¤à¤¾" +opn = "पà¥à¤°à¤¤à¤¿à¤¦à¥à¤µà¤‚दà¥à¤µà¥€" +org = "पà¥à¤°à¤µà¤°à¥à¤¤à¤•" +orm = "आयोजक" +osp = "ऑनसà¥à¤•à¥à¤°à¥€à¤¨ पà¥à¤°à¤¸à¥à¤¤à¥‹à¤¤à¤¾" +oth = "अनà¥à¤¯" +own = "मालिक" +pan = "पैनलिसà¥à¤Ÿ" +pat = "उपà¤à¥‹à¤•à¥à¤¤à¤¾" +pbd = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ निदेशक" +pbl = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +pdr = "परियोजना निदेशक" +pfr = "शà¥à¤¦à¥à¤§à¤¿à¤•à¤¾à¤°à¤•" +pht = "फोटोगà¥à¤°à¤¾à¤«à¤°" +plt = "पà¥à¤²à¥‡à¤Ÿ निरà¥à¤®à¤¾à¤¤à¤¾" +pma = "अनà¥à¤®à¤¤à¤¿ दायक अà¤à¤¿à¤•à¤°à¤£" +pmn = "निरà¥à¤®à¤¾à¤£ पà¥à¤°à¤¬à¤‚धक" +pop = "पà¥à¤²à¥‡à¤Ÿà¥‹à¤‚ का मà¥à¤¦à¥à¤°à¤•" +ppm = "कागज बनाने वाला" +ppt = "कठपà¥à¤¤à¤²à¥€ चलानेवाला" +pra = "पà¥à¤°à¥‡à¤¸à¥‡à¤¸" +prc = "पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ संपरà¥à¤•" +prd = "निरà¥à¤®à¤¾à¤£ करà¥à¤®à¥€" +pre = "पà¥à¤°à¤¸à¥à¤¤à¥à¤¤à¤•à¤°à¥à¤¤à¤¾" +prf = "अà¤à¤¿à¤¨à¥‡à¤¤à¤¾" +prg = "कà¥à¤°à¤®à¤¾à¤¦à¥‡à¤¶à¤•" +prm = "पà¥à¤°à¤¿à¤‚टमेकर" +prn = "निरà¥à¤®à¤¾à¤£ संगठन" +pro = "निरà¥à¤®à¤¾à¤¤à¤¾" +prod = "निरà¥à¤®à¤¾à¤¤à¤¾" +prp = "निरà¥à¤®à¤¾à¤£ सà¥à¤¥à¤¾à¤¨ " +prs = "पà¥à¤°à¥‹à¤¡à¤•à¥à¤¶à¤¨ डिजाइनर" +prt = "मà¥à¤¦à¥à¤°à¤•" +prv = "पà¥à¤°à¤¦à¤¾à¤¤à¤¾" +präses = "पà¥à¤°à¥‡à¤¸à¥‡à¤¸" +pta = "पेटेंट आवेदक" +pte = "अà¤à¤¿à¤¯à¥‹à¤—ी-अपीलि" +ptf = "अà¤à¤¿à¤¯à¥‹à¤—ी" +pth = "पेटेंट धारक" +ptt = "अà¤à¤¿à¤¯à¥‹à¤—ी -अपीलकरà¥à¤¤à¤¾" +pup = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ सà¥à¤¥à¤¾à¤¨" +rbr = "रयूबà¥à¤°à¤¿à¤•à¥‡à¤Ÿà¤°" +rcd = "धà¥à¤µà¤¨à¤¿ रिकॉरà¥à¤¡ करने वाला वà¥à¤¯à¤•à¥à¤¤à¤¿ " +rce = "रिकॉरà¥à¤¡à¤¿à¤‚ग अà¤à¤¿à¤¯à¤‚ता" +rcp = "पतà¥à¤° पानेवाला" +rdd = "रेडियो निरà¥à¤¦à¥‡à¤¶à¤•" +realisation = "पà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿" +Red = "संपादक" +red = "संपादक" +regie = "निरà¥à¤¦à¥‡à¤¶à¤•" +ren = "रेंडरर" +reporter = "संवाद-दाता" +res = "शोधकरà¥à¤¤à¤¾" +resp = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€" +rev = "समीकà¥à¤·à¤¾à¤•à¤¾à¤°" +rpc = "रेडियो निरà¥à¤®à¤¾à¤¤à¤¾" +rps = " संगà¥à¤°à¤¹" +rpt = "संवाद-दाता" +rpy = "उतà¥à¤¤à¤°à¤¦à¤¾à¤¯à¥€ पकà¥à¤·" +rse = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€-अपीलि" +rsg = "पà¥à¤¨à¤ƒà¤ªà¥à¤°à¤¦à¤°à¥à¤¶à¤•" +rsp = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€" +rsr = "पà¥à¤¨à¤°à¥à¤¦à¥à¤¦à¤¾à¤°à¤µà¤¾à¤¦à¥€" +rst = "पà¥à¤°à¤¤à¤¿à¤µà¤¾à¤¦à¥€-अपीलारà¥à¤¥à¥€" +rth = "अनà¥à¤¸à¤‚धान दल के पà¥à¤°à¤®à¥à¤–" +rtm = "अनà¥à¤¸à¤‚धान दल के सदसà¥à¤¯" +sad = "वैजà¥à¤žà¤¾à¤¨à¤¿à¤• सलाहकार" +Sammler = "संगà¥à¤°à¤¾à¤¹à¤•" +sammler = "संगà¥à¤°à¤¾à¤¹à¤•" +sce = "पटकथा लेखक" +Schreiber = "लेखक" +scl = "मूरà¥à¤¤à¤¿à¤•à¤¾à¤°" +scr = "मà¥à¤‚शी" +sds = "धà¥à¤µà¤¨à¤¿ डिजाइनर" +sec = "सचिव" +sgd = "मंच निरà¥à¤¦à¥‡à¤¶à¤•" +sgn = "सांकेतिक à¤à¤¾à¤·à¤¾ का संचार करने वाला" +sht = "सहायक मेजबान" +sll = "विकà¥à¤°à¥‡à¤¤à¤¾" +sng = "गायक" +spk = "वकà¥à¤¤à¤¾" +spn = "पà¥à¤°à¤¾à¤¯à¥‹à¤œà¤•" +sprecher = "वकà¥à¤¤à¤¾" +spy = "दूसरा पकà¥à¤·" +srv = "सरà¥à¤µà¥‡à¤•à¥à¤·à¤•" +std = "सेट डिजाइनर" +stecher = "उतà¥à¤•à¥€à¤°à¥à¤£à¤•" +stg = "सेटिंग" +stl = "कहानी सà¥à¤¨à¤¾à¤¨à¥‡ वाला" +stm = "सूतà¥à¤°à¤§à¤¾à¤°" +stn = "मानक निकाय" +str = "सà¥à¤Ÿà¥€à¤°à¤¿à¤“टाईपर" +tcd = "तकनीकी निदेशक" +tch = "शिकà¥à¤·à¤•" +textverf = "लेखक" +ths = "शोध सलाहकार" +tld = "टेलीविजन निरà¥à¤¦à¥‡à¤¶à¤•" +tlp = "टेलीविजन निरà¥à¤®à¤¾à¤¤à¤¾" +trc = "पà¥à¤°à¤¤à¤¿à¤²à¥‡à¤–क" +trl = "अनà¥à¤µà¤¾à¤¦à¤•" +tyd = "टाइप डिज़ाइनर" +tyg = "टाइपोगà¥à¤°à¤¾à¤«à¤°" +tänzer = "नृतक" +uvp = "विशà¥à¤µà¤µà¤¿à¤¦à¥à¤¯à¤¾à¤²à¤¯ सà¥à¤¥à¤¾à¤¨" +vac = "पारà¥à¤¶à¥à¤µ सà¥à¤µà¤° देने वाले अà¤à¤¿à¤¨à¥‡à¤¤à¤¾" +vdg = "वीडियोगà¥à¤°à¤¾à¤«à¤°" +Verstorb = "मृतक" +verstorb = "मृतक" +voc = "गायक" +vorl = "पà¥à¤°à¤¾à¤°à¥‚प" +Vorr = "परिचय का लेखक, आदि।" +wac = "जोड़ी गयी टिपà¥à¤ªà¤£à¥€ के लेखक" +wal = "जोड़े गठगीत के लेखक" +wam = "संलगà¥à¤¨ सामगà¥à¤°à¥€ के लेखक" +wat = "जोड़े गठलेख के लेखक " +wdc = "लकड़हारा" +wde = "लकड़ी का आवरण" +widmungsempfänger = "समरà¥à¤ªà¤£ à¤à¤¾à¤—ी" +win = "परिचय के लेखक" +wit = "गवाह" +wpr = "पà¥à¤°à¤¸à¥à¤¤à¤¾à¤µà¤¨à¤¾ के लेखक" +wst = "पूरक पाठà¥à¤¯ सामगà¥à¤°à¥€ के लेखक" +zeichner = "चितà¥à¤° बनानेवाला" +zensor = "नियंतà¥à¤°à¤•" +Ãœbers = "अनà¥à¤µà¤¾à¤¦à¤•" +übers = "अनà¥à¤µà¤¾à¤¦à¤•" diff --git a/languages/CreatorRoles/hr.ini b/languages/CreatorRoles/hr.ini new file mode 100644 index 0000000000000000000000000000000000000000..3b8d64ec7e0bdb7e97a1dd014d17a2f4bad22070 --- /dev/null +++ b/languages/CreatorRoles/hr.ini @@ -0,0 +1,357 @@ +; - Translation courtesy of Milo Ivir <mail@milotype.de> +; This file includes translations for author relator terms/abbreviations of the following standards: +; * MARC Code List for Relators (http://www.loc.gov/marc/relators/relacode.html) +; including discontinued codes: clb, grt, voc +; * German RAK Code List (recommended) for Relators +; * German RAK Code List for Relators extended by HBZ +; * German RAK Code List for Relators extended by GBV +; * German RAK Code List for Relators extended by SWB +; (http://swbtools.bsz-bw.de/cgi-bin/help.pl?cmd=kat&val=3010) +; Note: In some cases, terms are case sensitive, and case determines meaning; +; for example, see Beiträger vs. beiträger. +abr = "Autor skraćenog djela" +acp = "Autor kopija umjetnina" +act = "Glumac" +adi = "Kreativni direktor" +adp = "Prilagoditelj" +Adressat = "Adresat" +adressat = "Adresat" +aft = "Autor epiloga, impresum, itd." +angebl hrsg = "Dvojbeni izdavaÄ" +angebl komp = "Dvojbeni autor / komponist" +Angebl Verf = "Dvojbeni autor" +angebl verf = "Dvojbeni autor" +angebl übers = "Dvojbeni prevoditelj" +animation = "Animacija" +anl = "AnalitiÄar" +anm = "CrtaÄ animacija" +ann = "Autor napomena" +ant = "Bibliografski prethodnik" +ape = "Okrivljenik" +apl = "Žalitelj" +app = "Podnositelj" +aqt = "Autor u citatima ili tekstualnim sažecima" +arc = "Arhitekt" +ard = "UmjetniÄki voditelj" +arr = "Planer" +art = "Umjetnik" +asg = "Opunomoćenik" +asn = "Pridruženo ime" +ato = "Potpisnik" +att = "Pripisano ime" +auc = "Aukcionar" +aud = "Autor dijaloga" +Auftraggeber = "NaruÄitelj" +aui = "Autor uvoda, itd." +aus = "Scenarist" +aut = "Autor" +bdd = "Dizajner uveza" +Bearb = "Urednik" +bearb = "Urednik" +Begr = "OsnivaÄ" +begr = "OsnivaÄ" +Beiträger = "Pridonositelj" +beiträger = "Književni pridonositelj" +beiträger k = "UmjetniÄki pridonositelj" +beiträger m = "Glazbeni pridonositelj" +bjd = "Dizajner omota" +bkd = "Dizajner knjiga" +bkp = "ProizvoÄ‘aÄ knjiga" +blw = "Pisac preporuke" +bnd = "Knjigoveža" +bpd = "Dizajner ex librisa" +brd = "Prenositelj" +brl = "Graver brajice" +bsl = "Knjižar" +bühnenbild = "Scenograf" +cas = "LjevaÄ" +ccp = "Autor koncepta" +choreinstud = "Vježba zbora" +choreogr = "Koreograf" +chr = "Koreograf" +clb = "Suradnik" +cli = "Klijent" +cll = "Kaligraf" +clr = "Kolorist" +clt = "Svjetlotiskar" +cmm = "Komentator" +cmp = "Komponist" +cmt = "Slovoslagar" +cnd = "Dirigent" +cng = "Kinematograf" +cns = "Cenzor" +coe = "Osporavatelj žalbe – okrivljenik" +col = "Kolekcionar" +com = "SastavljaÄ" +con = "Konzervator" +cor = "Kurator zbirke" +cos = "Osporavatelj žalbe" +cot = "Osporavatelj žalbe – žalitelj" +cou = "Nadležni sud" +cov = "Dizajner korica" +cpc = "Tražitelj autorskog prava" +cpe = "Podnositelj žalbe – okrivljenik" +cph = "Vlasnik autorskog prava" +cpl = "Podnositelj žalbe" +cpt = "Podnositelj žalbe – žalitelj" +cre = "Stvaralac" +crp = "Dopisnik" +crr = "Korektor" +crt = "Sudski reporter" +csl = "Savjetnik" +csp = "Projektni savjetnik" +cst = "Kostimograf" +ctb = "Pridonositelj" +cte = "Branitelj osporavanja žalbe – okrivljenik" +ctg = "Kartograf" +ctr = "Stranka ugovora" +cts = "Branitelj osporavanja žalbe" +ctt = "Branitelj osporavanja žalbe – žalitelj" +cur = "Kurator" +cwt = "Komentator za pisani tekst" +darst = "IzvoÄ‘aÄ" +dbp = "Mjesto distribucije" +dfd = "Optuženik" +dfe = "Optuženik – okrivljenik" +dft = "Optuženik – žalitelj" +dgg = "Institucije koje daju akademski stupanj" +dgs = "Nadzornik akademskog rada" +dir = "Dirigent" +dis = "Disertant" +dln = "Skica" +dnc = "PlesaÄ" +dnr = "Donator" +dpc = "Odabrano" +dpt = "UlagaÄ" +drehbuch = "Scenarist" +drm = "TehniÄki crtaÄ" +drt = "Redatelj" +dsr = "Dizajner" +dst = "Distributor" +dtc = "Davatelj podataka" +dte = "Posvećenik" +dtm = "Upravitelj podataka" +dto = "Posvećuje" +dub = "Dvojbeni autor" +edc = "Urednik kompilacije" +edm = "Urednik djela pomiÄnih slika" +edt = "Urednik" +egr = "Graver" +elg = "ElektriÄar" +elt = "GalvanotehniÄar" +eng = "Inženjer" +enj = "OvlaÅ¡tena sudska nadležnost" +etr = "Bakrorezac" +evp = "Mjesto dogaÄ‘aja" +exp = "StruÄnjak" +fac = "Faksimil" +fds = "Distributor filma" +fld = "Å ef odjela" +flm = "Urednik filma" +fmd = "Redatelj filma" +fmk = "FilmaÅ¡" +fmo = "Prethodni vlasnik" +fmp = "ProizvoÄ‘aÄ filma" +fnd = "OsnivaÄ" +Forts = "Kontinuiranost" +fotogr = "Fotograf" +fpy = "Prva stranka" +frg = "Falsifikator" +gis = "Specijalist za geografske informacije" +grt = "GrafiÄki tehniÄar" +hg = "IzdavaÄ" +his = "Institucija domaćin" +hnr = "PoÄasna osoba" +Hrsg = "IzdavaÄ" +hrsg = "IzdavaÄ" +hst = "Domaćin" +Ill = "Ilustrator" +ill = "Ilustrator" +ilu = "Ilustrativni dekorater" +ins = "Pisac posvete" +inszenierung = "Scenski voditelj" +interpr = "Interpreter" +interpret = "Interpreter" +interviewer = "Anketar" +interviewter = "Ispitanik" +inv = "Izumitelj" +isb = "IzdavaÄko tijelo" +itr = "Instrumentalist" +ive = "Ispitanik" +ivr = "Ispitnik" +jud = "Sudac" +jug = "Vladajuća sudska nadležnost" +kad = "Autor kadence" +kamera = "Kamera" +kartograph = "Kartograf" +komm = "Komentator" +Komment = "Komentator" +Komp = "Komponist" +komp = "Komponist" +Korresp = "Dopisnik" +kostüm = "Kostimograf" +lbr = "Laboratorij" +lbt = "Libretist" +ldr = "Voditelj laboratorija" +led = "Vodstvo" +lee = "Podnositelj klevete – okrivljenik" +lel = "Podnositelj klevete" +len = "Zajmodavac" +let = "Podnositelj klevete – žalitelj" +lgd = "Majstor rasvjete" +lie = "Optuženik za klevetu – okrivljenik" +lil = "Optuženik za klevetu" +lit = "Optuženik za klevetu – žalitelj" +lsa = "Arhitekt krajolika" +lse = "Primatelj licence" +lso = "Davatelj licence" +ltg = "Litograf" +lyr = "Tekstopisac" +mcp = "UreÄ‘ivaÄ glazbe" +mdc = "Kontakt za metapodatke" +med = "Medij" +mfp = "Mjesto proizvodnje" +mfr = "ProizvoÄ‘aÄ" +mitarb = "Suradnik" +mod = "Moderator" +moderation = "Moderacija" +mon = "Nadglednik" +mrb = "ObraÄ‘ivaÄ mramora" +mrk = "UreÄ‘ivaÄ kodnih oznaka" +msd = "MuziÄki redatelj" +mte = "Metalo-graver" +mtk = "ZapisniÄar" +mus = "MuziÄar" +mutmassl Verf = "Navodni autor" +mutmassl VerfKomp = "Navodni autor / komponist" +mutmaßl hrsg = "Navodni izdavaÄ" +Mutmaßl Verf = "Navodni autor" +mutmaßl verf = "Navodni autor" +mutmaßl übers = "Navodni prevoditelj" +Nachr = "Autor epiloga, impresum, itd." +nrt = "PripovjedaÄ" +opn = "Protivnik" +org = "Izvorni autor" +orm = "Organizator" +osp = "Ekranski pripovjedaÄ" +oth = "Drugo" +own = "Vlasnik" +pan = "Sudionik panela" +pat = "Pokrovitelj" +pbd = "Voditelj izdanja" +pbl = "IzdavaÄ" +pdr = "Voditelj projekta" +pfr = "Korektor" +pht = "Fotograf" +plt = "ProizvoÄ‘aÄ ploÄa" +pma = "Agencija za izdavanje dozvola" +pmn = "Voditelj produkcije" +pop = "Tiskar ploÄa" +ppm = "ProizvoÄ‘aÄ papira" +ppt = "Lutkar" +pra = "Predsjednik" +prc = "Kontakt za procese" +prd = "Suradnici produkcije" +pre = "Predstavitelj" +prf = "IzvoÄ‘aÄ" +prg = "Programer" +prm = "Tiskar" +prn = "Tvrtka produkcije" +pro = "Producent" +prod = "Producent" +prp = "Mjesto produkcije" +prs = "Dizajner produkcije" +prt = "Tiskar" +prv = "Pružatelj usluge" +präses = "Predsjednik" +pta = "Podnositelj zahtjeva za patent" +pte = "Podnositelj tužbe – okrivljenik" +ptf = "Podnositelj tužbe" +pth = "Nositelj patenta" +ptt = "Podnositelj tužbe – žalitelj" +pup = "Mjesto publicirnja" +rbr = "Rubrikator" +rcd = "Tonski snimatelj" +rce = "Inženjer zvuka" +rcp = "Adresat" +rdd = "Voditelj radio stanice" +realisation = "Realizacija" +Red = "Urednik" +red = "Urednik" +regie = "Redatelj" +ren = "IscrtivaÄ" +reporter = "Reporter" +res = "Istražitelj" +resp = "Optuženik žalbe" +rev = "Recenzent" +rpc = "Radio producent" +rps = "Repozitorij" +rpt = "Reporter" +rpy = "Odgovorna stranka" +rse = "Optuženik žalbe – okrivljenik" +rsg = "Suredatelj predstave" +rsp = "Optuženik žalbe" +rsr = "Restaurator" +rst = "Optuženik žalbe – žalitelj" +rth = "Voditelj istraživaÄkog tima" +rtm = "ÄŒlan istraživaÄkog tima" +sad = "Znanstveni savjetnik" +Sammler = "Kolekcionar" +sammler = "Kolekcionar" +sce = "Scenarist" +Schreiber = "Pisac" +scl = "Kipar" +scr = "Pisar" +sds = "Majstor zvuka" +sec = "Sekretarijat" +sgd = "Scenski voditelj" +sgn = "Potpisnik" +sht = "Pomoćni domaćin" +sll = "ProdavaÄ" +sng = "PjevaÄ" +spk = "Govornik" +spn = "Sponzor" +sprecher = "Govornik" +spy = "Druga stranka" +srv = "Geodet" +std = "Scenograf" +stecher = "Graver" +stg = "Kulisa" +stl = "PripovjedaÄ" +stm = "Scenski upravitelj" +stn = "Organizacija za standardizaciju" +str = "Stereotipist" +tcd = "TehniÄki voditelj" +tch = "UÄitelj" +textverf = "Autor" +ths = "Savjetnik disertacije" +tld = "Tv voditelj" +tlp = "Tv producent" +trc = "Transkriptor" +trl = "Prevoditelj" +tyd = "Sizajner fontova" +tyg = "Tipograf" +tänzer = "PlesaÄ" +uvp = "Mjesto sveuÄiliÅ¡ta" +vac = "Sinkronizacija" +vdg = "Videograf" +Verstorb = "Pokojni" +verstorb = "Pokojni" +voc = "Solo pjevaÄ" +vorl = "Skica" +Vorr = "Autor uvoda, itd." +wac = "Pisac dodanih komentara" +wal = "Pisac dodanih stihova" +wam = "Pisac popratnog materijala" +wat = "Pisac dodanog teksta" +wdc = "Drvorezac" +wde = "Drvorezbar" +widmungsempfänger = "Posvećenik" +win = "Pisac uvoda" +wit = "Svjedok" +wpr = "Pisac predgovora" +wst = "Pisac dodatnogtekstualnog sadržaja" +zeichner = "CrtaÄ skica" +zensor = "Cenzor" +Ãœbers = "Prevoditelj" +übers = "Prevoditelj" diff --git a/languages/Exception/ar.ini b/languages/Exception/ar.ini new file mode 100644 index 0000000000000000000000000000000000000000..2495e219f25854ac67670eb189ec509114283d1a --- /dev/null +++ b/languages/Exception/ar.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بتغيير كلمة مرورك." +access_denied_read_fines = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بمشاهدة غراماتك." +access_denied_read_items = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بمشاهدة المواد التي قمت بطلبها أو استعارتها." +access_denied_read_patron = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بمشاهدة ملÙÙƒ الشخصي." +access_denied_write_items = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بطلب المواد أو تجديدها." diff --git a/languages/Exception/bn.ini b/languages/Exception/bn.ini new file mode 100644 index 0000000000000000000000000000000000000000..9f0040e61eb022fa7d434b140d0690daffa6f001 --- /dev/null +++ b/languages/Exception/bn.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "আপনার পাসওয়ারà§à¦¡ পরিবরà§à¦¤à¦¨ করার অনà§à¦®à¦¤à¦¿ নেই।" +access_denied_read_fines = "আপনার ফাইন দেখার অনà§à¦®à¦¤à¦¿ নেই।" +access_denied_read_items = "আপনার অনà§à¦°à§‹à¦§ অথবা চেকডআউট উপাদানগà§à¦²à¦¿ দেখার অনà§à¦®à¦¤à¦¿ নেই।" +access_denied_read_patron = "আপনাকে বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ পà§à¦°à§‹à¦«à¦¾à¦‡à¦² দেখার অনà§à¦®à¦¤à¦¿ নেই। " +access_denied_write_items = "উপাদানের অনà§à¦°à§‹à¦§ বা পà§à¦¨à¦°à§à¦¨à¦¬à§€à¦•à¦°à¦£ করার অণà§à¦®à¦¤à¦¿ নেই। " diff --git a/languages/Exception/ca.ini b/languages/Exception/ca.ini new file mode 100644 index 0000000000000000000000000000000000000000..b63c09199e83db62a42affcba585a8bc8af452e4 --- /dev/null +++ b/languages/Exception/ca.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "No tens permÃs per canviar la teva contrasenya." +access_denied_read_fines = "No teniu permÃs per veure les vostres multes." +access_denied_read_items = "No teniu permÃs per visualitzar els vostres articles sol·licitats o rebuts." +access_denied_read_patron = "No teniu permÃs per veure el vostre perfil d’usuari.." +access_denied_write_items = "No es permet sol·licitar o renovar articles." diff --git a/languages/Exception/cs.ini b/languages/Exception/cs.ini new file mode 100644 index 0000000000000000000000000000000000000000..b8caed3aacdccddd3af024ac1384c391f2187c0c --- /dev/null +++ b/languages/Exception/cs.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Nemáte oprávnÄ›nà pro zmÄ›nu hesla." +access_denied_read_fines = "Nemáte oprávnÄ›nà pro zobrazenà pÅ™ehledu poplatků." +access_denied_read_items = "Nemáte oprávnÄ›nà pro zobrazenà VaÅ¡ich výpůjÄek a rezervacÃ/objednávek." +access_denied_read_patron = "Nemáte oprávnÄ›nà pro zobrazenà informacà o vaÅ¡em Ätenářském kontÄ›." +access_denied_write_items = "Nemáte oprávnÄ›nà pro vytvářenà požadavků a prodlužovánà výpůjÄek." diff --git a/languages/Exception/de.ini b/languages/Exception/de.ini new file mode 100644 index 0000000000000000000000000000000000000000..c44c2007100ad6bce5c968c898d9b01a92faff29 --- /dev/null +++ b/languages/Exception/de.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Sie haben keine Berechtigung, Ihr Passwort zu ändern." +access_denied_read_fines = "Sie haben keine Berechtigung, ihre Gebühren einzusehen." +access_denied_read_items = "Sie haben keine Berechtigung, die von Ihnen bestellten oder ausgeliehenen Exemplare einzusehen." +access_denied_read_patron = "Sie haben keine Berechtigung, ihre Profildaten einzusehen." +access_denied_write_items = "Sie haben keine Berechtigung, Exemplare zu bestellen oder zu verlängern." diff --git a/languages/Exception/el.ini b/languages/Exception/el.ini new file mode 100644 index 0000000000000000000000000000000000000000..4829b5bc12d7f34ae48de94a7badd20100868955 --- /dev/null +++ b/languages/Exception/el.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Δεν Îχετε δυνατότητα να αλλάξετε τον κωδικό σας." +access_denied_read_fines = "Δεν Îχετε δυνατότητα να δείτε τα Ï€Ïόστιμά σας." +access_denied_read_items = "Δεν Îχετε δυνατότητα να δείτε τα τεκμήÏια που δανειστήκατε ή ζητήσατε." +access_denied_read_patron = "Δεν Îχετε δυνατότητα να δείτε το Ï€Ïοφίλ το λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚." +access_denied_write_items = "Δεν Îχετε δυνατότητα να ζητήσετε ή να ανανεώσετε τεκμήÏια." diff --git a/languages/Exception/en-gb.ini b/languages/Exception/en-gb.ini new file mode 100644 index 0000000000000000000000000000000000000000..452b128a19d2183ad2ef1c3fbbfb728ff00626d3 --- /dev/null +++ b/languages/Exception/en-gb.ini @@ -0,0 +1 @@ +@parent_ini = "Exception/en.ini" diff --git a/languages/Exception/en.ini b/languages/Exception/en.ini new file mode 100644 index 0000000000000000000000000000000000000000..93acc1e403bd9e42e257acbcfb4d90793af1a31c --- /dev/null +++ b/languages/Exception/en.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "You are not allowed to change your password." +access_denied_read_fines = "You are not allowed to view your fines." +access_denied_read_items = "You are not allowed to view your requested or checked out items." +access_denied_read_patron = "You are not allowed to view your user profile." +access_denied_write_items = "You are not allowed to request or renew items." diff --git a/languages/Exception/es.ini b/languages/Exception/es.ini new file mode 100644 index 0000000000000000000000000000000000000000..7f4d3b86feeff9d9de0da275e97164b24ecd739d --- /dev/null +++ b/languages/Exception/es.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "No se le permite cambiar su contraseña." +access_denied_read_fines = "No se le permite ver sus multas." +access_denied_read_items = "No se le permite ver los elementos solicitados o prestados." +access_denied_read_patron = "No tiene permiso para ver tu perfil de usuario." +access_denied_write_items = "No se le permite solicitar o renovar Ãtemes." diff --git a/languages/Exception/fr.ini b/languages/Exception/fr.ini new file mode 100644 index 0000000000000000000000000000000000000000..3ed52183c8059d6ac25e85ff343de8f770c3d042 --- /dev/null +++ b/languages/Exception/fr.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Vous n'avez pas les droits pour modifier votre mot de passe." +access_denied_read_fines = "Vous n'avez pas les droits pour consulter vos amendes." +access_denied_read_items = "Vous n'avez pas les droits pour consulter les documents que vous avez empruntés ou demandés." +access_denied_read_patron = "Vous n'avez pas les droits pour consulter votre profil." +access_denied_write_items = "Vous n'avez pas les droits pour demander un document ou renouveler un prêt." diff --git a/languages/Exception/hi.ini b/languages/Exception/hi.ini new file mode 100644 index 0000000000000000000000000000000000000000..543a0f9d9523d817dbb1bd6690055a2b4f2a16ae --- /dev/null +++ b/languages/Exception/hi.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "आपको अपना पासवरà¥à¤¡ बदलने की अनà¥à¤®à¤¤à¤¿ नहीं है"" +access_denied_read_fines = "आपको अपना जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾ देखने की अनà¥à¤®à¤¤à¤¿ नहीं है" +access_denied_read_items = "आपको अपने दà¥à¤µà¤¾à¤°à¤¾ अनà¥à¤°à¥‹à¤§à¤¿à¤¤ अथवा चेकà¥à¤¡-आउट किठगठआइटम देखने की अनà¥à¤®à¤¤à¤¿ नहीं है" +access_denied_read_patron = "आपको अपनी उपयोगकरà¥à¤¤à¤¾ पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤² देखने की अनà¥à¤®à¤¤à¤¿ नहीं है." +access_denied_write_items = "आपको आइटम का अनà¥à¤°à¥‹à¤§ अथवा नवीनीकरण करने की अनà¥à¤®à¤¤à¤¿ नहीं है" diff --git a/languages/Exception/hr.ini b/languages/Exception/hr.ini new file mode 100644 index 0000000000000000000000000000000000000000..5a7b47602144bd9ffcc907160fbd80925d92426f --- /dev/null +++ b/languages/Exception/hr.ini @@ -0,0 +1,6 @@ +; - Translation courtesy of Milo Ivir <mail@milotype.de> +access_denied_change_password = "Nije ti dozvoljeno promijeniti tvoju lozinku." +access_denied_read_fines = "Nije ti dozvoljeno vidjeti tvoje zakasnine." +access_denied_read_items = "Nije ti dozvoljeno vidjeti tvoje zahtjeve i posudbe." +access_denied_read_patron = "Nije ti dozvoljeno vidjeti tvoj korisniÄki profil." +access_denied_write_items = "Nije ti dozvoljeno postaviti zahtjev ili produljiti posudbu." diff --git a/languages/Exception/it.ini b/languages/Exception/it.ini new file mode 100644 index 0000000000000000000000000000000000000000..572b9d415ad0d367043f2aea42534dd4b93c6c17 --- /dev/null +++ b/languages/Exception/it.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Non ti è permesso cambiare la password." +access_denied_read_fines = "Non ti è permesso visualizzare le tue sanzioni." +access_denied_read_items = "Non ti è permesso visualizzare gli oggetti richiesti o di cui è stato fatto il checkout." +access_denied_read_patron = "Non ti è permesso visualizzare il tuo profilo utente." +access_denied_write_items = "Non ti è permesso richiedere nuovi oggetti." diff --git a/languages/Exception/ja.ini b/languages/Exception/ja.ini new file mode 100644 index 0000000000000000000000000000000000000000..1917df54f5c91176d8c88d1f58df3dd534323e8d --- /dev/null +++ b/languages/Exception/ja.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "パスワードã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“。" +access_denied_read_fines = "罰金ã®é–²è¦§ã¯ã§ãã¾ã›ã‚“。" +access_denied_read_items = "リクエスト資料ã¾ãŸã¯è²¸å‡ºè³‡æ–™ã®é–²è¦§ã¯ã§ãã¾ã›ã‚“。" +access_denied_read_patron = "ユーザプãƒãƒ•ã‚£ãƒ¼ãƒ«ã®é–²è¦§ã¯ã§ãã¾ã›ã‚“。" +access_denied_write_items = "資料ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¾ãŸã¯è²¸å‡ºæ›´æ–°ã¯ã§ãã¾ã›ã‚“。" diff --git a/languages/Exception/nl-be.ini b/languages/Exception/nl-be.ini new file mode 100644 index 0000000000000000000000000000000000000000..320d76d0090933c603a084fdf75d4ffadf9a90b8 --- /dev/null +++ b/languages/Exception/nl-be.ini @@ -0,0 +1 @@ +@parent_ini = "Exception/nl.ini" diff --git a/languages/Exception/nl.ini b/languages/Exception/nl.ini new file mode 100644 index 0000000000000000000000000000000000000000..952ceef43410f6a706ad626e4e73f163b55d4fee --- /dev/null +++ b/languages/Exception/nl.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Je hebt geen toestemming om je paswoord te veranderen." +access_denied_read_fines = "Je hebt geen toestemming om je boetes te bekijken." +access_denied_read_items = "Je hebt geen toestemming om je aangevraagde of uitgeleende items te bekijken." +access_denied_read_patron = "Je hebt geen toestemming om je gebruikersprofiel te bekijken." +access_denied_write_items = "Je hebt geen toestemming om items aan te vragen of te hernieuwen." diff --git a/languages/Exception/pl.ini b/languages/Exception/pl.ini new file mode 100644 index 0000000000000000000000000000000000000000..9d910276deeaf11dd456de3419f2758c41d7de91 --- /dev/null +++ b/languages/Exception/pl.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Nie możesz zmienić swojego hasÅ‚a." +access_denied_read_fines = "Nie możesz przeglÄ…dać swoich opÅ‚at." +access_denied_read_items = "Nie możesz przeglÄ…dać swoich zamówieÅ„ oraz wypożyczonych książek." +access_denied_read_patron = "Nie możesz przeglÄ…dać swojego profilu czytelnika." +access_denied_write_items = "Nie możesz rezerwować ani prolongować książek." diff --git a/languages/Exception/pt-br.ini b/languages/Exception/pt-br.ini new file mode 100644 index 0000000000000000000000000000000000000000..35c4245957b9bbf3db8e3a96f9ad354932a8b086 --- /dev/null +++ b/languages/Exception/pt-br.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Você não tem permissão para alterar sua senha." +access_denied_read_fines = "Você não tem permissão para ver suas multas." +access_denied_read_items = "Você não tem permissão para visualizar seus itens solicitados ou emprestados." +access_denied_read_patron = "Você não tem permissão para visualizar seu perfil de usuário." +access_denied_write_items = "Você não tem permissão para solicitar ou renovar itens." diff --git a/languages/Exception/tr.ini b/languages/Exception/tr.ini new file mode 100644 index 0000000000000000000000000000000000000000..2d79f3aecf00e72f75c281e690e1f9fd461cf8e8 --- /dev/null +++ b/languages/Exception/tr.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Åžifrenizi deÄŸiÅŸtirmenize izin verilmiyor." +access_denied_read_fines = "Para cezalarınızı görmenize izin verilmiyor." +access_denied_read_items = "Ä°stediÄŸiniz veya ödünç aldığınız materyalleri görmenize izin verilmiyor." +access_denied_read_patron = "Kullanıcı profilinizi görmenize izin verilmiyor." +access_denied_write_items = "Ä°stediÄŸiniz veya kaynakları yenilemenize izin verilmiyor." diff --git a/languages/Exception/vi.ini b/languages/Exception/vi.ini new file mode 100644 index 0000000000000000000000000000000000000000..b6223e4d236d668ea2af41a71a4abc71487cd787 --- /dev/null +++ b/languages/Exception/vi.ini @@ -0,0 +1,5 @@ +access_denied_change_password = "Bạn không được phép thay đổi máºt khẩu của bạn." +access_denied_read_fines = "Bạn không được phép xem tiá»n phạt của bạn." +access_denied_read_items = "Bạn không được phép xem các mục yêu cầu hoặc kiểm tra của bạn." +access_denied_read_patron = "Bạn không được phép xem hồ sÆ¡ ngÆ°á»i dùng của bạn." +access_denied_write_items = "Bạn không được phép yêu cầu hoặc gia hạn sách." diff --git a/languages/HoldingStatus/hi.ini b/languages/HoldingStatus/hi.ini new file mode 100644 index 0000000000000000000000000000000000000000..494aecbab46bc23ea9e66724d9b8f64c6bbb36f9 --- /dev/null +++ b/languages/HoldingStatus/hi.ini @@ -0,0 +1,4 @@ +service_available_presentation = "केवल पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ में उपयोग करने हेतॠ" +service_loan = "उधार हेतॠ" +service_presentation = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ में उपयोग करने हेतà¥" +services_available_html = "उपलबà¥à¤§ है : %%list%%" diff --git a/languages/HoldingStatus/hr.ini b/languages/HoldingStatus/hr.ini new file mode 100644 index 0000000000000000000000000000000000000000..59dffefd80d01596c186da417c09a613cf6a0634 --- /dev/null +++ b/languages/HoldingStatus/hr.ini @@ -0,0 +1,5 @@ +; - Translation courtesy of Milo Ivir <mail@milotype.de> +service_available_presentation = "Upotreba samo u knjižnici" +service_loan = "Posudba" +service_presentation = "Upotreba u knjižnici" +services_available_html = "Dostupno za %%list%%" diff --git a/languages/ar.ini b/languages/ar.ini index 3f0b6d7507a92bb5f0696dea16bd80f68729cebc..46971a7a6926915083654971fac8b50a84c82a10 100644 --- a/languages/ar.ini +++ b/languages/ar.ini @@ -51,6 +51,7 @@ advSearchError_notFound = "لم يتم العثور على البØØ« الذي ajax_load_interrupted = "تمت مقاطعة التØميل" ajaxview_label_information = "معلومات" ajaxview_label_tools = "أدوات" +alert_email_address = "سيتم إرسال نتائج التنبيه المجدولة إلى عنوان البريد الإلكتروني" All = "الكل" All Fields = "كل الØقول" All Pages Loaded = "تم تØميل كل الصÙØات" @@ -60,6 +61,7 @@ alphabrowselink_html = "استعرض المداخل Øسب %%index%% ابتدا An error has occurred = "لقد Øدث خطأ" An error occurred during execution; please try again later. = "لقد Øدث خطأ أثناء التنÙيذ؛ يرجى إعادة المØاولة لاØقا." AND = "Ùˆ" +and = "Ùˆ" anonymous_tags = "وسوم مجهولة" APA Citation = "APA استشهاد" applied_filter = "Ù…Ø±Ø´Ø Ù…Ø·Ø¨Ù‘Ù‚" @@ -72,6 +74,8 @@ authentication_error_admin = "لا يمكننا تسجيل دخولك ÙÙŠ ال authentication_error_blank = "لا يمكن أن تكون معلومات تسجيل الدخول خالية." authentication_error_creation_blocked = "ليس لديك صلاØية إنشاء Øساب." authentication_error_denied = "الاعتمادات غير متطابقة! تم رÙض الوصول." +authentication_error_email_not_verified_html = "لم يتم التأكد من عنوان بريدك الإلكتروني بعد. يرجى التØقق من Ù…Ù†Ù‚Ø Ø§Ù„Ø¨Ø±ÙŠØ¯ المزعج الخاص بك بØثًا عن رسالة تأكيد الØساب. إذا لزم الأمر، يمكننا <a href="%%url%%">إعادة إرسال رسالة تأكيد البريد الإلكتروني</a>." +authentication_error_in_progress = "طلب الاستيثاق قيد المعالجة بالÙعل. يرجى المØاولة مرة أخرى لاØقًا إذا كنت بØاجة للبدء من جديد." authentication_error_invalid = "تسجيل دخول غير صØÙŠØ -- الرجاء إعادة المØاولة." authentication_error_loggedout = "لقد قمت بتسجيل الخروج." authentication_error_technical = "لا يمكننا تسجيل دخولك ÙÙŠ ال وقت الØالي.يرجى إعادة المØاولة لاØقاً." @@ -97,7 +101,6 @@ Backtrace = "تتبع خلÙÙŠ" Bag = "سلة" Balance = "رصيد" Barcode = "باركود" -basic_search_keep_filters = "اØتÙظ بمرشØاتي الØالية" Be the first to leave a comment = "كن أول من يترك تعليقا" Be the first to tag this record = "كن أول من يضع وسما على هذه التسجيلة" Bibliographic Details = "التÙاصيل البيبلوغراÙية" @@ -167,6 +170,7 @@ Call Number = "رقم الطلب" callnumber_abbrev = "الطلب #" Cannot find record = "لا يمكن العثور على التسجيلة" Cannot find similar records = "لا يمكن العثور على تسجيلات مشابهة" +cannot set = "Cannot set" Cassette = "كاسيت" cat_establish_account = "لإنشاء المل٠الشخصي Ù„Øسابك، يرجى إدخال المعلومات التالية:" cat_password_abbrev = "كلمة مرور الÙهرس" @@ -175,7 +179,12 @@ Catalog Login = "تسجيل دخول الÙهرس" Catalog Results = "نتائج الÙهرس" catalog_login_desc = "أدخل اعتمادات Ùهرس مكتبتك." CD = "قرص مضغوط" +Change Email Address = "تغيير عنوان البريد الإلكتروني" Change Password = "تغيير كلمة المرور" +change_email_disabled = "غير Ù…Ø³Ù…ÙˆØ Ù„Ùƒ بتغيير عنوان بريدك الإلكتروني ÙÙŠ الوقت الØالي" +change_email_verification_reminder = "سيؤدي إرسال هذا النموذج إلى إرسال بريد إلكتروني إلى العنوان الجديد؛ سيكون عليك النقر على الرابط الموجود ÙÙŠ البريد الإلكتروني قبل سريان التغيير." +change_notification_email_message = "تم تقديم طلب لتغيير عنوان البريد الإلكتروني ÙÙŠ %%library%%. إذا لم تقم بإرسال هذا الطلب، قم بتسجيل الدخول إلى %%url%% والتأكد من سلامة Øسابك. يرجى الاتصال بالدعم على %%email%% إذا كان لديك أسئلة أو استÙسارات" +change_notification_email_subject = "إخطار تغيير عنوان البريد الإلكتروني للØساب" channel_add_more = "أض٠المزيد من القنوات هكذا" channel_browse = "استعراض المزيد من التسجيلات" channel_expand = "اكتش٠القنوات ذات الصلة" @@ -184,6 +193,7 @@ channel_search = "عرض المواد كنتائج بØØ«" channel_searchbox_label = "البØØ« عن المزيد من القنوات:" Check Hold = "تØديد الØجز" Check Recall = "تØديد الاستدعاء" +check_profile = "التØقق من بيانات المستخدم." Checked Out = "معار" Checked Out Items = "مواد معارة" Checkedout = "معار" @@ -217,6 +227,7 @@ comment_error_load = "خطأ: لا يمكن إعادة رسم قائمة الت comment_error_save = "خطأ: لا يمكن ØÙظ التعليق" Comments = "التعليقات" Company/Entity = "الشركة/الكيان" +Conference Proceeding = "وقائع المؤتمر" Configuration = "التهيئة" confirm_delete = "هل أنت متأكد من انك تريد Øذ٠هذا؟" confirm_delete_brief = "Øذ٠المادة؟" @@ -297,6 +308,7 @@ Due Date = "تاريخ الاستØقاق" DVD = "قرص Ùيديو رقمي" eBook = "كتاب الكتروني" Edit = "تØرير" +edit = "تØرير" Edit Library Card = "Øرّر بطاقة المكتبة" Edit this Advanced Search = "تØرير هذا البØØ« المتقدم" edit_list = "تØرير القائمة" @@ -325,8 +337,14 @@ Email address is invalid = "عنوان البريد الالكتروني غير Email Record = "إرسال التسجيلة بالبريد الالكتروني" Email this = "أرسل هذا بالبريد الإلكتروني" Email this Search = "أرسل هذا البØØ« بالبريد الإلكتروني" +email_change_pending_html = "لديك تغيير بريد إلكتروني ÙÙŠ الانتظار ÙÙŠ %%pending%%. يرجى النقر على الرابط الموجود ÙÙŠ رسالة التأكيد التي تم إرسالها إلى هذا العنوان لإتمام التغيير. إذا لزم الأمر، يمكننا <a href="%%url%%">إعادة إرسال رسالة التأكيد إلى البريد الإلكتروني</a>." email_failure = "خطأ - لا يمكن إرسال الرسالة" email_link = "الرابط" +email_login_desc = "يرجى استخدام الرابط التالي لتسجيل الدخول. إذا لم تبدأ تسجيل الدخول، يمكنك تجاهل هذه الرسالة بأمان. يرجى ملاØظة أن الرابط ØµØ§Ù„Ø Ùقط Ù„Ùترة Ù…Øدودة ومع الجهاز الذي استخدمته لإدخال عنوان البريد الإلكتروني Ùقط." +email_login_link = "رابط تسجيل الدخول: <%%url%%>" +email_login_link_sent = "لقد قمنا بإرسال رابط تسجيل الدخول إلى عنوان بريدك الإلكتروني. قد يستغرق الأمر بضع دقائق Øتى يصل الرابط. إذا لم تتلقى الرابط ÙÙŠ وقت قريب، يرجى التØقق من Ù…Ù†Ù‚Ø Ø§Ù„Ø¨Ø±ÙŠØ¯ العشوائي الخاص بك." +email_login_requested = "تم طلب تسجيل الدخول بعنوان بريدك الإلكتروني ÙÙŠ %%title%%." +email_login_subject = "تسجيل الدخول إلى %%title%%" email_maximum_recipients_note = "على أقصى Øد %%max%% متلقين Ù…Ø³Ù…ÙˆØ Ø¨Ù‡Ù…." email_multiple_recipients_note = "يمكنك تØديد عدة متلقين Ù…Ùصولين بÙاصلات." email_selected = "تم تØديد البريد الالكتروني" @@ -336,6 +354,7 @@ email_subject = "الموضوع" email_success = "تم إرسال الرسالة" Empty = "Ùارغ" Empty Book Bag = "سلة كتب Ùارغة" +empty_search_disallowed = "غير Ù…Ø³Ù…ÙˆØ Ø¨Ø§Ø³ØªØ¹Ù„Ø§Ù… Ùارغ ÙÙŠ هد٠البØØ« الØالي" Enable Auto Config = "تÙعيل التهيئة التلقائية" End Page = "نهاية الصÙØØ©" Era = "الØقبة الزمنية" @@ -388,12 +407,13 @@ Favorites = "المÙضلة" Fee = "رسم" Feedback = "تغذية راجعة" feedback_email = "البريد الالكتروني" -feedback_login_required = "يجب تسجيل دخولك أولا" feedback_name = "الاسم" Field of activity = "Øقل النشاط" File Description = "وص٠الملÙ" Filter = "المرشØ" +Filter Collection = "ØªÙ†Ù‚ÙŠØ Ø§Ù„Ù…Ø¬Ù…ÙˆØ¹Ø©" filter_tags = "ØªØ±Ø´ÙŠØ Ø§Ù„ÙˆØ³ÙˆÙ…" +filter_toggle_entries = "%%عد%% منقØ" filter_wildcard = "أي" Find = "ابØØ«" Find More = "ابØØ« عن المزيد" @@ -429,6 +449,7 @@ Go to Standard View = "اذهب إلى العرض القياسي" go_to_list = "اذهب إلى القائمة" google_map_cluster = "تجمع" google_map_cluster_points = "نقاط التجمع" +Government Document = "وثيقة رسمية" Grid = "شبكة" Group = "مجموعة" group_AND = "كل المجموعات" @@ -455,6 +476,7 @@ history_results = "النتائج" history_save = "ØÙظ؟" history_save_link = "ØÙظ" history_saved_searches = "بØوثك المØÙوظة" +history_schedule = "جدول التنبيهات" history_search = "البØØ« عن" history_time = "وقت" hold_available = "Ù…ØªØ§Ø Ù„Ù„Ø§Ù„ØªÙ‚Ø§Ø·" @@ -601,6 +623,7 @@ Library Web Search = "بØØ« ويب المكتبة" library_card_edit_password_placeholder = "كلمة مرور جديدة" lightbox_error = "خطأ: لا يمكن تØميل المربع المنبثق" Limit To = "تØديد إلى" +Link to full results = "رابط إلى النتائج الكاملة" List = "قائمة" List Tags = "وسوم القائمة" list_access_denied = "ليس لديك صلاØية عرض هذه القائمة." @@ -663,9 +686,12 @@ New Item Feed = "رد المادة الجديدة" New Item Search = "بØØ« مادة جديدة" New Item Search Results = "نتائج بØØ« المادة الجديدة" New Items = "مواد جديدة" +New results found for search = "تم العثور على نتائج جديدة للبØØ«" New Title = "عنوان جديد" +new_email_success = "تم تغيير عنوان بريدك الإلكتروني بنجاØ" new_password = "كلمة مرور جديدة" new_password_success = "تم تغيير كلمة مرورك بنجاØ" +new_results_heading = "%%count%% نتيجة جديدة" new_user_welcome_subject = "Øسابك الجديد ÙÙŠ %%library%%" new_user_welcome_text = "مرØبًا بك ÙÙŠ %%library%%. تم ÙØªØ Øساب جديد لـ %%firstname%% %%lastname%%. اسم المستخدم الخاص بك هو %%username%%. يرجى تعيين كلمة مرور ÙÙŠ هذه الصÙØØ©: %%url%%" Newspaper = "صØÙŠÙØ©" @@ -681,6 +707,7 @@ No Preference = "لا يوجد تÙضيل" No reviews were found for this record = "لم يتم العثور على مراجعات لهذه التسجيلة" No Tags = "لا توجد وسوم" no_description = "الوص٠غير متاØ." +no_email_address = "عنوان البريد الإلكتروني Ù…Ùقود." no_items_selected = "لم يتم تØديد مواد" nohit_active_filters = "Ø£Øد مرشØات الواجهة أو أكثر تم تطبيقه على هذا البØØ« . إذا قمت بØذ٠المرشØات، Ùقد تستعيد المزيد من النتائج." nohit_change_tab = "لقد كنت تبØØ« ÙÙŠ تبويب "%%activeTab%%" . يمكنك العثور على شيء ما ÙÙŠ Ø£Øد التبويبات الأخرى :" @@ -732,6 +759,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "OAI خادم" Occupation = "الوظيÙØ©" +od_account_noaccess = "هذه البطاقة لا تملك ØÙ‚ الوصول إلى المØتوى ÙÙŠ Overdrive" +od_account_problem = "توجد مشكلة ÙÙŠ Øسابك. %%رسالة%%" +od_audiobook-mp3 = "MP3 كتاب صوتي بصيغة" +od_audiobook-overdrive = "الصوتي OverDrive Listem كتاب" +od_avail_avail = "متاØ:" +od_avail_holds = "الØجوزات:" +od_avail_total = "إجمالي النسخ:" +od_but_cancel_hold = "إلغاء هذا الØجز" +od_but_checkout = "الإعارة عبر Overdrive" +od_but_checkout_s = "إعارة" +od_but_gettitle = "تنزيل هذا المØتوى" +od_but_gettitle_s = "تنزيل" +od_but_hold = "وضع Øجز عبر Overdrive" +od_but_hold_s = "وضع Øجز" +od_but_return = "إعادة هذا العنوان" +od_cancel_hold = "إلغاء Øجز Overdrive" +od_checkout = "Overdrive إعارة" +od_code_connection_failed = "Ùشل الاتصال بـ Overdrive. إذا استمرت هذه المشكلة، يرجى الاتصال بمكتبتك." +od_code_contentnotavail = "هذا المØتوى غير Ù…ØªØ§Ø ÙÙŠ منطقتك." +od_code_login_for_avail = "تسجيل الدخول للإتاØØ©" +od_code_resource_not_found = "لم يتم العثور على العنوان" +od_content = "Overdrive Ù…Øتوى" +od_dl_formats = "تنسيقات التنزيل المدعومة" +od_docheckout_failure = "تعذرت إعارة هذا العنوان." +od_docheckout_success = "تمت إعارة هذا العنوان لك. وتنتهي الإعارة ÙÙŠ. %%expireDate%%" +od_early_return = "إعادة Overdrive المبكرة" +od_ebook-epub-adobe = "كتاب Adobe EPUB الإلكتروني" +od_ebook-epub-open = "ÙØªØ ÙƒØªØ§Ø¨ EPUB الإلكتروني" +od_ebook-kindle = "كتاب ÙƒÙندل" +od_ebook-mediado = "كتاب MediaDo Reader الإلكتروني" +od_ebook-overdrive = "كتاب OverDrive Read الإلكتروني" +od_ebook-pdf-adobe = "كتاب Adobe PDF الإلكتروني" +od_ebook-pdf-open = "ÙØªØ ÙƒØªØ§Ø¨ PDF الإلكتروني" +od_expires_on = "ينتهي هذا العنوان ÙÙŠ %%due_date%%." +od_get_title = "تنزيل Overdrive" +od_gettitle_failure = "تعذر تنزيل هذا العنوان." +od_help_linktext = "مساعدة Overdrive" +od_history = "سجل Overdrive" +od_hold = "Øجز Overdrive" +od_hold_cancel_failure = "Ùشل طلب إلغاء الØجز. " +od_hold_cancel_success = "تم إلغاء الØجز بنجاØ." +od_hold_email = "عنوان البريد الإلكتروني لإشعار الØجز: %%holdEmailAddress%%." +od_hold_now_avail = "هذا الØجز Ù…ØªØ§Ø Ù„Ù„Ø¥Ø¹Ø§Ø±Ø©. تنتهي الإعارة ÙÙŠ %%expireDate%%." +od_hold_place_failure = "Ùشل طلب الØجز." +od_hold_place_success = "تم وضع Øجز على هذا العنوان. موضع Øجزك هو %%holdListPosition%%" +od_hold_placed_on = "تم وضع الØجز على %%holdPlacedDate%%." +od_hold_queue = "موضع %%holdPosition%% من %%numberOfHolds%% ÙÙŠ ص٠الØجوزات." +od_holds = "Øجوزات Overdrive" +od_info_unavail = "هذه المعلومة غير متوÙرة Øاليًا" +od_is_checkedout = "لقد قمت باستعارة هذا العنوان. وهو مستØÙ‚ ÙÙŠ %%due_date%%." +od_is_on_hold = "لديك Øجز على هذا العنوان." +od_loans = "إعارات Overdrive" +od_mycontent_help = "للمزيد من المعلومات والمساعدة Øول تنزيل هذه العناوين، انظر <a href="%%url%%">مساعدة Overdrive</a>." +od_none_found = "لم يتم العثور على عناوين." +od_return_failure = "تعذرت إعادة هذا العنوان." +od_return_success = "تمت إعادة هذا العنوان." +od_video-streaming = "بث مل٠الÙيديو" of_num_results = "#%%position%% من %%total%% نتائج" old_password = "كلمة المرور القديمة" On Reserve = "ÙÙŠ الØجز" @@ -761,6 +845,7 @@ pagination_label = "ترقم الصÙØات" Password = "كلمة المرور" Password Again = "كلمة المرور مرة أخرى" Password cannot be blank = "لا يمكن أن تكون كلمة المرور خالية" +password_error_auth_old = "كلمة المرور التي تم استخدامها سابقًا غير صالØØ©" password_error_invalid = "كلمة المرور الجديدة غير صØÙŠØØ© (يبدو تØوي على Øرو٠غير مقبولة)" password_error_not_unique = "لم يتم تغيير كلمة المرور" password_maximum_length = "الØد الأقصى لطول كلمة المرور هو %%maxlength%% Ø£ØرÙ" @@ -826,6 +911,7 @@ Read the full review online... = "قراءة المراجعة الكاملة Ø£ Recall This = "إستدعي هذه النسخة" recaptcha_not_passed = "CAPTCHA لم تمر" recently_returned_channel_title = "تمت إعادته مؤخرًا" +recommend_links_text = "كما يمكنك أيضًا تجربة:" Record Citations = "استشهادات التسجيلة" Record Count = "عد التسجيلة" Record Type = "نوع التسجيلة" @@ -844,6 +930,7 @@ recovery_title = "استعادة كلمة المرور" recovery_too_soon = "تم إجراء طلبات استعادة أكثر من اللازم، يرجى إعادة المØاولة لاØقا" recovery_user_not_found = "لم نستطع العثور على Øسابك" rectangle_center_message = "هذه النقطة المركزية للمستطيل المظلل" +Reference Material = "مادة مرجعية" Refine Results = "ØªÙ†Ù‚ÙŠØ Ø§Ù„Ù†ØªØ§Ø¦Ø¬" Region = "المنطقة" relais_available = "هذه المادة متاØØ© عبر الإعارة بين المكتبات. هل ترغب ÙÙŠ طلبها؟ " @@ -884,6 +971,7 @@ Requests = "طلبات" Reserves = "الØجز الأكاديمي" Reserves Search = "Ù„ØØ« الØجز الأكاديمي" Reserves Search Results = "نتائج البØØ« ÙÙŠ الØجز الأكاديمي" +reset_filters_button = "إعادة تعيين المنقØات" result_checkbox_label = "تØديد النتيجة رقم %%number%%" result_count = "نتيجة %%count%%" Results = "نتائج" @@ -899,6 +987,11 @@ Save Comment = "ØÙظ التعليق" save_search = "ØÙظ البØØ«" save_search_remove = "Øذ٠البØØ« المØÙوظ" Saved in = "Ù…ØÙوظ ÙÙŠ" +schedule_daily = "يوميًا" +schedule_explanation = "تلقي تنبيهات عبر البريد الإلكتروني Øول النتائج الجديدة للبØØ«." +schedule_none = "لا شيء" +schedule_weekly = "أسبوعيًا" +Scheduled Alert Results = "نتائج التنبيهات المجدولة" scholarly_limit = "تØديد إلى المقالات من الدوريات العلمية" Scroll to Load More = "تمرير لتØميل المزيد" Search = "بØØ« عن" @@ -913,6 +1006,7 @@ search results of = "نتائج البجث لـ" Search Tips = "إرشادات Øول معاملات البØØ«" Search Tools = "أدوات البØØ«" Search Type = "نوع البØØ«" +Search within collection = "بØØ« ÙÙŠ المجموعة" search_AND = "كل المصطلØات" search_groups = "مجموعات البØØ«" search_match = "مطابقة" @@ -938,6 +1032,7 @@ Sensor Image = "صورة المجس" Serial = "دورية" Series = "سلاسل" Set = "ضبط" +show_filters_html = "عرض المنقØات (%%عد%%)" showing_items_html = "يعرض <strong>%%start%% - %%end%%</strong> مواد" showing_items_of_html = "يعرض <strong>%%start%% - %%end%%</strong> من <strong>%%total%%</strong> مواد" showing_results_for_html = "يعرض <strong>%%start%% - %%end%%</strong> نتائج نتيجة بØØ« عن '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "بدء بØØ« أساسي جديد" Start Page = "صÙØØ© البدء" starting from = "يبدأ من" Status = "الØالة" +status_transit = "ÙÙŠ النقل" status_unknown_message = "الØالة المباشرة غير متاØØ©" Storage Retrieval Requests = "طلبات استرجاع التخزين" storage_retrieval_request_available = "Ù…ØªØ§Ø Ù„Ù„Ø§Ù„ØªÙ‚Ø§Ø·" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "التسجيلة ا The record you selected is not part of the selected list. = "التسجيلة التي قمت بتØديدها ليست جزءا من القائمة المØددة." The system is currently unavailable due to system maintenance = "النظام غري Ù…ØªØ§Ø Øاليا نظرا لأعمال صيانة النظام" Theme = "ثيمة" +Thesis = "أطروØØ©" This email was sent from = "تم إرسال هذا البريد الالكتروني من" This field is required = "هذا الØقل مطلوب" This item is already part of the following list/lists = "هذه المادة بالÙعل جزء من القائمة /القوائم التالية" @@ -1090,6 +1187,9 @@ unique_tags = "وسوم متÙردة" University Library = "مكتبة الجامعة" Unknown = "غير معروÙ" unrecognized_facet_label = "أخرى" +unsubscribe_confirmation = "هل تريد إلغاء اشتراك البريد الإلكتروني؟" +unsubscribe_description = "لا تريد تلقي هذه الرسالة ÙÙŠ المستقبل؟ قم بإلغاء الاشتراك باستخدام الرابط التالي" +unsubscribe_successful = "تم إلغاء الاشتراك" Upgrade VuFind = "ترقية VuFind" upgrade_description = "إذا كنت تقوم بترقية إصدار VuFind سابق، يمكنك تØميل إعداداتك القديمة بهذه الأداة." URL = "URL" @@ -1099,6 +1199,14 @@ User Account = "Øساب المستخدم" Username = "اسم المستخدم" Username cannot be blank = "لا يمكن أن يكون اسم المستخدم خاليا" Username is already in use in another library card = "الاسم مستخدم ÙÙŠ بطاقة مكتبة أخرى" +verification_done = "تم تأكيد عنوان بريدك الإلكتروني بنجاØ." +verification_email_change_sent = "تم إرسال تعليمات تأكيد عنوان البريد الإلكتروني إلى عنوان البريد الإلكتروني الجديد. يجب تأكيد العنوان قبل سريان Ù…Ùعول التغيير." +verification_email_notification = "تم طلب تأكيد عنوان بريدك الإلكتروني Ù„Øسابك ÙÙŠ %%المكتبة%%." +verification_email_sent = "تم إرسال تعليمات تأكيد البريد الإلكتروني إلى عنوان البريد الإلكتروني المسجل ÙÙŠ هذا الØساب." +verification_email_subject = "تأكيد بريد ÙيوÙايند الإلكتروني" +verification_email_url_pretext = "يمكنك تأكيد عنوان بريدك الإلكتروني عبر هذا الURL: %%url%%" +verification_too_soon = "يجب تأكيد عنوان بريدك الإلكتروني. تم إرسال رسالة إلى عنوان بريدك الإلكتروني المسجل لدينا. إذا لم تتلقاها، يرجى الانتظار بضع دقائق ثم إعادة المØاولة مرة أخرى." +verification_user_not_found = "لم نتمكن من العثور على Øسابك" VHS = "VHS" Video = "الÙيديو" Video Clips = "مقاطع الÙيديو" diff --git a/languages/bn.ini b/languages/bn.ini index b3d578169e6d1ea59a66816050bc2b802f9faf3b..cf4c3a85085061723296424c07412203009c0aa0 100644 --- a/languages/bn.ini +++ b/languages/bn.ini @@ -52,6 +52,7 @@ advSearchError_notFound = "যে অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à¦Ÿà¦¿ আপনি ajax_load_interrupted = "পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¦à¦°à§à¦¶à¦¨ বাধাপà§à¦°à¦¾à¦ªà§à¦¤ হয়েছে" ajaxview_label_information = "তথà§à¦¯" ajaxview_label_tools = "সরঞà§à¦œà¦¾à¦®à¦¸à¦®à§‚হ" +alert_email_address = "নিরà§à¦§à¦¾à¦°à¦¿à¦¤ তথà§à¦¯ সংকেত ফলাফল ই-মেইল ঠিকানায় পà§à¦°à§‡à¦°à¦£ করা হবে ।" All = "সমসà§à¦¤" All Fields = "সমসà§à¦¤ কà§à¦·à§‡à¦¤à§à¦°à¦¸à¦®à§‚হ" All Pages Loaded = "সমসà§à¦¤ পেজ লোডেড" @@ -61,6 +62,7 @@ alphabrowselink_html = "সংলেখ বà§à¦°à¦¾à¦‰à¦œ করà§à¦¨ %%index An error has occurred = "à¦à¦•à¦Ÿà¦¿ তà§à¦°à§à¦Ÿà¦¿ ঘটেছে" An error occurred during execution; please try again later. = "চালানোর সময় à¦à¦•à¦Ÿà¦¿ তà§à¦°à§à¦Ÿà¦¿ ঘটেছে; অনà§à¦—à§à¦°à¦¹ করে à¦à¦•à¦Ÿà§ পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à¥¤" AND = "à¦à¦¬à¦‚" +and = "à¦à¦¬à¦‚" anonymous_tags = "অনামা টà§à¦¯à¦¾à¦— " APA Citation = "APA সাইটেশন" applied_filter = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦¿à¦• ফিলà§à¦Ÿà¦¾à¦°" @@ -73,6 +75,8 @@ authentication_error_admin = "à¦à¦‡ সময়ে আপনার লগ ই authentication_error_blank = "লগ ইন তথà§à¦¯ শূনà§à¦¯ হতে পারে না ।" authentication_error_creation_blocked = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ তৈরী করার অনà§à¦®à¦¤à¦¿ নেই ।" authentication_error_denied = "পরিচিতি মিলছে না!পরিষেবা অসà§à¦¬à§€à¦•à§ƒà¦¤à¥¤" +authentication_error_email_not_verified_html = "আপনার ইমেল ঠিকানা à¦à¦–নো যাচাই করা হয় নি। মেসেজটা যাচাই করার জনà§à¦¯ সà§à¦ªà§à¦¯à¦¾à¦® ফিলà§à¦Ÿà¦¾à¦° দেখà§à¦¨à¥¤ পà§à¦°à§Ÿà§‹à¦œà¦¨à§‡ আমরা <a href="%%url%%">পà§à¦¨à¦°à¦¾à§Ÿ যাচাই করার জনà§à¦¯ ইমেল পাঠান</a>." +authentication_error_in_progress = "পà§à¦°à¦®à¦¾à¦£à§€à¦•à¦°à¦£à§‡à¦° অনà§à¦°à§‹à¦§à¦Ÿà¦¿ ইতিমধà§à¦¯à§‡ পà§à¦°à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾à¦§à§€à¦¨ রয়েছে। আপনার যদি আবার শà§à¦°à§ করার পà§à¦°à¦¯à¦¼à§‹à¦œà¦¨ হয় তবে দয়া করে পরে চেষà§à¦Ÿà¦¾ করà§à¦¨ ।" authentication_error_invalid = "অবৈধ লগ ইন -- অনà§à¦—à§à¦°à¦¹ করে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à¥¤" authentication_error_loggedout = "আপনি লগ আউট করেছেন।" authentication_error_technical = "আমরা à¦à¦‡ সময়ে আপনার লগ ইন অণà§à¦®à§‡à¦¾à¦¦à¦¨ করছি না । অনà§à¦—à§à¦°à¦¹ করে à¦à¦•à¦Ÿà§ পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨ ।" @@ -98,7 +102,6 @@ Backtrace = "বà§à¦¯à¦¾à¦•-টà§à¦°à§‡à¦¸" Bag = "সমà§à¦à¦¾à¦°" Balance = "à¦à¦¾à¦°à¦¸à¦¾à¦®à§à¦¯" Barcode = "বারকোডেড অধিগà§à¦°à¦¹à¦£ সংখà§à¦¯à¦¾" -basic_search_keep_filters = "বরà§à¦¤à¦®à¦¾à¦¨ ফিলà§à¦Ÿà¦¾à¦° বজায় রাখà§à¦¨ " Be the first to leave a comment = "পà§à¦°à¦¥à¦®à¦œà¦¨ হিসাবে মনà§à¦¤à¦¬à§à¦¯ করà§à¦¨" Be the first to tag this record = "পà§à¦°à¦¥à¦®à¦œà¦¨ হিসাবে টà§à¦¯à¦¾à¦— করà§à¦¨" Bibliographic Details = "গà§à¦°à¦¨à§à¦¥-পঞà§à¦œà§€à¦° বিবরন" @@ -168,6 +171,7 @@ Call Number = "ডাক সংখà§à¦¯à¦¾" callnumber_abbrev = "ডাক" Cannot find record = "নথি খà§à¦à¦œà§‡ পাওয়া যাচà§à¦›à§‡ না" Cannot find similar records = "অনà§à¦°à§‚প নথি খà§à¦à¦œà§‡ পাওয়া যাচà§à¦›à§‡ না" +cannot set = "সেট করা গেলো না ।" Cassette = "কà§à¦¯à¦¾à¦¸à§‡à¦Ÿ" cat_establish_account = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ রূপরেখা পà§à¦°à¦¤à¦¿à¦·à§à¦ া করার জনà§à¦¯, নিমà§à¦¨à¦²à¦¿à¦–িত তথà§à¦¯ লিখà§à¦¨:" cat_password_abbrev = "গà§à¦°à¦¨à§à¦¥ তালিকা পাসওয়ারà§à¦¡" @@ -176,7 +180,12 @@ Catalog Login = "গà§à¦°à¦¨à§à¦¥ তালিকা লগ ইন" Catalog Results = "গà§à¦°à¦¨à§à¦¥ তালিকা অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° ফলাফল" catalog_login_desc = "আপনার গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার কà§à¦¯à¦¾à¦Ÿà¦¾à¦²à¦—ে পরিচয়পতà§à¦° লিখà§à¦¨" CD = "সি ডি" +Change Email Address = "ই-মেইল ​​ঠিকানা পরিবরà§à¦¤à¦¨ করà§à¦¨ ।" Change Password = "পাসওয়ারà§à¦¡ পরিবরà§à¦¤à¦¨ করà§à¦¨ " +change_email_disabled = "à¦à¦‡ মà§à¦¹à§à¦°à§à¦¤à§‡ আপনার ই-মেইল ঠিকানা পরিবরà§à¦¤à¦¨ করার অনà§à¦®à¦¤à¦¿ নেই ।" +change_email_verification_reminder = "à¦à¦‡ ফরà§à¦®à¦Ÿà¦¿ জমা দেওয়া হলে নতà§à¦¨ ঠিকানায় à¦à¦•à¦Ÿà¦¿ ই-মেল পà§à¦°à§‡à¦°à¦£ করবে; পরিবরà§à¦¤à¦¨à¦Ÿà¦¿ কারà§à¦¯à¦•à¦° হওয়ার আগে আপনাকে ই-মেলের à¦à¦•à¦Ÿà¦¿ লিঙà§à¦•à§‡ কà§à¦²à¦¿à¦• করতে হবে ।" +change_notification_email_message = "আপনার ই-মেল ঠিকানাটি à¦à¦–ানে পরিবরà§à¦¤à¦¨ করার জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ অনà§à¦°à§‹à¦§ করা হয়েছিল %%library%%. আপনি যদি à¦à¦‡ অনà§à¦°à§‹à¦§à¦Ÿà¦¿ শà§à¦°à§ না করেন তবে আপনি লগ ইন করà§à¦¨ %%url%% à¦à¦¬à¦‚ আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡à¦° অখণà§à¦¡à¦¤à¦¾ নিশà§à¦šà¦¿à¦¤ করà§à¦¨à¥¤ আপনার যদি পà§à¦°à¦¶à§à¦¨ বা উদà§à¦¬à§‡à¦— থাকে তবে দয়া করে %%email%% ঠযোগাযোগ করà§à¦¨à¥¤" +change_notification_email_subject = "অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ ই-মেল পরিবরà§à¦¤à¦¨ বিজà§à¦žà¦ªà§à¦¤à¦¿" channel_add_more = "à¦à¦‡ ধরণের আরো চà§à¦¯à¦¾à¦¨à§‡à¦² যোগ করà§à¦¨" channel_browse = "à¦à¦‡ চà§à¦¯à¦¾à¦¨à§‡à¦²à§‡à¦° অনà§à¦¤à¦°à§à¦—ত আরো পà§à¦°à¦²à§‡à¦– দেখà§à¦¨ " channel_expand = "সমধরà§à¦®à§€ চà§à¦¯à¦¾à¦¨à§‡à¦² অনà§à¦¬à§‡à¦·à¦£ করà§à¦¨" @@ -185,6 +194,7 @@ channel_search = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡ পà§à¦°à¦²à§‡à¦– পà§à¦°à¦¦ channel_searchbox_label = "আরো চà§à¦¯à¦¾à¦¨à§‡à¦²à§‡ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ করà§à¦¨:" Check Hold = "গà§à¦°à¦¨à§à¦¥ রিজারà§à¦ করার জনà§à¦¯ চেক করà§à¦¨" Check Recall = "গà§à¦°à¦¨à§à¦¥ তলব করà§à¦¨" +check_profile = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° তথà§à¦¯ পরীকà§à¦·à¦¾ করà§à¦¨" Checked Out = "গà§à¦°à¦¨à§à¦¥ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" Checked Out Items = "উপাদানগà§à¦²à¦¿ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" Checkedout = "গà§à¦°à¦¨à§à¦¥ পà§à¦°à¦¦à¦¾à¦¨ হয়েছে" @@ -218,6 +228,7 @@ comment_error_load = "তà§à¦°à§à¦Ÿà¦¿: মনà§à¦¤à¦¬à§à¦¯ তালিক comment_error_save = "তà§à¦°à§à¦Ÿà¦¿: মনà§à¦¤à¦¬à§à¦¯ সংরকà§à¦·à¦£ করা যাবেনা" Comments = "মনà§à¦¤à¦¬à§à¦¯ করà§à¦¨" Company/Entity = "কোমà§à¦ªà¦¾à¦¨à¦¿/সতà§à¦¤à¦¾" +Conference Proceeding = "কনফারেনà§à¦¸ পà§à¦°à¦¸à¦¿à¦¡à¦¿à¦‚" Configuration = "কনফিগারেশন করà§à¦¨" confirm_delete = "আপনি কি à¦à¦Ÿà¦¾ মà§à¦›à§‡ ফেলতে চান ?" confirm_delete_brief = "উপাদানটি মà§à¦›à§‡ ফেলতে চান?" @@ -298,6 +309,7 @@ Due Date = "তারিখ বাকি আছে" DVD = "ডিà¦à¦¿à¦¡à¦¿" eBook = "বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" Edit = "সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨" +edit = "সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨" Edit Library Card = "গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার কারà§à¦¡ সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨" Edit this Advanced Search = "বিসà§à¦¤à§ƒà¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à¦Ÿà¦¿ সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨" edit_list = "তালিকাটি সমà§à¦ªà¦¾à¦¦à¦¨ করà§à¦¨" @@ -326,8 +338,14 @@ Email address is invalid = "ই-মেইল ঠিকানাটির অস Email Record = "ই-মেইল নথি" Email this = "à¦à¦‡ ই-মেইলটি" Email this Search = "à¦à¦‡ ই-মেইলটি অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ করà§à¦¨" +email_change_pending_html = "আপনার ই-মেল পরিবরà§à¦¤à¦¨à¦Ÿà¦¿ %%pending%% মà§à¦²à¦¤à§à¦¬à¦¿ রয়েছে। পরিবরà§à¦¤à¦¨à¦Ÿà¦¿ সমà§à¦ªà§‚রà§à¦£ করতে দয়া করে à¦à¦‡ ঠিকানায় পà§à¦°à§‡à¦°à¦¿à¦¤ যাচাইকরণের ই-মেলের লিঙà§à¦•à¦Ÿà¦¿ কà§à¦²à¦¿à¦• করà§à¦¨à¥¤ পà§à¦°à¦¯à¦¼à§‹à¦œà¦¨à§‡ আমরা যাচাই ই-মেলটি <a href="%%url%%">পà§à¦¨à¦°à¦¾à¦¯à¦¼ পাঠাতে পারি </a>" email_failure = "তà§à¦°à§à¦Ÿà¦¿ - বারà§à¦¤à¦¾ পাঠানো যাবে না" email_link = "à¦à¦‡ লিঙà§à¦•à§‡" +email_login_desc = "লগ ইন করতে দয়া করে à¦à¦‡ লিঙà§à¦•à¦Ÿà¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨à¥¤ আপনি যদি লগ ইন শà§à¦°à§ না করেন তবে আপনি নিরাপদে à¦à¦‡ বারà§à¦¤à¦¾à¦Ÿà¦¿à¦•à§‡ উপেকà§à¦·à¦¾ করতে পারেন। দয়া করে খেয়াল রাখà§à¦¨ যে লিঙà§à¦•à¦Ÿà¦¿ সীমিত সময়ের জনà§à¦¯ বৈধ à¦à¦¬à¦‚ কারà§à¦¯à¦•à¦°à§€ কেবলমাতà§à¦° সেই ডিà¦à¦¾à¦‡à¦¸à¦Ÿà¦¿à¦° সাথে যা আপনি ই-মেল ঠিকানা পà§à¦°à¦¬à§‡à¦¶ করতে বà§à¦¯à¦¬à¦¹à¦¾à¦° করেছিলেন।" +email_login_link = "লগইন করার লিঙà§à¦•: <%%url%%>" +email_login_link_sent = "আমরা আপনার ই-মেল ঠিকানায় à¦à¦•à¦Ÿà¦¿ লগ ইন লিঙà§à¦• পà§à¦°à§‡à¦°à¦£ করেছি। লিঙà§à¦•à¦Ÿà¦¿ আসতে কয়েক মà§à¦¹à§à¦°à§à¦¤ লাগতে পারে। আপনি যদি শীঘà§à¦°à¦‡ লিঙà§à¦•à¦Ÿà¦¿ না পান তবে দয়া করে আপনার সà§à¦ªà§à¦¯à¦¾à¦® ফিলà§à¦Ÿà¦¾à¦°à¦Ÿà¦¿à¦“ দেখà§à¦¨à¥¤" +email_login_requested = "আপনার à¦à¦‡ ই-মেল ঠিকানার সাথে লগ ইন অনà§à¦°à§‹à¦§ করা হয়েছে %%title%%." +email_login_subject = "লগ ইন করà§à¦¨ %%title%%" email_maximum_recipients_note = "At most %%max%% recipients are allowed." email_multiple_recipients_note = "আপনি কমা বà§à¦¯à¦¬à¦¹à¦¾à¦° করে à¦à¦•à¦¾à¦§à¦¿à¦• পà§à¦°à¦¾à¦ªà¦•à¦¦à§‡à¦° নাম উলà§à¦²à§‡à¦– করতে পারেন।" email_selected = "নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ ই-মেইল" @@ -337,6 +355,7 @@ email_subject = "বিষয়" email_success = "বারà§à¦¤à¦¾ পাঠানো হয়েছে" Empty = "খালি" Empty Book Bag = "গà§à¦°à¦¨à§à¦¥à¦¸à¦®à§à¦à¦¾à¦°à¦Ÿà¦¿ খালি" +empty_search_disallowed = "বরà§à¦¤à¦®à¦¾à¦¨ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ লকà§à¦·à§à¦¯ à¦à¦•à¦Ÿà¦¿ পà§à¦°à¦¶à§à¦¨ শà§à¦¨à§à¦¯ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ অনà§à¦®à§‹à¦¦à¦¨ করে না " Enable Auto Config = "অটো কনফিগ সকà§à¦°à¦¿à¦¯à¦¼ করà§à¦¨" End Page = "পাতা শেষ" Era = "যà§à¦—" @@ -389,12 +408,13 @@ Favorites = "উপাদানটি সংরকà§à¦·à¦¨ করা হয়ে Fee = "খরচ" Feedback = "পà§à¦°à¦¤à¦¿à¦•à§à¦°à¦¿à¦¯à¦¼à¦¾ পাঠান" feedback_email = "ই-মেইল" -feedback_login_required = "আপনি আগে লগইন করà§à¦¨" feedback_name = "নাম" Field of activity = "বিষয় দকà§à¦·à¦¤à¦¾" File Description = "ফাইলের বিবরণ" Filter = "ফিলà§à¦Ÿà¦¾à¦°" +Filter Collection = "ফিলà§à¦Ÿà¦¾à¦° সংগà§à¦°à¦¹" filter_tags = "ফিলà§à¦Ÿà¦¾à¦° টà§à¦¯à¦¾à¦— করà§à¦¨" +filter_toggle_entries = "%%count%% ফিলà§à¦Ÿà¦¾à¦°" filter_wildcard = "কোনো" Find = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ " Find More = "আরও অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ " @@ -430,6 +450,7 @@ Go to Standard View = "পà§à¦°à¦®à¦¾à¦£à¦• দেখতে যান" go_to_list = "তালিকা দেখà§à¦¨" google_map_cluster = "গà§à¦šà§à¦›" google_map_cluster_points = "আলোচà§à¦¯ বিষয় গà§à¦šà§à¦›" +Government Document = "সরকারি নথিপতà§à¦°" Grid = "গà§à¦°à¦¿à¦¡" Group = "দল" group_AND = "সমসà§à¦¤ দল" @@ -456,6 +477,7 @@ history_results = "ফলাফল" history_save = "সংরকà§à¦·à¦¿à¦¤?" history_save_link = "সংরকà§à¦·à¦¿à¦¤" history_saved_searches = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à¦—à§à¦²à¦¿ সংরকà§à¦·à¦¿à¦¤" +history_schedule = "তথà§à¦¯à¦œà§à¦žà¦¾à¦ªà¦¨ সময়তালিকা" history_search = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" history_time = "সময়" hold_available = "Aপিকআপের জনà§à¦¯" @@ -602,6 +624,7 @@ Library Web Search = "গà§à¦°à¦¨à§à¦¥à¦¤à¦¾à¦²à¦¿à¦•à¦¾à¦°à¦—à§à¦°à¦¨à§à¦¥ library_card_edit_password_placeholder = "নতà§à¦¨ পাসওইয়ারà§à¦¡" lightbox_error = "তà§à¦°à§à¦Ÿà¦¿à¦ƒ পপআপ বকà§à¦¸ লোড করা যাবে না" Limit To = "পরিধির মধà§à¦¯à§‡" +Link to full results = "সমà§à¦ªà§‚রà§à¦£ ফলাফলের লিঙà§à¦•" List = "তালিকা" List Tags = "টà§à¦¯à¦¾à¦—ের তালিকা" list_access_denied = "আপনার à¦à¦‡ তালিকা দেখার অনà§à¦®à¦¤à¦¿ নেই।" @@ -664,9 +687,12 @@ New Item Feed = "নতà§à¦¨ উপাদানের ফিড" New Item Search = "নতà§à¦¨ উপাদান অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" New Item Search Results = "নতà§à¦¨ উপাদান অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° ফলাফল" New Items = "নতà§à¦¨ উপাদানগà§à¦²à¦¿" +New results found for search = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° জনà§à¦¯ নতà§à¦¨ ফলাফল পাওয়া গেছে" New Title = "নতà§à¦¨ আখà§à¦¯à¦¾" +new_email_success = "আপনার ইমেল ঠিকানা সফলà¦à¦¾à¦¬à§‡ পরিবরà§à¦¤à¦¨ করা হয়েছে" new_password = "নতà§à¦¨ পাসওইয়ারà§à¦¡" new_password_success = "আপনার পাসওয়ারà§à¦¡à¦Ÿà¦¿ সফলà¦à¦¾à¦¬à§‡ পরিবরà§à¦¤à¦¨ করা হয়েছে" +new_results_heading = "%%count%% নতà§à¦¨ ফলাফল" new_user_welcome_subject = "আপনার নতà§à¦¨ সদসà§à¦¯à¦ªà¦¦ (অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ) à¦à¦–ানে পাবেন %%library%%" new_user_welcome_text = "সà§à¦¬à¦¾à¦—ত জানাই %%library%%. à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ %%firstname%% %%lastname%% à¦à¦‡ নামে খোলা হলো। আপনার বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ পরিচয় হলো - %%username%%. অনà§à¦—à§à¦°à¦¹ করে পাসওয়ারà§à¦¡à¦Ÿà¦¿ ঠিক করà§à¦¨ : %%url%%" Newspaper = "সংবাদপতà§à¦°" @@ -682,6 +708,7 @@ No Preference = "কোন সà§à¦¬à¦¿à¦§à¦¾ পাওয়া যাবে ন No reviews were found for this record = "à¦à¦‡ নথির জনà§à¦¯ কোনো পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾ খà§à¦à¦œà§‡ পাওয়া যাচà§à¦›à§‡ না" No Tags = "কোনো টà§à¦¯à¦¾à¦— নেই" no_description = "বরà§à¦£à¦¨à¦¾ উপলবদà§à¦§ নয়।" +no_email_address = "ই-মেল ঠিকানা অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤à¥¤" no_items_selected = "কোন উপাদান নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ হয় নি" nohit_active_filters = "à¦à¦• বা à¦à¦•à¦¾à¦§à¦¿à¦• ফিলà§à¦Ÿà¦¾à¦° à¦à¦‡ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à¦Ÿà¦¿à¦° সঙà§à¦—ে যà§à¦•à§à¦¤ আছে। পà§à¦°à¦²à§‡à¦– সংখà§à¦¯à¦¾ বেশি পেতে ফিলà§à¦Ÿà¦¾à¦° অপসারণ করà§à¦¨à¥¤" nohit_change_tab = "আপনার অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à¦Ÿà¦¿ "%%activeTab%%" টà§à¦¯à¦¾à¦¬à§‡ সীমিত আছে। পà§à¦°à¦²à§‡à¦– সংখà§à¦¯à¦¾ বেশি পেতে অনà§à¦¯à¦¾à¦¨à§à¦¯ টà§à¦¯à¦¾à¦¬ বà§à¦¯à¦¬à¦¹à¦¾à¦° করà§à¦¨à¥¤" @@ -733,6 +760,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "ওà¦à¦†à¦‡ সারà§à¦à¦¾à¦°" Occupation = "পেশা" +od_account_noaccess = "à¦à¦‡ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার কারà§à¦¡ দিয়ে ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° বিষয়বসà§à¦¤à§ বà§à¦¯à¦¬à¦¹à¦¾à¦° করার অনà§à¦®à¦¤à¦¿ নেই।" +od_account_problem = "আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡ কোন সমসà§à¦¯à¦¾ আছে। %%message%%" +od_audiobook-mp3 = "MP3 অডিওবà§à¦•" +od_audiobook-overdrive = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡ অডিওবà§à¦• শà§à¦¨à§à¦¨" +od_avail_avail = "সহজলà¦à§à¦¯à¦ƒ" +od_avail_holds = "হোলà§à¦¡à¦ƒ" +od_avail_total = "মোট পà§à¦°à¦¤à¦¿à¦²à¦¿à¦ªà¦¿à¦—à§à¦²à¦¿ হলঃ" +od_but_cancel_hold = "হোলà§à¦¡ বাতিল করà§à¦¨" +od_but_checkout = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° মাধà§à¦¯à¦®à§‡ চেকআউট করà§à¦¨" +od_but_checkout_s = "চেকআউট" +od_but_gettitle = "বিষয়বসà§à¦¤à§ ডাউনলোড করà§à¦¨" +od_but_gettitle_s = "ডাউনলোড" +od_but_hold = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° মাধà§à¦¯à¦®à§‡ হোলà§à¦¡ করে রাখà§à¦¨" +od_but_hold_s = "হোলà§à¦¡ করে রাখà§à¦¨" +od_but_return = "শিরোনামটি ফিরে পেতে" +od_cancel_hold = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° হোলà§à¦¡ বাতিল করà§à¦¨" +od_checkout = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à¦Ÿà¦¿ চেকআউট করà§à¦¨" +od_code_connection_failed = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ à¦à¦° সাথে যোগাযোগ বিছিনà§à¦¨ হয়েছে। যদি সমসà§à¦¯à¦¾ à¦à¦–ন অবà§à¦¯à¦¾à¦¹à¦¤ থাকে, তাহলে অনà§à¦—à§à¦°à¦¹à¦•à¦°à§‡ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ারে যোগাযোগ করà§à¦¨à¥¤ " +od_code_contentnotavail = "à¦à¦‡ বিষয়বসà§à¦¤à§à¦Ÿà¦¿ আপনার à¦à¦²à¦¾à¦•à¦¾à§Ÿ অনà§à¦ªà¦²à§à¦¬à§à¦§" +od_code_login_for_avail = "উপসà§à¦¥à¦¿à¦¤à¦¿à¦° জনà§à¦¯ লগইন করà§à¦¨" +od_code_resource_not_found = "শিরোনামটি খà§à¦à¦œà§‡ পাওয়া যায় নি" +od_content = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° বিষয়বসà§à¦¤à§" +od_dl_formats = "অনà§à¦®à§‹à¦¦à¦¿à¦¤ ডাউনলোড বিনà§à¦¯à¦¾à¦¸à¦—à§à¦²à¦¿" +od_docheckout_failure = "à¦à¦‡ শিরোনামটির চেকআউট অসমà§à¦à¦¬à¥¤" +od_docheckout_success = "à¦à¦‡ শিরোনামটি আপনাকে চেকআউট করা হয়েছে। à¦à¦Ÿà¦¾à¦° মেয়াদ শেষ %%expireDate%%" +od_early_return = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ আগে ফেরত করà§à¦¨" +od_ebook-epub-adobe = "à¦à¦¡à¦¬ ইপাব বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" +od_ebook-epub-open = "উনà§à¦®à§à¦•à§à¦¤ ইপাব বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" +od_ebook-kindle = "কিনà§à¦¡à¦²à§‡ গà§à¦°à¦¨à§à¦¥" +od_ebook-mediado = "মিডিয়াদো রিডার বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" +od_ebook-overdrive = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥ পড়à§à¦¨" +od_ebook-pdf-adobe = "à¦à¦¡à¦¬ পিডিà¦à¦« বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" +od_ebook-pdf-open = "উনà§à¦®à§à¦•à§à¦¤ পিডিà¦à¦« বৈদà§à¦¯à§à¦¤à¦¿à¦¨ গà§à¦°à¦¨à§à¦¥" +od_expires_on = "à¦à¦‡ শিরোনামটির মেয়াদ শেষ %%due_date%%." +od_get_title = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ ডাউনলোড করà§à¦¨" +od_gettitle_failure = "à¦à¦‡ শিরোনামটি ডাউনলোড অসমà§à¦à¦¬à¥¤" +od_help_linktext = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° সাহাযà§à¦¯" +od_history = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° ইতিহাস" +od_hold = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ হোলà§à¦¡ করà§à¦¨" +od_hold_cancel_failure = "হোলà§à¦¡ বাতিলের অনà§à¦°à§‹à¦§à¦Ÿà¦¿ বà§à¦¯à¦°à§à¦¥à¥¤ " +od_hold_cancel_success = "হোলà§à¦¡ সফলà¦à¦¾à¦¬à§‡ বাতিল করা হয়েছে। " +od_hold_email = "হোলà§à¦¡ নোটিফিকেশন পেতে ইমেল ঠিকানা দিনঃ %%holdEmailAddress%%." +od_hold_now_avail = "চেকআউটের জনà§à¦¯ হোলà§à¦¡ উপলবà§à¦§à¥¤ চেকআউটের মেয়াদ শেষ %%expireDate%%." +od_hold_place_failure = "হোলà§à¦¡ রাখার অনà§à¦°à§‹à¦§à¦Ÿà¦¿ বà§à¦¯à¦°à§à¦¥à¥¤" +od_hold_place_success = "à¦à¦‡ শিরোনামটি হোলà§à¦¡ করা হয়েছে। আপনার হোলà§à¦¡à§‡à¦° অবসà§à¦¥à¦¾à¦¨ হল %%holdListPosition%%" +od_hold_placed_on = "হোলà§à¦¡ করা হয়েছে %%holdPlacedDate%%." +od_hold_queue = "অবসà§à¦¥à¦¾à¦¨ %%holdPosition%% à¦à¦° %%numberOfHolds%% হোলà§à¦¡à§‡à¦° কিউতে।" +od_holds = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ হোলà§à¦¡" +od_info_unavail = "à¦à¦‡ তথà§à¦¯à¦Ÿà¦¿ বরà§à¦¤à¦®à¦¾à¦¨à§‡ অনà§à¦ªà¦²à§à¦¬à§à¦§à¥¤ " +od_is_checkedout = "আপনি à¦à¦‡ শিরোনামটি চেকআউট করেছেন। ফেরতের তারিখ হল %%due_date%%." +od_is_on_hold = "আপনি à¦à¦‡ শিরোনামটি হোলà§à¦¡ করেছেন। " +od_loans = "ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦à§‡à¦° ঋণগà§à¦²à¦¿" +od_mycontent_help = "শিরোনাম ডাউনলোড সমà§à¦ªà¦°à§à¦•à§‡ তথà§à¦¯ à¦à¦¬à¦‚ সাহাযà§à¦¯, দেখà§à¦¨ <a href="%%url%%">ওà¦à¦¾à¦°à¦¡à§à¦°à¦¾à¦‡à¦ সাহাযà§à¦¯</a>." +od_none_found = "কোন শিরোনাম খà§à¦à¦œà§‡ পাওয়া যায় নি।" +od_return_failure = "à¦à¦‡ শিরোনামটি ফিরে আসবে না।" +od_return_success = "à¦à¦‡ শিরোনামটি ফিরে আসবে।" +od_video-streaming = "সà§à¦Ÿà§à¦°à¦¿à¦®à¦¿à¦‚ à¦à¦¿à¦¡à¦¿à¦“ ফাইল" of_num_results = "#%%position%% à¦à¦° %%total%% ফলাফল" old_password = "পà§à¦°à§‹à¦¨à§‹ পাসওয়ারà§à¦¡" On Reserve = "আরকà§à¦·à¦¿à¦¤" @@ -762,6 +846,7 @@ pagination_label = "পতà§à¦°à¦¾à¦™à§à¦•à¦¨" Password = "পাসওয়ারà§à¦¡" Password Again = "আবার পাসওয়ারà§à¦¡ দিন" Password cannot be blank = "পাসওয়ারà§à¦¡ শূনà§à¦¯ হতে পারে না" +password_error_auth_old = "পূরà§à¦¬à§‡ বà§à¦¯à¦¬à¦¹à§ƒà¦¤ পাসওয়ারà§à¦¡ অবৈধ" password_error_invalid = "নতà§à¦¨ পাসওয়ারà§à¦¡à¦Ÿà¦¿ অবৈধ (যেমন, অবৈধ অকà§à¦·à¦° রয়েছে)" password_error_not_unique = "পাসওয়ারà§à¦¡ পরিবরà§à¦¤à¦¨ করা হয়নি" password_maximum_length = "Maximum password length is %%maxlength%% characters" @@ -827,6 +912,7 @@ Read the full review online... = "পূরà§à¦£ পরà§à¦¯à¦¾à¦²à§‹à¦šà¦¨à¦¾ Recall This = "মনে করà§à¦¨" recaptcha_not_passed = "CAPTCHA অনà§à¦¤à§à¦¤à§€à¦°à§à¦£" recently_returned_channel_title = "সদà§à¦¯ ফেরত দেয়া পà§à¦°à¦²à§‡à¦–গà§à¦²à¦¿ " +recommend_links_text = "আপনি অতিরিকà§à¦¤ চেষà§à¦Ÿà¦¾ করতে পারেন:" Record Citations = "সাইটেশন নথি" Record Count = "নথির সংখà§à¦¯à¦¾" Record Type = "নথির পà§à¦°à¦•à¦¾à¦°" @@ -845,6 +931,7 @@ recovery_title = "পাসওয়ারà§à¦¡ পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° recovery_too_soon = "অনেকগà§à¦²à¦¿ পà§à¦¨à¦°à§à¦¦à§à¦§à¦¾à¦° অনà§à¦°à§‹à¦§ করা হয়েছে, দয়া করে পরে আবার চেষà§à¦Ÿà¦¾ করà§à¦¨" recovery_user_not_found = "আমরা আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ খà§à¦à¦œà§‡ পাইনি" rectangle_center_message = "আলোকিত আয়তকà§à¦·à§‡à¦¤à§à¦°à§‡à¦° মধà§à¦¯à¦¬à¦°à§à¦¤à§€ সà§à¦¥à¦¾à¦¨" +Reference Material = "রেফারেনà§à¦¸ উপাদান" Refine Results = "ফলাফল পরিমারà§à¦œà¦¨ করà§à¦¨" Region = "à¦à¦²à¦¾à¦•à¦¾ à¦à¦¿à¦¤à§à¦¤à¦¿à¦•" relais_available = "à¦à¦‡ পà§à¦°à¦²à§‡à¦–টি আনà§à¦¤à¦ƒà¦—à§à¦°à¦¨à§à¦¥à¦¾à¦—ার ঋণের মাধà§à¦¯à¦®à§‡ পাওয়া যেতে পারে। আপনি কি অনà§à¦°à§‹à¦§ জানাতে চান?" @@ -885,6 +972,7 @@ Requests = "অনà§à¦°à§‹à¦§" Reserves = "অরকà§à¦·à¦¿à¦¤" Reserves Search = "আরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" Reserves Search Results = "আরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ ফলাফল" +reset_filters_button = "ফিলà§à¦Ÿà¦¾à¦° পà§à¦¨à¦°à¦¾à¦¯à¦¼ সেট করà§à¦¨" result_checkbox_label = "পà§à¦°à¦¦à¦°à§à¦¶à¦¿à¦¤ পà§à¦°à¦²à§‡à¦– সংখà§à¦¯à¦¾ %%number%%" result_count = "%%count%% ফলাফল" Results = "ফলাফল" @@ -900,6 +988,11 @@ Save Comment = "মনà§à¦¤à¦¬à§à¦¯ সংরকà§à¦·à¦£ করà§à¦¨" save_search = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ সংরকà§à¦·à¦£ করà§à¦¨" save_search_remove = "সংরকà§à¦·à¦¿à¦¤ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ মà§à¦›à§à¦¨" Saved in = "সংরকà§à¦·à¦£ করà§à¦¨" +schedule_daily = "দৈননà§à¦¦à¦¿à¦¨" +schedule_explanation = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° জনà§à¦¯ নতà§à¦¨ ফলাফল সমà§à¦ªà¦°à§à¦•à¦¿à¦¤ ই-মেলগà§à¦²à¦¿ পান।" +schedule_none = "কোনটি নয় " +schedule_weekly = "সাপà§à¦¤à¦¾à¦¹à¦¿à¦•" +Scheduled Alert Results = "অনà§à¦°à§‹à¦§ করা সামà§à¦ªà§à¦°à¦¤à¦¿à¦• তথà§à¦¯à¦œà§à¦žà¦¾à¦ªà¦¨ ফলাফল " scholarly_limit = "পাণà§à¦¡à¦¿à¦¤à§à¦¯à¦ªà§‚রà§à¦£ পতà§à¦°à¦¿à¦•à¦¾ থেকে নিবনà§à¦§à¦—à§à¦²à¦¿ সীমিত" Scroll to Load More = "সà§à¦•à§à¦°à§‹à¦² করে আরো লোড করà§à¦¨" Search = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨" @@ -914,6 +1007,7 @@ search results of = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° ফলাফল" Search Tips = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ পরামরà§à¦¶à¦—à§à¦²à¦¿" Search Tools = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ সাধনীগà§à¦²à¦¿" Search Type = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ নমà§à¦¨à¦¾" +Search within collection = "সংগà§à¦°à¦¹à§‡à¦° মধà§à¦¯à§‡ অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ করà§à¦¨" search_AND = "সমসà§à¦¤ শরà§à¦¤à¦¾à¦¬à¦²à§€" search_groups = "অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨ দলগà§à¦²à¦¿" search_match = "সদৃশ" @@ -939,6 +1033,7 @@ Sensor Image = "সেনà§à¦¸à¦° চিতà§à¦°" Serial = "সাময়িক পতà§à¦°" Series = "মালা" Set = "সেট" +show_filters_html = "ফিলà§à¦Ÿà¦¾à¦° দেখà§à¦¨ (%%count%%)" showing_items_html = "পà§à¦°à¦¦à¦°à§à¦¶à¦¨ <strong>%%start%% - %%end%%</strong> উপাদানগà§à¦²à¦¿" showing_items_of_html = "পà§à¦°à¦¦à¦°à§à¦¶à¦¨ <strong>%%start%% - %%end%%</strong> à¦à¦° <strong>%%total%%</strong> উপাদানগà§à¦²à¦¿" showing_results_for_html = "পà§à¦°à¦¦à¦°à§à¦¶à¦¨ <strong>%%start%% - %%end%%</strong> ফলাফল অনà§à¦¸à¦¨à§à¦§à¦¾à¦¨à§‡à¦° জনà§à¦¯ '<strong>%%lookfor%%</strong>'" @@ -986,6 +1081,7 @@ Start a new Basic Search = "à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ মৌলিক অনৠStart Page = "পৃষà§à¦ া আরমà§à¦" starting from = "à¦à¦–ান থেকে শà§à¦°à§ করà§à¦¨" Status = "বরà§à¦¤à¦®à¦¾à¦¨ অবসà§à¦¥à¦¾" +status_transit = "পà§à¦°à¦•à§à¦°à¦¿à§Ÿà¦¾à¦§à§€à¦¨ " status_unknown_message = "বরà§à¦¤à¦®à¦¾à¦¨ অবসà§à¦¥à¦¾ অনà§à¦ªà¦²à¦¬à§à¦§" Storage Retrieval Requests = "সঞà§à¦šà¦¿à¦¤ তথà§à¦¯à§‹à¦¦à§à¦§à¦¾à¦°à§‡à¦° অণà§à¦°à§‹à¦§ করà§à¦¨" storage_retrieval_request_available = "পিকআপের জনà§à¦¯ উপলবà§à¦§ আছে" @@ -1061,6 +1157,7 @@ The record you selected is not part of any of your lists. = "আপনার ন The record you selected is not part of the selected list. = "আপনার নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ নথিটি নিরà§à¦¬à¦¾à¦šà¦¿à¦¤ তালিকার কোন অংশ নয়।" The system is currently unavailable due to system maintenance = "পদà§à¦§à¦¤à¦¿à¦Ÿà¦¿ রকà§à¦·à¦£à¦¾à¦¬à§‡à¦•à§à¦·à¦£à§‡à¦° কারণে বরà§à¦¤à¦®à¦¾à¦¨à§‡ অনà§à¦ªà¦²à¦¬à§à¦§" Theme = "থিম" +Thesis = "গবেষণাপতà§à¦°" This email was sent from = "à¦à¦‡ ই-মেইল থেকে পাঠানো হয়েছে" This field is required = "ঘরটি অবশà§à¦¯à¦‡ পূরণ করতে হবে" This item is already part of the following list/lists = "à¦à¦‡ উপাদানটি ইতিমধà§à¦¯à§‡ নিমà§à¦¨à¦²à¦¿à¦–িত তালিকার অংশ" @@ -1091,6 +1188,9 @@ unique_tags = "সà§à¦¬à¦¤à¦¨à§à¦¤à§à¦° টà§à¦¯à¦¾à¦— " University Library = "বিশà§à¦¬à¦¬à¦¿à¦¦à§à¦¯à¦¾à¦²à§Ÿ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার" Unknown = "অজà§à¦žà¦¾à¦¤" unrecognized_facet_label = "অনà§à¦¯à¦¾à¦¨à§à¦¯" +unsubscribe_confirmation = "আপনি কি ই-মেল সাবসà§à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ বাতিল করতে চান?" +unsubscribe_description = "à¦à¦¬à¦¿à¦·à§à¦¯à¦¤à§‡ à¦à¦‡ বারà§à¦¤à¦¾à¦Ÿà¦¿ পেতে চান না? নিমà§à¦¨à¦²à¦¿à¦–িত লিঙà§à¦•à¦Ÿà¦¿ বà§à¦¯à¦¬à¦¹à¦¾à¦° করে সাবসà§à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ বাতিল করà§à¦¨" +unsubscribe_successful = "সাবসà§à¦•à§à¦°à¦¿à¦ªà¦¶à¦¨ বাতিল হয়েছে" Upgrade VuFind = "VuFind আপগà§à¦°à§‡à¦¡ করà§à¦¨" upgrade_description = "যদি আপনি à¦à¦•à¦Ÿà¦¿ পূরà§à¦¬à¦¬à¦°à§à¦¤à§€ VuFind সংসà§à¦•à¦°à¦£ আপগà§à¦°à§‡à¦¡ করতে চান, আপনি à¦à¦‡ টà§à¦² দিয়ে আপনার পà§à¦°à¦¾à¦¨à§‹ সেটিংস লোড করতে পারেন" URL = "ইউআরà¦à¦²" @@ -1100,6 +1200,14 @@ User Account = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§ Username = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° নাম" Username cannot be blank = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° নাম শূনà§à¦¯ রাখা যাবে না" Username is already in use in another library card = "বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦° নাম অনà§à¦¯ গà§à¦°à¦¨à§à¦¥à¦¾à¦—ার কারà§à¦¡à§‡ আগে থেকেই আছে" +verification_done = "আপনার ইমেল ঠিকানা সফলà¦à¦¾à¦¬à§‡ যাচাই করা হয়েছে।" +verification_email_change_sent = "ই-মেল ঠিকানা যাচাইকরণ নিরà§à¦¦à§‡à¦¶à¦¾à¦¬à¦²à§€ নতà§à¦¨ ই-মেল ঠিকানায় পà§à¦°à§‡à¦°à¦£ করা হয়েছে। পরিবরà§à¦¤à¦¨à¦Ÿà¦¿ কারà§à¦¯à¦•à¦° হওয়ার আগে আপনাকে অবশà§à¦¯à¦‡ ই-মেল ঠিকানাটি যাচাই করতে হবে।" +verification_email_notification = "আপনার আকাউনà§à¦Ÿà§‡à¦° ইমেল ঠিকানা যাচাই করার জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ অনà§à¦°à§‹à¦§ করা হয়েছিল %%library%%." +verification_email_sent = "ইমেল ঠিকানা যাচাইকরণের নিরà§à¦¦à§‡à¦¶à¦¾à¦¬à¦²à§€, নিবনà§à¦§à¦¿à¦¤ অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿà§‡à¦° ইমেল ঠিকানাতে পাঠানো হয়েছে।" +verification_email_subject = "à¦à¦¿à¦‰à¦«à¦¾à¦‡à¦¨à§à¦¡ ইমেল যাচাই" +verification_email_url_pretext = "আপনি à¦à¦‡ ইউআরà¦à¦²à¦à¦° মাধà§à¦¯à¦®à§‡ আপনার ইমেল ঠিকানা যাচাই করতে পারেনঃ %%url%%" +verification_too_soon = "আপনার ইমেলের বৈধতা পà§à¦°à¦¯à¦¼à§‹à¦œà¦¨à¥¤ à¦à¦•à¦Ÿà¦¿ ইমেইল সমà§à¦ªà§à¦°à¦¤à¦¿ আপনার নিবনà§à¦§à¦¿à¦¤ ইমেইল ঠিকানাতে পাঠানো হয়েছে। যদি আপনি না পান, দয়া করে কয়েক মিনিট অপেকà§à¦·à¦¾ করà§à¦¨ à¦à¦¬à¦‚ আবার চেষà§à¦Ÿà¦¾ করà§à¦¨à¥¤" +verification_user_not_found = "আমরা আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ খà§à¦à¦œà§‡ পাচà§à¦›à¦¿ না" VHS = "à¦à¦¿à¦à¦‡à¦šà¦à¦¸" Video = "à¦à¦¿à¦¡à¦¿à¦“" Video Clips = "à¦à¦¿à¦¡à¦¿à¦“ কà§à¦²à¦¿à¦ªà¦¸" diff --git a/languages/ca.ini b/languages/ca.ini index a9051fb992a4fc0dae67daedd648fd67cfecfdb6..de6a7104f61aa0816d52b15a98b9ac90c2e93de9 100644 --- a/languages/ca.ini +++ b/languages/ca.ini @@ -62,6 +62,7 @@ advSearchError_notFound = "La cerca sol·licitada no s’ha trobat." ajax_load_interrupted = "Carrega no finalitzada" ajaxview_label_information = "Informació" ajaxview_label_tools = "Utilitats" +alert_email_address = "Els resultats de la cerca s'enviaran periódicament a l'adreça especÃfica" All = "All" All Fields = "Tots els camps" All Pages Loaded = "S'han carregat totes les pà gines" @@ -71,6 +72,7 @@ alphabrowselink_html = "Revisar registres per %%index%% desde fins <a href="%%ur An error has occurred = "S'ha produït un error" An error occurred during execution; please try again later. = "S'ha produït un error durant el procés; si us plau espera una estona i torna a provar-ho." AND = "i" +and = "i" anonymous_tags = "Etiqueta anònima" APA Citation = "Cita APA" applied_filter = "Filtre aplicat" @@ -83,6 +85,8 @@ authentication_error_admin = "No podem iniciar la vostra sessió en aquests mome authentication_error_blank = "La informació de registre no pot estar en blanc." authentication_error_creation_blocked = "No està s autoritzat per crear un usuari." authentication_error_denied = "Les credencials no coincideixen! Accés denegat." +authentication_error_email_not_verified_html = "La vostra adreça de correu electrònic encara no s'ha verificat. Comproveu el filtre de correu brossa per obtenir el missatge de verificació. Si és necessari, podem <a href="%%url%%">tornar a enviar el correu electrònic de verificació</a>." +authentication_error_in_progress = "La petició d' inici de sessió ja està en procés. Si us plau proveu més tard" authentication_error_invalid = "Inici de sessió no và lid -- torneu-ho a intentar." authentication_error_loggedout = "Ha sortir del seu compte." authentication_error_technical = "No podem iniciar la vostra sessió en aquests moments. Torneu-ho a provar més tard." @@ -108,7 +112,6 @@ Backtrace = "Retrocedir" Bag = "Bossa" Balance = "Balanç" Barcode = "Codi de barres" -basic_search_keep_filters = "Conservar els meus filtres actuals" Be the first to leave a comment = "Sigues el primer a deixar un comentari" Be the first to tag this record = "Sigues el primer a etiquetar aquest registre" Bibliographic Details = "Dades bibliogrà fiques" @@ -178,6 +181,7 @@ Call Number = "Topogrà fic" callnumber_abbrev = "Topogrà fic #" Cannot find record = "No es pot trobar el registre" Cannot find similar records = "No es poden trobar registres similars" +cannot set = "No es pot establir" Cassette = "Cassette" cat_establish_account = "Per tal d’establir el vostre perfil, introduïu la següent informació:" cat_password_abbrev = "Contrasenya del catà leg" @@ -186,7 +190,12 @@ Catalog Login = "El meu compte" Catalog Results = "Resultats al catà leg" catalog_login_desc = "Enter your library catalog credentials." CD = "CD" +Change Email Address = "Canvia l'adreça de correu electrònic" Change Password = "Canviar contrasenya" +change_email_disabled = "En aquest moment no teniu permÃs de canviar la vostra adreça de correu electrònic" +change_email_verification_reminder = "Al enviar aquest formulari enviarà s un correu electrònic a la nova adreça; haurà s de fer clic a l' enllaç per a que el canvi tingui efecte." +change_notification_email_message = "Hem rebut una sol·licitud per canviar la vostra adreça de correu electrònic a %%library%%. Si heu iniciat aquesta sol·licitud, recomanem que inicieu sessió a %%url%% i confirmeu la integritat del vostre compte. Podeu demanar ajuda al correu electrònic %%email%% si tens dubtes." +change_notification_email_subject = "Notificació de canvi de correu electrònic." channel_add_more = "Afegeix més canals com aquest" channel_browse = "Veure més registres" channel_expand = "Explora canals relacionats" @@ -195,6 +204,7 @@ channel_search = "Mostrar elements com a resultats de cerca" channel_searchbox_label = "Cercar a més canals:" Check Hold = "Comprovar fons" Check Recall = "Comprovar recordatoris" +check_profile = "Comproveu la informació de l'usuari." Checked Out = "Retirat" Checked Out Items = "Exemplars retirats" Checkedout = "Retirat" @@ -228,6 +238,7 @@ comment_error_load = "Error: No s’ha pogut recuperar la llista de comentaris" comment_error_save = "Error: No s’ha pogut guardar el comentari" Comments = "Comentaris" Company/Entity = "Societat/Institutció" +Conference Proceeding = "Actes de congresos" Configuration = "Configuració" confirm_delete = "Esteu segurs que el voleu eliminar?" confirm_delete_brief = "Borrar exemplar?" @@ -308,6 +319,7 @@ Due Date = "Data de venciment" DVD = "DVD" eBook = "eBook" Edit = "Editar" +edit = "editar" Edit Library Card = "Editar el carnet de la biblioteca" Edit this Advanced Search = "Editar aquesta cerca avançada" edit_list = "Editar llista" @@ -336,8 +348,14 @@ Email address is invalid = "L’adreça de correu electrònic no és và lida" Email Record = "Registre de correu electrònic" Email this = "Enviar per correu electrònic aquest" Email this Search = "Enviar per correu electrònic aquesta cerca" +email_change_pending_html = "Teniu un canvi de correu electrònic pendent a %%pending%%. Feu clic a l'enllaç del correu electrònic de verificació enviat a aquesta adreça per completar el canvi. Si és necessari, podem <a href="%%url%%"> Reenvia el correu electrònic de verificació </a>." email_failure = "Error - El missatge no es pot enviar" email_link = "Enllaç" +email_login_desc = "Si us plau, utilitzeu el següent enllaç per iniciar la sessió. Si no heu iniciat l'inici de sessió, pot ignorar aquest missatge amb seguretat. Tingueu en compte que l'enllaç només és và lid per a un temps limitat i només amb el dispositiu que heu utilitzat per introduir l'adreça de correu electrònic."." +email_login_link = "Enllaç per iniciar la sessió: <%%url%%>" +email_login_link_sent = "Hem enviat un enllaç d'inici de sessió a la vostra adreça de correu electrònic. Pot arribar en breu a la bústia. Si no rebeu l'enllaç en breu, comproveu també el filtre de correu brossa." +email_login_requested = "S’ha sol·licitat l’inici de sessió amb l’adreça de correu electrònic a %%title%%." +email_login_subject = "Iniciar sessió to %%title%%" email_maximum_recipients_note = "Com a mà xim %%max%% receptors autoritzats." email_multiple_recipients_note = "Pots especificar diferents destinataris separant amb una coma cadasqun." email_selected = "Enviar favorits seleccionats per correu electrònic" @@ -347,6 +365,7 @@ email_subject = "Matèria" email_success = "MIssatge enviat" Empty = "Buit" Empty Book Bag = "Buidar book bag" +empty_search_disallowed = "No es permet una consulta buida" Enable Auto Config = "Activar auto configuració" End Page = "Final de pà gina" Era = "Era" @@ -399,12 +418,13 @@ Favorites = "Favorits" Fee = "Quota" Feedback = "Feedback" feedback_email = "Correu electrònic" -feedback_login_required = "Abans heu d’iniciar sessió." feedback_name = "Nom" Field of activity = "Camp d'activitat" File Description = "Descripció del fitxer" Filter = "Filtre" +Filter Collection = "Filtre de col·lecció" filter_tags = "Filtre d'etiquetes" +filter_toggle_entries = "%%count%% filtres" filter_wildcard = "Qualsevol" Find = "Trobar" Find More = "Trobar-ne més" @@ -440,6 +460,7 @@ Go to Standard View = "Anar a la visualització està ndard" go_to_list = "Anar a llista" google_map_cluster = "Cluster" google_map_cluster_points = "Cluster Points" +Government Document = "Document de govern" Grid = "QuadrÃcula" Group = "Grup" group_AND = "TOTS els grups" @@ -466,6 +487,7 @@ history_results = "Resultats" history_save = "Guardar?" history_save_link = "Guardar" history_saved_searches = "Cerques guardades" +history_schedule = "Programació d'alertes" history_search = "Cerca" history_time = "Hora" hold_available = "Disponible per recollir" @@ -612,6 +634,7 @@ Library Web Search = "Cerca al web de la Biblioteca" library_card_edit_password_placeholder = "Nova contrasenya" lightbox_error = "Error: No es pot carregar caixa emergent" Limit To = "Limitar a" +Link to full results = "Enllaç als resultats" List = "Llistat" List Tags = "Llistar etiquetes" list_access_denied = "No teniu permisos per visualitzar aquesta llista." @@ -674,9 +697,12 @@ New Item Feed = "Alerta nous Ãtems" New Item Search = "Cerca nous Ãtems" New Item Search Results = "Nous Ãtems resultants de la cerca" New Items = "Nous Ãtems" +New results found for search = "S'han trobat nous resultats" New Title = "Nou tÃtol" +new_email_success = "La vostra adreça de correu electrònic ha canviat correctament" new_password = "Nova contrasenya" new_password_success = "Ha canviat la seva contrasenya" +new_results_heading = "%%count%% resultats més nous" new_user_welcome_subject = "El seu nou compte a %%library%%" new_user_welcome_text = "Benvingut a %%library%%. Un nou compte s'ha creat %%firstname%% %%lastname%%. El seu usuari %%username%%. Si us plau introdueixi la contrasenya: %%url%%" Newspaper = "Diari" @@ -692,6 +718,7 @@ No Preference = "Sense preferències" No reviews were found for this record = "No s’han trobat ressenyes per aquest registre" No Tags = "Sense etiquetes" no_description = "Descripció no disponible." +no_email_address = "Falta l'adreça de correu electrònic." no_items_selected = "No hi ha Ãtems seleccionats" nohit_active_filters = "Una o más facetas s’han aplicar a apuesta cerca. Si elimina algun filtre la cerca tornarà más resultaste." nohit_change_tab = "Has estar cercana a "%%activeTab%%". JaurÃa de cerca en altera pestañees per obtener más resultaste:" @@ -743,6 +770,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "Servidor OAI" Occupation = "Ocupació" +od_account_noaccess = "Aquesta usuari no té accés al contingut de Overdrive" +od_account_problem = "Hi ha un problema amb el vostre compte. %%message%%" +od_audiobook-mp3 = "MP3 audiobook" +od_audiobook-overdrive = "Escolar audiollibres OverDrive " +od_avail_avail = "Disponible:" +od_avail_holds = "Reserves:" +od_avail_total = "Total de còpies:" +od_but_cancel_hold = "Cancel·lar la reserva" +od_but_checkout = "Prestar en Overdrive" +od_but_checkout_s = "Prestat" +od_but_gettitle = "Baixa aquest contingut" +od_but_gettitle_s = "Descarregar" +od_but_hold = "Fer una reserva a Overdrive" +od_but_hold_s = "Fer una reserva" +od_but_return = "Tornar aquest tÃtol" +od_cancel_hold = "Cancel·lar la reserva a Overdrive" +od_checkout = "Préstec a overdrive" +od_code_connection_failed = "La connexió a Overdrive ha fallat. Si el problema continua, poseu-vos en contacte amb la vostra biblioteca." +od_code_contentnotavail = "Aquest contingut no està disponible a la vostra zona." +od_code_login_for_avail = "Iniciar sessió per a disponibilitat" +od_code_resource_not_found = "No s'ha trobat el tÃtol" +od_content = "Contingut Overdrive" +od_dl_formats = "Formats de descà rrega compatibles" +od_docheckout_failure = "Aquest tÃtol està exclòs de préstec." +od_docheckout_success = "Aquest tÃtol els tens en préstec. Caduca el %%expireDate%%" +od_early_return = "Overdrive Early Return" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "Aquest tÃtol caduca el %%due_date%%." +od_get_title = "Descà rrega Overdrive" +od_gettitle_failure = "Aquest tÃtol no es pot descarregar." +od_help_linktext = "Ajuda Overdrive" +od_history = "Histora Overdrive" +od_hold = "Reserva Overdrive" +od_hold_cancel_failure = "Ha fallat la sol·licitud de cancel·lació. " +od_hold_cancel_success = "La reserva s'ha cancel·lat correctament." +od_hold_email = "Adreça de correu electrònic per a la notificació: %%holdEmailAddress%%." +od_hold_now_avail = "Aquesta reserva està llesta per a ser recollida. El préstec venç %%expireDate%%." +od_hold_place_failure = "La sol·licitud de reserva ha fallat." +od_hold_place_success = "Aquest tÃtol ha estat posat en reserva. La vostre torn de reserva es el %%holdListPosition%%" +od_hold_placed_on = "Reserva feta el %%holdPlacedDate%%." +od_hold_queue = "Posició %%holdPosition%% de %%numberOfHolds%% en torn de reserva." +od_holds = "Reserves Overdrive" +od_info_unavail = "Aquesta informació no està disponible actualment." +od_is_checkedout = "Tens aquest llibre en préstec. Venciment el %%due_date%%." +od_is_on_hold = "Tens reservat aquest llibre." +od_loans = "Overdrive Loans" +od_mycontent_help = "FPer obtenir informació sobre i descarregar els tÃtols, vegeu <a href="%%url%%">Overdrive Help</a>." +od_none_found = "No s'ha trobat cap tÃtol." +od_return_failure = "Aquest tÃtol no es pot tornar." +od_return_success = "Aquest tÃtol ha estat retornat." +od_video-streaming = "streaming fitxers video" of_num_results = "#%%position%% de %%total%% resultats" old_password = "Constrasenya antiga" On Reserve = "Reservat" @@ -772,6 +856,7 @@ pagination_label = "Paginació" Password = "Contrasenya" Password Again = "Contrasenya un altre cop" Password cannot be blank = "La contrasenya no pot estar en blanc" +password_error_auth_old = "La contrasenya utilitzada anteriorment no és và lida" password_error_invalid = "Nova contrassenya no và lida (ex. conté caracters no và lids)" password_error_not_unique = "La constrasenya no s'ha canviat" password_maximum_length = "Longitut mà xima de la contrasenya %%maxlength%% caracters" @@ -837,6 +922,7 @@ Read the full review online... = "Llegiu la resenya completa en lÃnia..." Recall This = "Reclamar aquest" recaptcha_not_passed = "CAPTCHA invà lid" recently_returned_channel_title = "Retornat fa poc" +recommend_links_text = "També podeu provar:" Record Citations = "Cites del registre" Record Count = "Número de registres" Record Type = "Tipus de registre" @@ -855,6 +941,7 @@ recovery_title = "Recuperació de la contrasenya" recovery_too_soon = "Massa peticions de recuperació, si us plau torna a internar-ho en 5 minuts" recovery_user_not_found = "Usuari no registrat" rectangle_center_message = "This is the center point for the highlighted rectangle" +Reference Material = "Material de referència" Refine Results = "Refinar resultats" Region = "Regió" relais_available = "Aquest registre està disponible amb prèstec interbibliotecari. Voldira demanar-ho?" @@ -895,6 +982,7 @@ Requests = "Peticions" Reserves = "Reserves" Reserves Search = "Cerca de reserves" Reserves Search Results = "Resultats de la cerca de reserves" +reset_filters_button = "Treure filtres" result_checkbox_label = "Selecciona el número de resultat %%number%%" result_count = "%%count%% results" Results = "Resultats" @@ -910,6 +998,11 @@ Save Comment = "Guardar comentari" save_search = "Guardar cerca" save_search_remove = "Eliminar la cerca guardada" Saved in = "Guardat en" +schedule_daily = "Dià ria" +schedule_explanation = "Rebre correus electrònics d'alerta sobre els nous resultats de la cerca." +schedule_none = "Cap" +schedule_weekly = "Setmanal" +Scheduled Alert Results = "Resultats d'alerta programats" scholarly_limit = "Limitar a articles de revistes acadèmiques" Scroll to Load More = "Desplaça per veure més" Search = "Cerca" @@ -924,6 +1017,7 @@ search results of = "cerca resultats de" Search Tips = "Consells de cerca" Search Tools = "Eines de cerca" Search Type = "Tipus de cerca" +Search within collection = "Cerca dintre de la col·lecció" search_AND = "TOTS els termes" search_groups = "Grups de cerca" search_match = "Coindicència" @@ -949,6 +1043,7 @@ Sensor Image = "Imatge del sensor" Serial = "Publicació Periòdica" Series = "Periòdiques" Set = "Conjunt" +show_filters_html = "Veure filtres (%%count%%)" showing_items_html = "Mostrar <strong>%%start%% - %%end%%</strong> Exemplars" showing_items_of_html = "Mostrar <strong>%%start%% - %%end%%</strong> de <strong>%%total%%</strong> Exemplars" showing_results_for_html = "Mostrar <strong>%%start%% - %%end%%</strong> resultats per cerca '<strong>%%lookfor%%</strong>'" @@ -996,6 +1091,7 @@ Start a new Basic Search = "Començar una nova cerca bà sica" Start Page = "Pà gina d’inici" starting from = "començant des de" Status = "Estat" +status_transit = "En trà nsit" status_unknown_message = "Comprovació en temps real no disponible" Storage Retrieval Requests = "Peticions al magatzem" storage_retrieval_request_available = "Disponible per recollir" @@ -1071,6 +1167,7 @@ The record you selected is not part of any of your lists. = "El registre selecci The record you selected is not part of the selected list. = "El registre seleccionat no forma part de la llista seleccionada." The system is currently unavailable due to system maintenance = "El sistema actualment no està disponible degut a tasques de manteniment" Theme = "Tema" +Thesis = "Thesis" This email was sent from = "Correu electrònic enviat per" This field is required = "Aquest camp és necessari" This item is already part of the following list/lists = "L’Ãtem ja forma part de la/-es següent/-s llista/-es" @@ -1101,6 +1198,9 @@ unique_tags = "Etiquetes úniques" University Library = "Biblioteca università ria" Unknown = "Desconegut" unrecognized_facet_label = "Altrer" +unsubscribe_confirmation = "Voleu cancel·lar la subscripció per correu electrònic?" +unsubscribe_description = "No voldreu rebre aquest missatge en el futur? Cancel·la la subscripció mitjançant l’enllaç següent" +unsubscribe_successful = "Subscripció cancel·lada" Upgrade VuFind = "Actualitzar Vufind" upgrade_description = "Si esteu actualitzant Vufind, feu servir aquesta eina per carregar les configuracions antigues." URL = "URL" @@ -1110,6 +1210,14 @@ User Account = "Compte de l’usuari" Username = "Nom d’usuari" Username cannot be blank = "El nom d’usuari no pot estar en blanc" Username is already in use in another library card = "L' usuari ja està en ús." +verification_done = "La teva adreça de correu electrònic s'ha verificat correctament." +verification_email_change_sent = "S'han enviat instruccions de verificació de l'adreça de correu electrònic a la nova adreça de correu electrònic. Heu de verificar l'adreça abans que el canvi tingui efecte." +verification_email_notification = ""S'ha fet una sol·licitud per verificar la vostra adreça de correu electrònic del vostre compte amb %%library%%." +verification_email_sent = "Les instruccions de verificació de l'adreça de correu electrònic s'han enviat a l'adreça de correu electrònic registrada amb aquest compte." +verification_email_subject = "Verificació de correu electrònic VuFind" +verification_email_url_pretext = "Podeu verificar la vostra adreça de correu electrònic en aquest URL: %%url%%" +verification_too_soon = "El teu correu electrònic requereix validació. Recentment s'ha enviat un correu electrònic a la vostra adreça de correu electrònic registrada. Si no el heu rebut, espereu uns minuts i torneu-ho a provar." +verification_user_not_found = "No hem pogut trobar el vostre compte" VHS = "VHS" Video = "Video" Video Clips = "Video Clips" diff --git a/languages/cs.ini b/languages/cs.ini old mode 100644 new mode 100755 index 9988c3633ea5110bb5d817dd0abc79f58ea0e443..871ace8fbafdc509caa370cda14fed7a4c23ba88 --- a/languages/cs.ini +++ b/languages/cs.ini @@ -46,9 +46,10 @@ Advanced Search = "PokroÄilé vyhledávánÃ" advSearchError_noRights = "Bohužel nemáte oprávnÄ›nà upravovat toto vyhledávánÃ. Možná doÅ¡lo k automatickému odhlášenà po delÅ¡Ã neÄinnosti." advSearchError_notAdvanced = "VyhledávánÃ, které chcete upravit, nenà pokroÄilé vyhledávánÃ." advSearchError_notFound = "Požadované vyhledávánà nebylo nalezeno." -ajax_load_interrupted = "Nahrávánà bylo pÅ™eruÅ¡eno" +ajax_load_interrupted = "NaÄÃtánà stránky bylo pÅ™eruÅ¡eno" ajaxview_label_information = "Informace" ajaxview_label_tools = "Nástroje" +alert_email_address = "Adresa pro zasÃlánà upozornÄ›nà na nové výsledky vyhledávánÃ" All = "VÅ¡e" All Fields = "VÅ¡e" All Pages Loaded = "VÅ¡echny stránky nahrány" @@ -58,6 +59,7 @@ alphabrowselink_html = "Procházet záznamy podle %%index%% od <a href="%%url%%" An error has occurred = "DoÅ¡lo k chybÄ›" An error occurred during execution; please try again later. = "Vyskytla se chyba, zkuste to znovu pozdÄ›ji." AND = "AND" +and = "a" anonymous_tags = "Anonymnà tagy" APA Citation = "Citace podle APA" applied_filter = "Použitý filtr" @@ -70,6 +72,8 @@ authentication_error_admin = "PÅ™i pÅ™ihlášenà doÅ¡lo k chybÄ›. Kontaktujte s authentication_error_blank = "Vyplňte uživatelské jméno" authentication_error_creation_blocked = "Nemáte oprávnÄ›nà pro vytvoÅ™enà úÄtu." authentication_error_denied = "PÅ™Ãstup odmÃtnut" +authentication_error_email_not_verified_html = "VaÅ¡e e-mailová adresa doposud nebyla ověřena. Zkontrolujte také složku se spamem. Pokud je to nutné, můžeme Vám ověřovacà zprávu <a href="%%url%%">zaslat znovu.</a>." +authentication_error_in_progress = "Proces ověřenà právÄ› probÃhá. Pokud chcete zaÄÃt znovu, zkuste to pozdÄ›ji." authentication_error_invalid = "Neplatné jméno Äi heslo" authentication_error_loggedout = "Odhlásili jste se." authentication_error_technical = "PÅ™i pÅ™ihlaÅ¡ovánà doÅ¡lo k technické chybÄ›. Zkuste to pozdÄ›ji." @@ -95,7 +99,6 @@ Backtrace = "Podrobnosti" Bag = "KoÅ¡Ãk" Balance = "Dluh/Kredit" Barcode = "Čárový kód" -basic_search_keep_filters = "Zachovat souÄasné nastavenà filtrů" Be the first to leave a comment = "BuÄte prvnÃ, kdo okomentuje tento záznam" Be the first to tag this record = "BuÄte prvnÃ, kdo otaguje tento záznam" Bibliographic Details = "Podrobná bibliografie" @@ -165,6 +168,7 @@ Call Number = "Signatura" callnumber_abbrev = "Sign." Cannot find record = "Záznam nelze najÃt" Cannot find similar records = "Podobné záznamy nelze najÃt" +cannot set = "Nelze nastavit" Cassette = "Audio kazeta" cat_establish_account = "K vytvoÅ™enà úÄtu je nutné zadat následujÃcà údaje:" cat_password_abbrev = "Heslo" @@ -173,7 +177,12 @@ Catalog Login = "PÅ™ihlášenà do katalogu" Catalog Results = "Výsledky z katalogu" catalog_login_desc = "Vložte pÅ™Ãstupové údaje do katalogu knihovny." CD = "CD" +Change Email Address = "ZmÄ›nit e-maiovou adresu" Change Password = "ZmÄ›nit heslo" +change_email_disabled = "ZmÄ›na e-mailové adresy nenà povolena" +change_email_verification_reminder = "PotvrzenÃm formuláře dojde k odeslánà e-mailové zprávy na novou adresu. Pro dokonÄenà zmÄ›ny e-mailové adresy je tÅ™eba kliknout na odkaz obsažený v zaslané zprávÄ›." +change_notification_email_message = "NÄ›kdo (pravdÄ›podobnÄ› vy) vytvoÅ™il požadavek na zmÄ›nu VaÅ¡Ã e-mailové adresy v knihovnÄ› %%library%%. Pokud jste to nebyli vy, pÅ™ihlaste se do svého úÄtu na adrese %%url%% a zkontrolujte, že jsou VaÅ¡e údaje v pořádku. V pÅ™ÃpadÄ› jakýchkoliv pochybnostà kontaktujte knihovnu na adrese %%email%%." +change_notification_email_subject = "Oznámenà o zmÄ›nÄ› e-mailové adresy" channel_add_more = "PÅ™idat dalÅ¡Ã podobné pohledy" channel_browse = "Procházet dalÅ¡Ã záznamy" channel_expand = "Zobrazit souvisejÃcà pohledy" @@ -182,6 +191,7 @@ channel_search = "Zobrazit jako výsledek vyhledávánÃ" channel_searchbox_label = "Vyhledat dalÅ¡Ã pohledy:" Check Hold = "Zjistit stav rezervacÃ" Check Recall = "Zjistit stav objednávek" +check_profile = "Zkontrolujte své údaje." Checked Out = "VypůjÄeno" Checked Out Items = "VýpůjÄky" Checkedout = "VýpůjÄky" @@ -215,6 +225,7 @@ comment_error_load = "PÅ™i zÃskávánà komentářů doÅ¡lo k chybÄ›" comment_error_save = "PÅ™i ukládánà komentáře doÅ¡lo k chybÄ›" Comments = "Komentáře" Company/Entity = "Korporace" +Conference Proceeding = "KonferenÄnà pÅ™ÃspÄ›vek" Configuration = "NastavenÃ" confirm_delete = "Chcete je smazat?" confirm_delete_brief = "Smazat položku?" @@ -295,6 +306,7 @@ Due Date = "PůjÄeno do" DVD = "DVD" eBook = "E-kniha" Edit = "Upravit" +edit = "upravit" Edit Library Card = "Úpravit úÄet" Edit this Advanced Search = "Upravit toto pokroÄilé vyhledávánÃ" edit_list = "Upravit seznam" @@ -323,8 +335,14 @@ Email address is invalid = "Emailová adresa nenà platná" Email Record = "Zaslat záznam emailem" Email this = "Poslat emailem" Email this Search = "Poslat emailem" +email_change_pending_html = "Požádali jste o zmÄ›nu e-mailové adresy na %%pending%%. Pro potvrzenà zmÄ›ny kliknÄ›te na odkaz, který jsme odeslali na tuto adresu odeslali. Je-li to nutné, můžeme si nechat potvrzovacà e-mail <a href="%%url%%">poslat znovu</a>." email_failure = "Chyba pÅ™i odesÃlánà emailu" email_link = "Odkaz" +email_login_desc = "Pro pÅ™ihlášenà použijte následujÃcà odkaz. Pokud se nechcete pÅ™ihlásit, můžete tuto zprávu ignorovat. UpozornÄ›nÃ: následujà odkaz je platný pouze po omezený Äas a pouze pro zaÅ™ÃzenÃ, z kterého jste zahájili pÅ™ihlaÅ¡ovánÃ." +email_login_link = "Odkaz na pÅ™ihlášenÃ: <%%url%%>" +email_login_link_sent = "Na VaÅ¡i e-mailovou adresu jsme odeslali odkaz pro pÅ™ihlášenÃ. Může chvÃli trvat než dorazÃ. Pokud jste zprávu neobdrželi, zkontrolujte také složku se spamem." +email_login_requested = "Požádali jste o pÅ™ihlášenà pomocà e-mailové adresy na webových stránkách '%%title%%'." +email_login_subject = "PÅ™ihlášenà na stránky %%title%%" email_maximum_recipients_note = "Je možné urÄit maximálnÄ› %%max%% pÅ™Ãjemců zprávy." email_multiple_recipients_note = "Můžete urÄit vÃce pÅ™Ãjemců zprávy. Jednotlivé adresy oddÄ›lte Äárkou." email_selected = "Poslat emailem" @@ -334,6 +352,7 @@ email_subject = "Téma" email_success = "Odeslánà emailu bylo úspěšné" Empty = "Prázdný" Empty Book Bag = "Vyprázdnit koÅ¡Ãk" +empty_search_disallowed = "Tento typ vyhledávánà neumožňuje zadánà prázdného dotazu" Enable Auto Config = "Zapnout automatickou konfiguraci" End Page = "Konec stránky" Era = "ObdobÃ" @@ -386,12 +405,13 @@ Favorites = "OblÃbené" Fee = "Poplatek" Feedback = "Váš názor" feedback_email = "Poslat emailem" -feedback_login_required = "Nejprve se musÃte pÅ™ihlásit." feedback_name = "Jméno" Field of activity = "Oblast působenÃ" File Description = "Popis souboru" Filter = "Filtr" +Filter Collection = "Filtry sbÃrky" filter_tags = "Filtrovat tagy" +filter_toggle_entries = "Filtry: %%count%%" filter_wildcard = "Libovolný" Find = "Hledat" Find More = "Objevte vÃce" @@ -427,6 +447,7 @@ Go to Standard View = "Standardnà zobrazenÃ" go_to_list = "Zobrazit seznam" google_map_cluster = "Google map klastr" google_map_cluster_points = "Body Google map klastru" +Government Document = "Vládnà dokument" Grid = "MřÞka" Group = "Skupina" group_AND = "VÅ ECHNY skupiny" @@ -453,6 +474,7 @@ history_results = "Výsledky" history_save = "Akce" history_save_link = "Uložit" history_saved_searches = "Uložená vyhledávánÃ" +history_schedule = "Naplánovat zasÃlánà upozornÄ›nÃ" history_search = "Vyhledávacà dotaz" history_time = "Datum a Äas" hold_available = "PÅ™ipraveno k vyzvednutÃ" @@ -495,7 +517,7 @@ Identifier = "Identifikátor" ill_request_available = "PÅ™ipraveno k vyzvednutÃ" ill_request_cancel = "ZruÅ¡it požadavek na meziknihovnà výpůjÄku" ill_request_cancel_all = "ZruÅ¡it vÅ¡echny požadavky na meziknihovnà výpůjÄky" -ill_request_cancel_fail = "Váš požadavek se nepodaÅ™ilo zruÅ¡it . Kontaktujte pracovnÃky knihovny pro dalÅ¡Ã informace." +ill_request_cancel_fail = "Váš požadavek se nepodaÅ™ilo zruÅ¡it. Pro dalÅ¡Ã informace kontaktujte pracovnÃky knihovny." ill_request_cancel_selected = "ZruÅ¡it vybrané požadavky na meziknihovnà výpůjÄky" ill_request_cancel_success = "Váš požadavek byl úspěšnÄ› zruÅ¡en." ill_request_cancel_success_items = "Bylo zruÅ¡eno %%count%% požadavků" @@ -512,7 +534,7 @@ ill_request_error_unknown_patron_source = "PÅ™i zpracovánà požadavku se nepod ill_request_invalid_pickup = "Neplatné mÃsto vyzvednutÃ. Zkuste to znovu." ill_request_pick_up_library = "Vyzvednout v knihovnÄ›" ill_request_pick_up_location = "Vyzvednout na oddÄ›lenÃ" -ill_request_place_fail_missing = "Váš požadavek selhal. Chybà nÄ›které údaje. Kontaktujte pracovnÃky knihovny pro dalÅ¡Ã informace." +ill_request_place_fail_missing = "Váš požadavek selhal. Chybà nÄ›které údaje. Pro dalÅ¡Ã informace kontaktujte pracovnÃky knihovny." ill_request_place_success = "Váš požadavek byl úspěšnÄ› vložen." ill_request_place_success_html = "Váš požadavek byl úspěšnÄ› vložen. <a href="%%url%%">Požadavky na meziknihovnà výpůjÄky</a>." ill_request_place_text = "Objednat MVS" @@ -599,6 +621,7 @@ Library Web Search = "Vyhledávánà na webu knihovny" library_card_edit_password_placeholder = "Nové heslo" lightbox_error = "Chyba: nemohu otevÅ™Ãt vyskakovacà okno" Limit To = "Omezit na" +Link to full results = "Odkaz na úplné výsledky vyhledávánÃ" List = "Seznam" List Tags = "Vypsat tagy" list_access_denied = "Nemáte oprávnÄ›nà zobrazit tento seznam." @@ -648,7 +671,7 @@ more_topics = "%%count%% dalÅ¡Ãch témat" Most Recent Received Issues = "NejnovÄ›jÅ¡Ã jednotky" Multiple Call Numbers = "VÃce signatur" Multiple Locations = "VÃce umÃstÄ›nÃ" -Musical Score = "HodnocenÃ" +Musical Score = "Hudebnina" My Favorites = "OblÃbené" My Fines = "Poplatky" My Holds = "Rezervace" @@ -661,9 +684,12 @@ New Item Feed = "Nové tituly v katalogu" New Item Search = "Hledánà v novinkách" New Item Search Results = "Výsledky vyhledávánà v novinkách" New Items = "Nové tituly v katalogu" +New results found for search = "Byly nalezeny nové výsledky" New Title = "Nový název" +new_email_success = "VaÅ¡e e-mailová adresa byla zmÄ›nÄ›na" new_password = "Nové heslo" new_password_success = "VaÅ¡e heslo bylo úspěšnÄ› zmÄ›nÄ›no" +new_results_heading = "%%count%% nejnovÄ›jÅ¡Ãch výsledků" new_user_welcome_subject = "Váš nový úÄet v knihovnÄ›: %%library%%" new_user_welcome_text = "VÃtejte v knihovnÄ›: %%library%%. PrávÄ› se Vám podaÅ™ilo založit úÄet na jméno %%firstname%% %%lastname%%. VaÅ¡e pÅ™ihlaÅ¡ovacà jméno je %%username%%. Heslo si můžete nastavit na této stránce: %%url%%" Newspaper = "Noviny" @@ -679,6 +705,7 @@ No Preference = "Nezáležà na nich" No reviews were found for this record = "Žádná recenze pro tento záznam" No Tags = "Žádné tagy" no_description = "Žádný popis." +no_email_address = "Chybà e-mailová adresa." no_items_selected = "Nic nebylo vybráno" nohit_active_filters = "Použili jste funkci upÅ™esnÄ›nà výsledků. Pokud tato upÅ™esnÄ›nà odstranÃte, můžete zÃskat vÃce výsledků." nohit_change_tab = "Hledali jste v sekci "%%activeTab%%". Zkuste také dalÅ¡Ã sekce, tÅ™eba budete úspěšnà tam:" @@ -688,7 +715,7 @@ nohit_lookfor_html = "Pro váš dotaz - <strong>%%lookfor%%</strong> - nebyl nal nohit_no_filters = "Pro toto hledánà nebyly použity žádné filtry." nohit_parse_error = "Vypadá to, že je nÄ›jaký problém s vaÅ¡Ãm vyhledávacÃm dotazem. Zkuste zkontrolovat, je-li zapsán správnÄ›, nebo uzavÅ™ete hledaný výraz do uvozovek." nohit_query_without_filters = "Odstranit vÅ¡echna upÅ™esnÄ›nà výsledků." -nohit_spelling = "Můžete zkusit použÃt nÄ›které podobné tvary:" +nohit_spelling = "Můžete zkusit použÃt nÄ›které podobné tvary" nohit_suggest = "Můžete se pokusit upravit váš vyhledávacà dotaz vynechánÃm nÄ›kterých slov. Zkontrolujte také zda-li jste nenapsali nÄ›jaký pÅ™eklep." NOT = "NOT" Not Illustrated = "Bez ilustracÃ" @@ -730,8 +757,65 @@ number_decimal_point = "," number_thousands_separator = " " OAI Server = "OAI Server" Occupation = "PovolánÃ" +od_account_noaccess = "Nemáte povolen pÅ™Ãstup k obsahu ze systému OverDrive" +od_account_problem = "Vyskytl se problém s VaÅ¡Ãm úÄtem. %%message%%" +od_audiobook-mp3 = "Audiokniha v MP3" +od_audiobook-overdrive = "Audiokniha k poslechu v systému OverDrive" +od_avail_avail = "Dostupnost:" +od_avail_holds = "Rezervace:" +od_avail_total = "PoÄet kopiÃ:" +od_but_cancel_hold = "ZruÅ¡it rezervaci" +od_but_checkout = "VypůjÄit v systému Overdrive" +od_but_checkout_s = "VypůjÄit" +od_but_gettitle = "Stáhnout" +od_but_gettitle_s = "Stáhnout" +od_but_hold = "Rezervovat v systému Overdrive" +od_but_hold_s = "Rezervovat" +od_but_return = "Vrátit výpůjÄku" +od_cancel_hold = "ZruÅ¡it rezervaci" +od_checkout = "VýpůjÄka v systému Overdrive" +od_code_connection_failed = "PÅ™ipojenà k systému Overdrive selhalo. Pokud problém pÅ™etrvává, kontaktujte knihovnu." +od_code_contentnotavail = "Tento obsah nenà dostupný ve VaÅ¡Ã oblasti." +od_code_login_for_avail = "Pro zjiÅ¡tÄ›nà dostupnosti je tÅ™eba se pÅ™ihlásit" +od_code_resource_not_found = "Titul nebyl nalezen" +od_content = "Overdrive" +od_dl_formats = "Podporované formáty pro staženÃ" +od_docheckout_failure = "Tento titul nelze vypůjÄit." +od_docheckout_success = "Titul byl vypůjÄen. VýpůjÄka vyprÅ¡Ã dne %%expireDate%%" +od_early_return = "Vrátit pÅ™ed termÃnem" +od_ebook-epub-adobe = "E-kniha ve formátu Adobe EPUB" +od_ebook-epub-open = "E-kniha ve formátu Open EPUB" +od_ebook-kindle = "E-kniha ve formátu Kindle" +od_ebook-mediado = "E-kniha ve formátu MediaDo" +od_ebook-overdrive = "E-kniha ve formátu OverDrive Read" +od_ebook-pdf-adobe = "E-kniha ve formátu Adobe PDF" +od_ebook-pdf-open = "E-kniha ve formátu Open PDF" +od_expires_on = "VýpůjÄka vyprÅ¡Ã dne %%due_date%%." +od_get_title = "Stáhnout ze systému Overdrive" +od_gettitle_failure = "Tento titul nenà možné stáhnout." +od_help_linktext = "NápovÄ›da pro systém Overdrive" +od_history = "Historie v systému Overdrive" +od_hold = "Rezervace v systému Overdrive" +od_hold_cancel_failure = "Rezervaci se nepodaÅ™ilo zruÅ¡it." +od_hold_cancel_success = "Rezervace byla zruÅ¡ena." +od_hold_email = "E-mailová adresa pro zaslánà upozornÄ›nà pÅ™i dostupnosti rezervace: %%holdEmailAddress%%." +od_hold_now_avail = "Rezervovaný titul je nynà dostupný k vypůjÄenÃ. VýpůjÄka vyprÅ¡Ã dne %%expireDate%%." +od_hold_place_failure = "Rezervaci se nepodaÅ™ilo vytvoÅ™it." +od_hold_place_success = "Rezervace byla vytvoÅ™ena. VaÅ¡e poÅ™adà je %%holdListPosition%%." +od_hold_placed_on = "Rezervace byla vytvoÅ™ena dne %%holdPlacedDate%%." +od_hold_queue = "VaÅ¡e poÅ™adà ve frontÄ› rezervacà je %%holdPosition%% z %%numberOfHolds%%." +od_holds = "Rezervace v systému Overdrive" +od_info_unavail = "Tato informace nenà právÄ› dostupná." +od_is_checkedout = "Tento titul již máte vypůjÄený do %%due_date%%." +od_is_on_hold = "Tento titul již máte rezervovaný." +od_loans = "VýpůjÄky v systému Overdrive" +od_mycontent_help = "VÃce informacà o stahovánà tÄ›chto titulů si naleznete v <a href="%%url%%">nápovÄ›dÄ› systému OverDrive</a>." +od_none_found = "Nebyly nalezeny žádné tituly." +od_return_failure = "VýpůjÄku se nepodaÅ™ilo vrátit." +od_return_success = "VýpůjÄka byla vrácena." +od_video-streaming = "streamovánà videa" of_num_results = "Ä. %%position%% z %%total%% výsledků" -old_password = "Staré heslo" +old_password = "StávajÃcà heslo" On Reserve = "Rezervováno" On Reserve - Ask at Circulation Desk = "Rezervováno. Bližšà informace zÃskáte u knihovnÃka." on_reserve = "Rezervováno" @@ -759,6 +843,7 @@ pagination_label = "StránkovánÃ" Password = "Heslo" Password Again = "Heslo (pro kontrolu)" Password cannot be blank = "Heslo nesmà být prázdné" +password_error_auth_old = "Nezadali jste správnÄ› stávajÃcà heslo." password_error_invalid = "Nové heslo nenà platné (napÅ™. obsahuje nepovolené znaky)" password_error_not_unique = "Heslo nebylo zmÄ›nÄ›no" password_maximum_length = "Heslo může být dlouhé maximálnÄ› %%maxlength%% znaků" @@ -824,6 +909,7 @@ Read the full review online... = "PÅ™eÄÃst celou recenzi online..." Recall This = "Rezervovat" recaptcha_not_passed = "Kód CAPTCHA nesouhlasÃ." recently_returned_channel_title = "Nedávno vrácené" +recommend_links_text = "Můžete také zkusit:" Record Citations = "Citace záznamu" Record Count = "PoÄet záznamů" Record Type = "Typ záznamu" @@ -842,6 +928,7 @@ recovery_title = "Obnovenà hesla" recovery_too_soon = "Bylo zadáno pÅ™ÃliÅ¡ mnoho požadavků na obnovenà hesla, zkuste to, prosÃm, pozdÄ›ji." recovery_user_not_found = "Uživatelský úÄet se nepodaÅ™ilo nalézt" rectangle_center_message = "Toto je stÅ™ed vyznaÄené oblasti" +Reference Material = "ReferenÄnà dokument" Refine Results = "UpÅ™esnit hledánÃ" Region = "Oblast" relais_available = "Tento dokument je dostupný v rámci meziknihovnà výpůjÄnà služby. Chcete jej objednat?" @@ -873,8 +960,8 @@ renew_item_requested = "Tuto položku již požaduje jiný uživatel" renew_select_box = "Prodloužit výpůjÄku" renew_selected = "Prodloužit vybrané" renew_success = "Prodlouženà úspěšné" -Renewed = "Prodloužit" -Request full text = "Vyžádat plnný text" +Renewed = "PoÄet prodlouženÃ" +Request full text = "Vyžádat plný text" request_in_transit = "Na cestÄ› do mÃsta vyzvednutÃ" request_place_text = "VytvoÅ™enà rezervace" request_submit_text = "Odeslat požadavek" @@ -882,6 +969,7 @@ Requests = "Požadavky" Reserves = "Rezervace" Reserves Search = "Vyhledávánà rezervacÃ" Reserves Search Results = "Výsledky vyhledávánà rezervacÃ" +reset_filters_button = "ZruÅ¡it filtry" result_checkbox_label = "Vybrat výsledek ÄÃslo %%number%%" result_count = "%%count%% výsledků" Results = "Výsledky" @@ -897,6 +985,11 @@ Save Comment = "Uložte komentář" save_search = "Uložit hledánÃ" save_search_remove = "Odstranit uložené hledánÃ" Saved in = "Uloženo v" +schedule_daily = "DennÄ›" +schedule_explanation = "Nechte si zasÃlat nové výsledky vyhledávánà na Váš e-mail." +schedule_none = "Nikdy" +schedule_weekly = "TýdnÄ›" +Scheduled Alert Results = "E-mailová upozornÄ›nà na nové výsledky" scholarly_limit = "Omezit na Älánky z odborných Äasopisů" Scroll to Load More = "PosunovánÃm naÄtete dalÅ¡Ã" Search = "VyhledávánÃ" @@ -911,6 +1004,7 @@ search results of = "Výsledky dotazů pro" Search Tips = "Tipy pro vyhledávánÃ" Search Tools = "Vyhledávacà nástroje" Search Type = "Typ vyhledávánÃ" +Search within collection = "Vyhledávat ve sbÃrce" search_AND = "VÅ ECHNY výrazy" search_groups = "Vyhledávacà skupiny" search_match = "Shoda" @@ -936,6 +1030,7 @@ Sensor Image = "Sensor Image" Serial = "Seriál" Series = "Edice" Set = "Nastavit" +show_filters_html = "Zobrazit filtry (%%count%%)" showing_items_html = "Zobrazuji položky <strong>%%start%% - %%end%%</strong>" showing_items_of_html = "Zobrazuji položky <strong>%%start%% - %%end%%</strong> z <strong>%%total%%</strong>" showing_results_for_html = "Zobrazuji výsledky <strong>%%start%% - %%end%%</strong> pro vyhledávánà '<strong>%%lookfor%%</strong>'" @@ -983,7 +1078,8 @@ Start a new Basic Search = "ZaÄÃt základnà vyhledávánÃ" Start Page = "ZaÄÃt na stranÄ›" starting from = "zaÄÃnajÃcÃ" Status = "Stav" -status_unknown_message = "Live Status nedostupný" +status_transit = "Na cestÄ›" +status_unknown_message = "Aktuálnà dostupnost nelze naÄÃst" Storage Retrieval Requests = "Požadavky na vyzvednutà ze skladu" storage_retrieval_request_available = "PÅ™ipraveno k vyzvednutÃ" storage_retrieval_request_cancel = "ZruÅ¡it požadavek na vyzvednutà ze skladu" @@ -1058,6 +1154,7 @@ The record you selected is not part of any of your lists. = "Zvolený záznam ne The record you selected is not part of the selected list. = "Zvolený záznam nenà souÄástà vybraného seznamu." The system is currently unavailable due to system maintenance = "Aplikace je momentálnÄ› nedostupná z důvodu údržby" Theme = "Å ablona" +Thesis = "Diplomová práce" This email was sent from = "Tento email byl odeslán z" This field is required = "VyplnÄ›nà toho pole je povinné" This item is already part of the following list/lists = "Tato položka se již nacházà v následujÃcÃm seznamu/seznamech" @@ -1088,7 +1185,10 @@ unique_tags = "Unikátnà tagy" University Library = "Univerzitnà knihovna" Unknown = "Neznámo" unrecognized_facet_label = "Jiný" -Upgrade VuFind = "Aktualizujte VuFind" +unsubscribe_confirmation = "Chcete zruÅ¡it zasÃlánà upozornÄ›nà na nové výsledky vyhledávánÃ?" +unsubscribe_description = "Nechcete už dostávat dalÅ¡Ã upozornÄ›nÃ? ZasÃlánà upozornÄ›nà na nové výsledky vyhledávánà můžete zruÅ¡it na následujÃcÃm odkazu" +unsubscribe_successful = "ZasÃlánà upozornÄ›nà bylo zruÅ¡eno" +Upgrade VuFind = "Aktualizace VuFindu" upgrade_description = "Pokud aktualizujete z pÅ™edchozà verze VuFind, můžete použÃt tento nástroj k importu původnÃho nastavenÃ." URL = "URL" Use for = "DalÅ¡Ã jména" @@ -1097,6 +1197,14 @@ User Account = "Uživatelský úÄet" Username = "Uživatelské jméno" Username cannot be blank = "Uživatelské jméno musà být vyplnÄ›no" Username is already in use in another library card = "Uživatelské jméno již je použito u jiného Ätenářského úÄtu" +verification_done = "VaÅ¡e e-mailová adresa byla úspěšnÄ› ověřena." +verification_email_change_sent = "Na VaÅ¡i e-mailovou adresu byly zaslány instrukce pro ověřenà VaÅ¡Ã adresy. VaÅ¡i e-mailovou adresu nebudeme moci použÃt, dokud jejà ověřenà neprovedete." +verification_email_notification = "Byl vytvoÅ™en požadavek na ověřenà VaÅ¡Ã e-mailové adresy pro úÄet v knihovnÄ›: %%library%%." +verification_email_sent = "Zpráva s instrukcemi k ověřenà VaÅ¡Ã e-mailové adresy byla zaslaná na adresu zadanou pÅ™i vytvoÅ™enà úÄtu." +verification_email_subject = "Ověřenà e-mailové adresy" +verification_email_url_pretext = "Svoji e-mailovou adresu můžete ověřit na stránce %%url%%" +verification_too_soon = "Je tÅ™eba ověřit platnost e-mailové adresy. Zpráva s instrukcemi byla zaslána na Váš e-mail, pokud jste ji doposud neodrželi, vyÄkejte nÄ›kolik minut a vyzkouÅ¡ejte to znovu." +verification_user_not_found = "Váš úÄet se nepodaÅ™ilo najÃt" VHS = "VHS" Video = "Video" Video Clips = "Video klipy" diff --git a/languages/cy.ini b/languages/cy.ini index 3138218d52aa193ba494e4303e37866888d92fd5..d1e6c6d88d28464f9013e391b8e1115b7e40aeda 100644 --- a/languages/cy.ini +++ b/languages/cy.ini @@ -97,7 +97,6 @@ Backtrace = "Yn ôl" Bag = "Bag" Balance = "Balans" Barcode = "Cod Bar" -basic_search_keep_filters = "Cadw fy hidlwyr presennol" Be the first to leave a comment = "Byddwch y cyntaf i adael sylw" Be the first to tag this record = "Byddwch y cyntaf i dagio'r cofnod hwn" Bibliographic Details = "Manylion Llyfryddiaeth" @@ -387,7 +386,6 @@ Favorites = "Ffefrynnau" Fee = "Fee" Feedback = "Adborth" feedback_email = "E-bost" -feedback_login_required = "Rhaid i chi fewngofnodi yn gyntaf." feedback_name = "Enw" Field of activity = "Maes gweithgarwch" File Description = "Disgrifiad Ffeil" diff --git a/languages/da.ini b/languages/da.ini index 8e36300fe459fa3c6b2cb6dd5a3c0eb5d03c6358..e5ad0de54458d96bb18e60d3dd21552a551ebc82 100644 --- a/languages/da.ini +++ b/languages/da.ini @@ -78,7 +78,6 @@ Back to Search Results = "Tilbage til søgeresultat" Backtrace = "Backtrace" Bag = "Kurv" Balance = "Balance" -basic_search_keep_filters = "Behold mine nuværende filtre" Be the first to leave a comment = "Vær først til at give en kommentarø" Be the first to tag this record = "Vær først til at tagge denne postø" Bibliographic Details = "Bibliografiske detaljer" @@ -254,7 +253,6 @@ fav_list_delete_fail = "Vi beklager. Der er sket en fejl. Den liste blev ikke sl Favorites = "Favoritter" Fee = "Betaling" feedback_email = "Email" -feedback_login_required = "Du skal først være logget ind." Find = "Find" Find More = "Find flere" Find New Items = "Find nye værkerâ€" diff --git a/languages/de.ini b/languages/de.ini index 8a873065ca584a463b6e46a054ce8983777a98a6..f35511c4e912819d0db2906eb8a178b252c9b47c 100644 --- a/languages/de.ini +++ b/languages/de.ini @@ -50,6 +50,7 @@ advSearchError_notFound = "Die angeforderte Suche wurde nicht gefunden." ajax_load_interrupted = "Ladevorgang abgebrochen" ajaxview_label_information = "Information" ajaxview_label_tools = "Werkzeuge" +alert_email_address = "Alertinhalte werden regelmäßig an die E-Mail-Adresse gesendet" All = "Alles" All Fields = "Alle Felder" All Pages Loaded = "Alle Seiten geladen" @@ -59,6 +60,7 @@ alphabrowselink_html = "Einträge nach %%index%% ansehen ab <a href="%%url%%">%% An error has occurred = "Es ist ein Fehler aufgetreten" An error occurred during execution; please try again later. = "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal." AND = "UND" +and = "und" anonymous_tags = "Anonyme Tags" APA Citation = "APA Zitierstil" applied_filter = "Aktivierter Filter" @@ -71,6 +73,8 @@ authentication_error_admin = "Sie können sich momentan nicht einloggen. Bitte k authentication_error_blank = "Die Loginangaben dürfen nicht leer sein." authentication_error_creation_blocked = "Sie haben keine Berechtigung ein Konto zu eröffnen." authentication_error_denied = "Ungültige Angaben! Kein Zugang." +authentication_error_email_not_verified_html = "Ihre E-Mail-Adresse wurde noch nicht verifiziert. Bitte prüfen Sie Ihren Spamfilter auf die E-Mail zur Verifizierung. Falls notwendig können Sie hier <a href="%%url%%">eine neue E-Mail anfordern</a>." +authentication_error_in_progress = "Eine Authentifizierungsanforderung wird bereits bearbeitet. Bitte versuchen Sie es später noch einmal, falls Sie den Vorgang wiederholen müssen." authentication_error_invalid = "Ungültige Loginangaben -- Bitte versuchen Sie es erneut." authentication_error_loggedout = "Sie haben sich abgemeldet." authentication_error_technical = "Sie können sich momentan nicht einloggen. Bitte versuchen Sie es später erneut." @@ -96,7 +100,6 @@ Backtrace = "Ablaufverfolgung" Bag = "Korb" Balance = "Saldo" Barcode = "Strichcode" -basic_search_keep_filters = "Behalte die Filtereinstellungen." Be the first to leave a comment = "Schreiben Sie den ersten Kommentar" Be the first to tag this record = "Fügen Sie den ersten Tag hinzu" Bibliographic Details = "Bibliographische Detailangaben" @@ -166,6 +169,7 @@ Call Number = "Signatur" callnumber_abbrev = "Sig.: " Cannot find record = "Datensatz nicht gefunden" Cannot find similar records = "Keine ähnlichen Titel gefunden" +cannot set = "Kann nicht geändert werden" Cassette = "Kassette" cat_establish_account = "Um ihr Benutzerkonto einzurichten, hinterlegen Sie bitte folgende Angaben:" cat_password_abbrev = "Passwort Katalog" @@ -174,7 +178,12 @@ Catalog Login = "Anmeldung Bibliothekskonto" Catalog Results = "Katalogtreffer" catalog_login_desc = "Geben Sie die Anmeldeinformationen ihres Bibliothekskontos an." CD = "CD" +Change Email Address = "E-Mail-Adresse ändern" Change Password = "Passwort ändern" +change_email_disabled = "Sie sind momentan nicht berechtigt, Ihre E-Mail-Adresse zu ändern" +change_email_verification_reminder = "Nach dem Absenden dieses Formulars, wird eine E-Mail an die neue Adresse verschickt; Sie müssen zur Bestätigung auf den Link im E-Mail klicken, damit die Änderung übernommen wird." +change_notification_email_message = "Es wurde gerade eine Anfrage zur Änderung Ihrer E-Mail-Adresse gestellt bei: %%library%%. Falls nicht Sie diese Anfrage ausgelöst haben, können Sie sich unter %%url%% einloggen um die Angaben für ihr Konto zu bestätigen. Bitte kontaktieren Sie den Support unter %%email%%, wenn Sie Fragen oder Bedenken haben." +change_notification_email_subject = "Benachrichtigung über E-Mail-Änderungen ihres Kontos" channel_add_more = "Weitere ähnliche Inhalte hinzufügen" channel_browse = "Weitere Inhalte anzeigen" channel_expand = "Ähnliche Inhalte erkunden" @@ -183,6 +192,7 @@ channel_search = "Inhalte in den Suchergebnissen anzeigen" channel_searchbox_label = "Nach weiteren Inhalten suchen:" Check Hold = "Bestellbarkeit prüfen" Check Recall = "Möglichkeit einer Vormerkung prüfen" +check_profile = "Benutzerangaben überprüfen." Checked Out = "Ausgeliehen" Checked Out Items = "Ausgeliehene Medien" Checkedout = "Ausgliehene Medien" @@ -216,6 +226,7 @@ comment_error_load = "Fehler: Laden der Kommentareinträge fehlgeschlagen" comment_error_save = "Fehler: Speichern des Kommentars fehlgeschlagen" Comments = "Kommentare" Company/Entity = "Firma/Körperschaft" +Conference Proceeding = "Tagungsbericht" Configuration = "Konfiguration" confirm_delete = "Wollen Sie wirklich löschen?" confirm_delete_brief = "Löschen?" @@ -296,6 +307,7 @@ Due Date = "Rückgabedatum" DVD = "DVD" eBook = "E-Book" Edit = "Bearbeiten" +edit = "bearbeiten" Edit Library Card = "Bibliotheksausweis bearbeiten" Edit this Advanced Search = "Erweiterte Suche bearbeiten" edit_list = "Liste bearbeiten" @@ -324,8 +336,14 @@ Email address is invalid = "Die E-Mail-Adresse ist ungültig" Email Record = "Datensatz als E-Mail versenden" Email this = "Als E-Mail versenden" Email this Search = "Diese Suche als E-Mail versenden" +email_change_pending_html = "Sie haben eine anstehende E-Mail-Änderung für %%pending%%. Bitte klicken Sie auf den Link in der Bestätigungs-E-Mail an diese Adresse, um die Änderung abzuschließen. Bei Bedarf können wir die <a href="%%url%%">Bestätigungs-E-Mail erneut versenden</a>." email_failure = "Fehler - Nachricht nicht versendet" email_link = "E-Mail-Link" +email_login_desc = "Bitte verwenden Sie folgenden Link, um sich anzumelden. Falls nicht Sie diese Anfrage ausgelöst haben, können Sie diese Meldung ohne Weiteres ignorieren. Bitte beachten Sie, dass der Link nur für eine begrenzte Zeit und nur mit dem Gerät gültig ist, mit dem Sie die neue E-Mail-Adresse eingegeben haben." +email_login_link = "Link zum Login: <%%url%%>" +email_login_link_sent = "Wir haben einen Login-Link an Ihre E-Mail Adresse geschickt. Es kann einige Augenblicke dauern, bis der Link ankommt. Wenn Sie den Link nicht in Kürze erhalten, überprüfen Sie bitte auch Ihren Spamfilter." +email_login_requested = "Ein Login wurde mit Ihrer E-Mail-Adresse angefordert für %%title%%." +email_login_subject = "Login für %%title%%" email_maximum_recipients_note = "Sie können maximal %%max%% Adressen angeben." email_multiple_recipients_note = "Trennen Sie mehrere E-Mail-Adressen mit einem Komma." email_selected = "Auswahl per E-Mail versenden" @@ -335,6 +353,7 @@ email_subject = "Schlagwort" email_success = "Nachricht versendet" Empty = "Leer" Empty Book Bag = "Zwischenablage leeren" +empty_search_disallowed = "Eine leere Abfrage ist für das aktuelle Suchziel nicht erlaubt" Enable Auto Config = "Autokonfiguration aktivieren" End Page = "Letzte Seite" Era = "Ära" @@ -387,12 +406,13 @@ Favorites = "Favoriten" Fee = "Gebühr" Feedback = "Feedback" feedback_email = "E-Mail" -feedback_login_required = "Bitte loggen Sie sich zuerst ein." feedback_name = "Name" Field of activity = "Tätigkeitsbereich" File Description = "Dateibeschreibung" Filter = "Filter" +Filter Collection = "Sammlung filtern" filter_tags = "Tags filtern" +filter_toggle_entries = "%%count%% Filter" filter_wildcard = "alles" Find = "Suchen" Find More = "Weitere Suchoptionen" @@ -428,6 +448,7 @@ Go to Standard View = "Zurück zur Standardansicht" go_to_list = "Zurück zur Liste" google_map_cluster = "Ballung" google_map_cluster_points = "Ballungspunkte" +Government Document = "Regierungsdokument" Grid = "Tabellenansicht" Group = "Gruppe" group_AND = "Mit ALLEN Wortgruppen" @@ -454,6 +475,7 @@ history_results = "Ergebnis" history_save = "Speichern?" history_save_link = "Speichern" history_saved_searches = "Ihre gespeicherten Suchanfragen" +history_schedule = "Alerthäufigkeit" history_search = "Suche" history_time = "Zeit" hold_available = "Abholbereit" @@ -600,6 +622,7 @@ Library Web Search = "Bibiothek Internetsuche" library_card_edit_password_placeholder = "Neues Passwort" lightbox_error = "Fehler: Laden des Popup-Fensters fehlgeschlagen" Limit To = "Eingrenzen" +Link to full results = "Link zu den vollständigen Ergebnissen" List = "Liste" List Tags = "Tags auflisten" list_access_denied = "Sie sind nicht berechtigt diese Liste anzusehen." @@ -662,9 +685,12 @@ New Item Feed = "RSS-Feed Neuerscheinungen" New Item Search = "Suche Neuerscheinungen" New Item Search Results = "Neuerscheinungen Suchergebnisse" New Items = "Neuerscheinungen" +New results found for search = "Neue Ergebnisse für die Suche gefunden" New Title = "Späterer Titel" +new_email_success = "Ihre E-Mail-Adresse wurde erfolgreich geändert" new_password = "Neues Passwort" new_password_success = "Ihr Passwort wurde erfolgreich geändert" +new_results_heading = "%%count%% neueste Ergebnisse" new_user_welcome_subject = "Ihr neuer Account bei %%library%%" new_user_welcome_text = "Willommen bei %%library%%. Für %%firstname%% %%lastname%% wurde ein neues Konto eröffnet. Ihr Benutzername lautet %%username%%. Bitte setzen Sie ein Passwort auf folgender Seite: %%url%%" Newspaper = "Zeitung" @@ -680,6 +706,7 @@ No Preference = "Keine Vorgabe" No reviews were found for this record = "Es wurden keine Rezensionen für diesen Datensatz gefunden" No Tags = "Keine Tags" no_description = "Keine Beschreibung verfügbar." +no_email_address = "E-Mail-Adresse fehlt." no_items_selected = "Sie haben nichts ausgewählt" nohit_active_filters = "Es wurden Suchfilter/Facetten verwendet. Ohne Filter kann die Ergebnisanzahl möglicherweise erhöht werden." nohit_change_tab = "Sie haben im Reiter %%activeTab%% gesucht. Möglicherweise finden Sie etwas in einem anderen Reiter:" @@ -731,6 +758,63 @@ number_decimal_point = "," number_thousands_separator = "." OAI Server = "OAI-Server" Occupation = "Beruf" +od_account_noaccess = "Dieses Bibliothekskonto hat keinen Zugriff auf Inhalte in Overdrive." +od_account_problem = "Es gibt ein Problem mit deinem Konto. %%message%%" +od_audiobook-mp3 = "MP3 Hörbuch" +od_audiobook-overdrive = "OverDrive Hörbuch" +od_avail_avail = "Verfügbar:" +od_avail_holds = "Warteliste:" +od_avail_total = "Total Exemplare:" +od_but_cancel_hold = "Diese Vormerkung löschen" +od_but_checkout = "Ausleihe über Overdrive" +od_but_checkout_s = "Ausleihe" +od_but_gettitle = "Diesen Inhalt herunterladen" +od_but_gettitle_s = "Herunterladen" +od_but_hold = "Eine Vormerkung in Overdrive aufgeben" +od_but_hold_s = "Vormerkung aufgeben" +od_but_return = "Diesen Titel zurückgeben" +od_cancel_hold = "Vormerkung für Overdrive löschen" +od_checkout = "Overdrive Ausleihe" +od_code_connection_failed = "Die Verbindung zu Overdrive ist fehlgeschlagen. Kontaktieren Sie ihre Bibliothek, falls das Problem andauert." +od_code_contentnotavail = "Dieser Inhalt ist für ihre Region nicht freigeschaltet." +od_code_login_for_avail = "Login für Verfügbarkeit" +od_code_resource_not_found = "Title nicht gefunden" +od_content = "Overdrive Inhalte" +od_dl_formats = "Unterstützte Download-Formate" +od_docheckout_failure = "Dieser Titel konnte nicht ausgeliehen werden." +od_docheckout_success = "Dieser Titel wurde für Sie ausgeliehen. Die Ausleihe läuft ab am %%expireDate%%" +od_early_return = "Overdrive vorzeitige Rückgabe" +od_ebook-epub-adobe = "Adobe EPUB e-Book" +od_ebook-epub-open = "EPUB e-Book öffnen" +od_ebook-kindle = "Kindle e-Book" +od_ebook-mediado = "MediaDo Reader e-Book" +od_ebook-overdrive = "OverDrive Online e-Book" +od_ebook-pdf-adobe = "Adobe PDF e-Book" +od_ebook-pdf-open = "PDF e-Book öffnen" +od_expires_on = "Dieser Titel läuft ab am %%due_date%%." +od_get_title = "Overdrive Download" +od_gettitle_failure = "Dieser Titel konnte nicht heruntergeladen werden." +od_help_linktext = "Overdrive Hilfe" +od_history = "Overdrive bisherige Nutzung" +od_hold = "Overdrive Vormerkungen" +od_hold_cancel_failure = "Das Löschen der Vormerkung ist fehlgeschlagen. " +od_hold_cancel_success = "Vormerkung erfolgreich gelöscht." +od_hold_email = "E-Mail-Adresse für Benachrichtigung bei Vormerkungen: %%holdEmailAddress%%." +od_hold_now_avail = "Diese Vormerkung ist zur Ausleihe bereit. Die Ausleihe läuft ab am %%expireDate%%." +od_hold_place_failure = "Vormerkung fehlgeschlagen." +od_hold_place_success = "Dieser Titel wurde vorgemerkt. Sie befinden sich in der Warteliste an folgender Stelle: %%holdListPosition%%" +od_hold_placed_on = "Vormerkung aufgegeben am %%holdPlacedDate%%." +od_hold_queue = "Vormerkung %%holdPosition%% von %%numberOfHolds%% Vormerkungen in der Warteliste." +od_holds = "Overdrive Vormerkungen" +od_info_unavail = "Diese Informationen sind derzeit nicht verfügbar." +od_is_checkedout = "Sie haben diesen Titel ausgeliehen. Er wird fällig am %%due_date%%." +od_is_on_hold = "Sie haben diesen Titel vorgemerkt." +od_loans = "Overdrive Ausleihen" +od_mycontent_help = "Informationen und Hilfe zum Herunterladen dieser Titel finden Sie unter <a href="%%url%%">Overdrive Help</a>." +od_none_found = "Keine Titel gefunden." +od_return_failure = "Dieser Titel konnte nicht ausgebucht werden." +od_return_success = "Dieser Titel wurde ausgebucht." +od_video-streaming = "Videodatei streamen" of_num_results = "#%%position%% von %%total%% Treffer" old_password = "Bisheriges Passwort" On Reserve = "Vorgemerkt" @@ -760,6 +844,7 @@ pagination_label = "Seitenzahlen" Password = "Passwort" Password Again = "Passwort wiederholen" Password cannot be blank = "Das Passwort darf nicht leer sein" +password_error_auth_old = "Das bisher verwendete Password ist nicht gültig" password_error_invalid = "Ungültiges neues Passwort (enthält z.B. ungültige Zeichen)" password_error_not_unique = "Das Passwort wurde nicht geändert" password_maximum_length = "Die maximale Länge des Passworts beträgt %%maxlength%% Zeichen" @@ -825,6 +910,7 @@ Read the full review online... = "Die vollständige Rezension online lesen..." Recall This = "Vormerken" recaptcha_not_passed = "CAPTCHA nicht korrekt eingegeben" recently_returned_channel_title = "Kürzlich zurückgekommen" +recommend_links_text = "Sie können auch Folgendes versuchen:" Record Citations = "Zitate" Record Count = "Satzanzahl" Record Type = "Datenart" @@ -843,6 +929,7 @@ recovery_title = "Passwort zurücksetzen" recovery_too_soon = "Es wurden zuviele Anfragen gestellt um das Passwort zurückzusetzen. Bitte versuchen Sie es später wieder" recovery_user_not_found = "Ihr Konto wurde nicht gefunden" rectangle_center_message = "Mittelpunkt des hervorgehobenen Rechts" +Reference Material = "Nachschlagewerke" Refine Results = "Treffer weiter einschränken" Region = "Region" relais_available = "Das Medium ist per Fernleihe verfügbar. Möchten Sie das Exemplar anfordern?" @@ -883,6 +970,7 @@ Requests = "Anfragen" Reserves = "Vormerkungen" Reserves Search = "Suche Vormerkungen" Reserves Search Results = "Suchergebnisse Vormerkungen" +reset_filters_button = "Filter zurücksetzen" result_checkbox_label = "Bitte wählen Sie die Treffernummer %%number%%" result_count = "%%count%% Treffer" Results = "Treffer" @@ -898,6 +986,11 @@ Save Comment = "Kommentar speichern" save_search = "Suche speichern" save_search_remove = "Lösche gespeicherte Suche" Saved in = "Gespeichert in" +schedule_daily = "Täglich" +schedule_explanation = "Alerts für eine Suche per E-Mail erhalten." +schedule_none = "Nie" +schedule_weekly = "Wöchentlich" +Scheduled Alert Results = "Regelmäßiger Versand von Alertinhalten" scholarly_limit = "Eingrenzen auf Artikel aus Fachzeitschriften" Scroll to Load More = "Scrollen um mehr anzuzeigen" Search = "Suche" @@ -912,6 +1005,7 @@ search results of = "Treffer von" Search Tips = "Suchtipps" Search Tools = "Suchwerkzeuge" Search Type = "Suchmodus" +Search within collection = "Innerhalb der Kollektion suchen" search_AND = "Mit ALLEN Wörtern" search_groups = "Suchgruppe" search_match = "Suchbedingung" @@ -937,6 +1031,7 @@ Sensor Image = "Fernerkundungsbilder" Serial = "Schriftenreihe" Series = "Schriftenreihe" Set = "Wechseln" +show_filters_html = "Filter anzeigen (%%count%%)" showing_items_html = "Treffer <strong>%%start%% - %%end%%</strong>" showing_items_of_html = "Treffer <strong>%%start%% - %%end%%</strong> von <strong>%%total%%</strong>" showing_results_for_html = "Treffer <strong>%%start%% - %%end%%</strong> für Suche '<strong>%%lookfor%%</strong>'" @@ -984,6 +1079,7 @@ Start a new Basic Search = "Neue einfache Suche starten" Start Page = "Erste Seite" starting from = "ab" Status = "Status" +status_transit = "unterwegs" status_unknown_message = "Live-Status nicht verfügbar" Storage Retrieval Requests = "Magazinbestellungen" storage_retrieval_request_available = "Abholbereit" @@ -1059,6 +1155,7 @@ The record you selected is not part of any of your lists. = "Der ausgewählte Da The record you selected is not part of the selected list. = "Der ausgewählte Datensatz ist in nicht in der ausgewählten Liste gespeichert." The system is currently unavailable due to system maintenance = "Das System ist momentan wegen Wartungsarbeiten nicht verfügbar" Theme = "Layout" +Thesis = "Abschlussarbeit" This email was sent from = "Diese Mail wurde verschickt von" This field is required = "Pflichtfeld" This item is already part of the following list/lists = "Dieser Datensatz ist bereits in der/den folgenden Liste/Listen enthalten" @@ -1089,6 +1186,9 @@ unique_tags = "Einmalige Tags" University Library = "Universitätsbibliothek" Unknown = "Unbekannt" unrecognized_facet_label = "Sonstige" +unsubscribe_confirmation = "Sie möchten Sie dieses E-Mail-Abonnement kündigen?" +unsubscribe_description = "Sie möchten diese Nachricht in Zukunft nicht mehr erhalten? Kündigen Sie das Abonnement über den folgenden Link" +unsubscribe_successful = "Abonnement gekündigt" Upgrade VuFind = "Vufind upgraden" upgrade_description = "Falls Sie von einer früheren Version von Vufind upgraden, können Sie ihre bisherigen Einstellungen mit diesem Tool übernehmen." URL = "URL" @@ -1098,6 +1198,14 @@ User Account = "Persönliches Konto" Username = "Benutzername" Username cannot be blank = "Der Benutzername darf nicht leer sein" Username is already in use in another library card = "Der Benutzername ist bereits für einen anderen Bibliotheksausweis vergeben" +verification_done = "Ihre E-Mail-Adresse wurde erfolgreich verifiziert." +verification_email_change_sent = "Die Informationen zur Ãœberprüfung der E-Mail-Adresse wurden an die neue E-Mail-Adresse gesendet. Sie müssen die Adresse bestätigen, bevor die Änderung wirksam wird." +verification_email_notification = "Für ihr Konto bei %%library%% wurde soeben die Anfrage ausgelöst ihre E-Mail-Adresse zu verifizieren." +verification_email_sent = "Es wurden Angaben an die für dieses Konto registrierte E-Mail-Adresse verschickt, um die E-Mail-Adresse zu verifizieren." +verification_email_subject = "VuFind-Konto verifizieren" +verification_email_url_pretext = "Bitte rufen Sie diese URL auf um Ihre E-Mail-Adresse zu verifizieren: %%url%%" +verification_too_soon = "Ihre E-Mail-Adresse wurde noch nicht verifiziert. Hierzu wurde eine E-Mail an Ihre hinterlegte Adresse gesendet. Falls Sie diese noch nicht erhalten haben, warten Sie bitte einige Minuten und versuchen es dann erneut." +verification_user_not_found = "Ihr Konto wurde nicht gefunden" VHS = "VHS" Video = "Video" Video Clips = "Videoclips" diff --git a/languages/el.ini b/languages/el.ini index 760d6448a275fba6a207174ef1d230c48635b703..88fce875fcfc9290f9593c6f580ae33aba4fff7f 100644 --- a/languages/el.ini +++ b/languages/el.ini @@ -51,6 +51,7 @@ advSearchError_notFound = "Δεν βÏÎθηκαν αποτελÎσματα γι ajax_load_interrupted = "Η φόÏτωση διακόπηκε" ajaxview_label_information = "ΠληÏοφοÏίες" ajaxview_label_tools = "ΕÏγαλεία" +alert_email_address = "Θα σταλοÏν Ï€ÏογÏαμματισμÎνες ειδοποιήσεις στην email διεÏθυνση" All = "Όλα" All Fields = "Όλα τα πεδία" All Pages Loaded = "Η φόÏτωση σελίδων ολοκληÏώθηκε" @@ -60,6 +61,7 @@ alphabrowselink_html = "ΠεÏιήγηση σε %%index%% ξεκινώντας An error has occurred = "ΠαÏουσιάστηκε σφάλμα" An error occurred during execution; please try again later. = "ΠαÏουσιάστηκε σφάλμα κατά την εκτÎλεση. Ξαναδοκιμάστε αÏγότεÏα." AND = "KAI" +and = "και" anonymous_tags = "Ανώνυμες ετικÎτες" APA Citation = "ΠαÏαπομπή APA" applied_filter = "ΕφαÏμοσμÎνο φίλτÏο" @@ -72,6 +74,8 @@ authentication_error_admin = "Η σÏνδεση δεν είναι δυνατή. authentication_error_blank = "Τα στοιχεία Ï€Ïόσβασης δεν μποÏεί να είναι κενά" authentication_error_creation_blocked = "Δεν Îχετε τη δυνατότητα να δημιουÏγήσετε λογαÏιασμό." authentication_error_denied = "Η σÏνδεση δεν είναι δυνατή. Δεν επιτÏÎπεται η Ï€Ïόσβαση" +authentication_error_email_not_verified_html = "Η διεÏθυνση του email σας δεν Îχει επιβεβαιωθεί ακόμα. ΠαÏακαλώ ελÎγξτε την αλληλογÏαφία σας (ακόμα και τον φάκελο της ανεπιθÏμητης αλληλογÏαφίας) για να εντοπίσετε το email επιβεβαίωσης που σας Îχει σταλεί. Εάν χÏειαστεί, το email επιβεβαίωσης μποÏεί να <a href="%%url%%">σας σταλεί εκ νÎου</a>." +authentication_error_in_progress = "Το αίτημα ελÎγχου ταυτότητας είναι υπό επεξεÏγασία. ΠαÏακαλώ δοκιμάστε αÏγότεÏα αν χÏειαστεί να αÏχίσετε από την αÏχή." authentication_error_invalid = "Τα στοιχεία που δώσατε δεν είναι ÎγκυÏα. Δοκιμάστε ξανά" authentication_error_loggedout = "Έχετε αποσυνδεθεί." authentication_error_technical = "Η σÏνδεση δεν είναι δυνατή. Δοκιμάστε αÏγότεÏα" @@ -97,7 +101,6 @@ Backtrace = "Διαγνωστική πληÏοφοÏία" Bag = "Καλάθι" Balance = "Οφειλόμενα" Barcode = "Ραβδοκώδικας" -basic_search_keep_filters = "ΔιατήÏηση φίλτÏων" Be the first to leave a comment = "καταχωÏήστε σχόλιο Ï€Ïώτοι" Be the first to tag this record = "ΚαταχωÏήστε ετικÎτα Ï€Ïώτοι" Bibliographic Details = "ΛεπτομÎÏειες βιβλιογÏαφικής εγγÏαφής" @@ -167,6 +170,7 @@ Call Number = "Ταξιθετικός ΑÏιθμός" callnumber_abbrev = "Ταξιθετικός ΑÏιθμός" Cannot find record = "Δε βÏÎθηκε" Cannot find similar records = "Δε βÏÎθηκαν παÏόμοια αÏχεία" +cannot set = "Αδυναμία οÏισμοÏ" Cassette = "ΚασÎτα" cat_establish_account = "Για να δημιουÏγήσετε λογαÏιασμό, παÏακαλώ εισάγετε τις παÏακάτω πληÏοφοÏίες:" cat_password_abbrev = "Κωδικός καταλόγου" @@ -175,7 +179,12 @@ Catalog Login = "Είσοδος καταλόγου" Catalog Results = "ΑποτελÎσματα καταλόγου" catalog_login_desc = "Εισάγετε τα στοιχεία εισόδου για τον κατάλογο." CD = "CD" +Change Email Address = "Αλλαγή διεÏθυνσης email" Change Password = "Αλλαγή κωδικοÏ" +change_email_disabled = "Δεν Îχετε δυνατότητα αλλαγής της διεÏθυνσης email" +change_email_verification_reminder = "Με την υποβολή αυτής της φόÏμας θα σταλεί Îνα email στην νÎα διεÏθυνση το οποίο θα πεÏιÎχει Îναν σÏνδεσμο επιβεβαίωσης. Θα χÏειαστεί να κάνετε κλικ στον σÏνδεσμο επιβεβαίωσης για να τεθεί σε Î¹ÏƒÏ‡Ï Î· αλλαγή της διεÏθυνσης email." +change_notification_email_message = "ΥπήÏξε αίτημα για αλλαγή της διεÏθυνσης email σας στην %%library%%. Εάν δεν ξεκινήσατε εσείς αυτό το αίτημα, πιθανόν να Ï€ÏÎπει να μπείτε στο %%url%% για να επιβεβαιώσετε την ακεÏαιότητα του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚. Σε πεÏίπτωση που Îχετε εÏωτήσεις ή αποÏίες, παÏακαλώ επικοινωνήστε με την τεχνική υποστήÏιξη στο %%email%%. " +change_notification_email_subject = "Ειδοποίηση αλλαγής διεÏθυνσης email" channel_add_more = "Î ÏοσθÎστε παÏόμοια κανάλια" channel_browse = "ΠεÏιηγηθείτε σε πεÏισσότεÏες εγγÏαφÎÏ‚" channel_expand = "Î’Ïείτε σχετικά κανάλια" @@ -184,6 +193,7 @@ channel_search = "Εμφάνιση τεκμηÏίων ως αποτελεσμά channel_searchbox_label = "Αναζητήστε πεÏισσότεÏα κανάλια:" Check Hold = "Επιλογή κÏατημÎνων" Check Recall = "Επιλογή ανακληθÎντων" +check_profile = "Έλεγχος πληÏοφοÏιών χÏήστη." Checked Out = "Μη διαθÎσιμο" Checked Out Items = "ΔανεισμÎνα τεκμήÏια" Checkedout = "ΔανεισμÎνο" @@ -217,6 +227,7 @@ comment_error_load = "Σφάλμα: Το σχόλιο δε βÏÎθηκε" comment_error_save = "Σφάλμα: Το σχόλιο δεν αποθηκεÏτηκε" Comments = "Σχόλια" Company/Entity = "ΕταιÏία/Οντότητα" +Conference Proceeding = "Î Ïακτικό ΣυνεδÏίου" Configuration = "ΠαÏαμετÏοποίηση" confirm_delete = "ΣίγουÏα θÎλετε να διαγÏάψετε;" confirm_delete_brief = "Îα διαγÏαφεί το τεκμήÏιο;" @@ -297,6 +308,7 @@ Due Date = "ΗμεÏομηνία επιστÏοφής" DVD = "DVD" eBook = "Ηλ. βιβλίο" Edit = "ΕπεξεÏγασία" +edit = "επεξεÏγασία" Edit Library Card = "ΕπεξεÏγασία κάÏτας βιβλιοθήκης" Edit this Advanced Search = "ΤÏοποποίηση σÏνθετης αναζήτησης" edit_list = "ΕπεξεÏγασία λίστας" @@ -325,8 +337,14 @@ Email address is invalid = "Η διεÏθυνση email δεν είναι Îγκ Email Record = "αÏχείο Email" Email this = "Αποστολή με email" Email this Search = "Αποστολή αναζήτησης με email" +email_change_pending_html = "ΕκκÏεμεί η αλλαγή διεÏθυνσης email σε %%pending%%. ΠαÏακαλώ κάντε κλικ στον σÏνδεσμο επιβεβαίωσης που πεÏιÎχεται στο email που απεστάλη στην νÎα διεÏθυνση, ώστε να ολοκληÏώσετε την αλλαγή. Σε πεÏίπτωση που χÏειαστεί, μποÏεί να γίνει <a href="%%url%%">εκ νÎου αποστολή του email επιβεβαίωσης</a>." email_failure = "Σφάλμα - το μήνυμα δε στάλθηκε" email_link = "ΣÏνδεσμος email" +email_login_desc = "ΠαÏακαλώ χÏησιμοποιήστε τον παÏακάτω σÏνδεσμο για είσοδο στο σÏστημα. Ωστόσο, εάν δεν ξεκινήσατε την διαδικασία εισόδου στο σÏστημα, μποÏείτε με ασφάλεια να αγνοήσετε αυτό το μήνυμα. ΠαÏακαλώ σημειώστε ότι ο σÏνδεσμος Îχει Î¹ÏƒÏ‡Ï Î³Î¹Î± πεÏιοÏισμÎνο χÏονικό διάστημα και μποÏεί να χÏησιμοποιηθεί μόνο από την συσκευή στην οποία Îγινε η εισαγωγή της νÎας email διεÏθυνσης." +email_login_link = "ΣÏνδεσμος για είσοδο στο σÏστημα: <%%url%%>" +email_login_link_sent = "Σας Îχει αποσταλεί Îνας σÏνδεσμος εισόδου στο σÏστημα στην καταχωÏημÎνη email διεÏθυνση σας. ΜποÏεί να χÏειαστεί να πεÏιμÎνετε οÏισμÎνα λεπτά μÎχÏι να εμφανιστεί το νÎο μήνυμα στο γÏαμματοκιβώτιό σας. Εάν Îχει πεÏάσει εÏλογο χÏονικό διάστημα και συνεχίζετε να μην βλÎπετε το μήνυμα, ενδεχομÎνως να Ï€ÏÎπει να ελÎγξετε τον φάκελο ανεπιθÏμητης αλληλογÏαφίας του email παÏόχου σας." +email_login_requested = "Ζητήθηκε είσοδος στο %%title%% με την email διεÏθυνσή σας." +email_login_subject = "Είσοδος σε %%title%%" email_maximum_recipients_note = "Ο επιτÏεπόμενος μÎγιστος αÏιθμός παÏαληπτών είναι %%max%%." email_multiple_recipients_note = "ΜποÏείτε να οÏίσετε πολλαπλοÏÏ‚ παÏαλήπτες διαχωÏίζοντάς τους με κόμμα." email_selected = "Αποστολή επιλεγμÎνων με email" @@ -336,6 +354,7 @@ email_subject = "ΘÎμα" email_success = "Το μήνυμα στάλθηκε" Empty = "Άδειο" Empty Book Bag = "Άδειασμα καλαθιοÏ" +empty_search_disallowed = "Δεν επιτÏÎπεται κενό εÏώτημα με τον Ï„ÏÎχοντα στόχο αναζήτησης" Enable Auto Config = "ΕνεÏγοποίηση αυτόματης παÏαμετÏοποίησης" End Page = "ΤÎλος σελίδας" Era = "ΧÏονική ΠεÏίοδος" @@ -388,12 +407,13 @@ Favorites = "ΑγαπημÎνα" Fee = "ΧÏÎωση" Feedback = "Σχόλιά" feedback_email = "ΗλεκτÏονικό ταχυδÏομείο" -feedback_login_required = "Î ÏÎπει Ï€Ïώτα να συνδεθείτε." feedback_name = "Όνομα" Field of activity = "Πεδίο δÏαστηÏιότητας" File Description = "ΠεÏιγÏαφή αÏχείου" Filter = "ΦίλτÏο" +Filter Collection = "ΦιλτÏάÏισμα συλλογής" filter_tags = "ΦιλτÏάÏισμα ετικετών" +filter_toggle_entries = "%%count%% φίλτÏα" filter_wildcard = "Οτιδήποτε" Find = "Αναζήτηση" Find More = "Î’Ïείτε πεÏισσότεÏα" @@ -429,6 +449,7 @@ Go to Standard View = "Κανονική Ï€Ïοβολή" go_to_list = "Μετάβαση στη λίστα" google_map_cluster = "ΣÏμπλεγμα (Google Maps)" google_map_cluster_points = "Σημεία ομαδοποίησης" +Government Document = "ΚυβεÏνητικό ΈγγÏαφο" Grid = "ΠλÎγμα" Group = "ΚατηγοÏία" group_AND = "Όλες οι ομάδες" @@ -455,6 +476,7 @@ history_results = "ΑποτελÎσματα" history_save = "Αποθήκευση ΙστοÏικοÏ;" history_save_link = "Αποθήκευση ΣÏνδεσμου ΙστοÏικοÏ" history_saved_searches = "ΑποθηκευμÎνες Αναζητήσεις ΙστοÏικοÏ" +history_schedule = "Î ÏόγÏαμμα ειδοποιήσεων" history_search = "Αναζήτηση ΙστοÏικοÏ" history_time = "ÎÏα ΙστοÏικοÏ" hold_available = "ΔιαθÎσιμο" @@ -601,6 +623,7 @@ Library Web Search = "Αναζήτηση ιστοσελίδων βιβλιοθή library_card_edit_password_placeholder = "ÎÎος κωδικός" lightbox_error = "Σφάλμα. Αδυναμία φόÏτωσης αναδυόμενου παÏαθÏÏου" Limit To = "ΠεÏιοÏισμός σε" +Link to full results = "ΣÏνδεσμος για πλήÏη αποτελÎσματα" List = "Λίστα" List Tags = "Λίστα ετικετών" list_access_denied = "Δεν Îχετε Ï€Ïόσβαση σε αυτή τη λίστα" @@ -663,9 +686,12 @@ New Item Feed = "Feed νÎων τεκμηÏίων" New Item Search = "Αναζήτηση νÎων τεκμηÏίων" New Item Search Results = "ΑποτελÎσματα αναζήτησης νÎων τεκμηÏίων" New Items = "ÎÎα τεκμήÏια" +New results found for search = "Î’ÏÎθηκαν νÎα αποτελÎσματα για την αναζήτηση" New Title = "ÎÎος τίτλος" +new_email_success = "Η διεÏθυνση email άλλαξε με επιτυχία" new_password = "ÎÎος κωδικός" new_password_success = "Ο κωδικός σας άλλαξε επιτυχώς" +new_results_heading = "%%count%% νεότεÏα αποτελÎσματα" new_user_welcome_subject = "Ο νÎος λογαÏιασμός σας στη %%library%%" new_user_welcome_text = "ΚαλωσήÏθατε στη %%library%%. ΔημιουÏγήσατε νÎο λογαÏιασμό για το ονοματεπώνυμο %%firstname%% %%lastname%%, με όνομα χÏήστη %%username%%. Για να οÏίσετε τον κωδικό Ï€Ïόσβασης μεταβείτε στη σελίδα: %%url%%" Newspaper = "ΕφημεÏίδα" @@ -681,6 +707,7 @@ No Preference = "ΑδιάφοÏο" No reviews were found for this record = "Δεν υπάÏχουν reviews για αυτή την εγγÏαφή" No Tags = "Δεν υπάÏχουν" no_description = "Η πεÏιγÏαφή δεν είναι διαθÎσιμη" +no_email_address = "Λείπει η διεÏθυνση email." no_items_selected = "Δεν επιλÎχθηκαν τεκμήÏια" nohit_active_filters = "Ένα ή πεÏισσότεÏα φίλτÏα Îχουν εφαÏμοστεί σε αυτή την αναζήτηση. Αν τα φίλτÏα αναζήτησης αφαιÏεθοÏν, θα μποÏÎσετε να ανακτήσετε πεÏισσότεÏα αποτελÎσματα." nohit_change_tab = "Κάνετε αναζήτηση στην καÏÏ„Îλα "%%activeTab%%". ΜποÏεί να βÏείτε σχετικά αποτελÎσματα σε μια από τις άλλες καÏÏ„Îλες:" @@ -732,6 +759,63 @@ number_decimal_point = "," number_thousands_separator = "." OAI Server = "ΕξυπηÏετητής OAI" Occupation = "Επάγγελμα" +od_account_noaccess = "Αυτή η κάÏτα βιβλιοθήκης δεν Îχει Ï€Ïόσβαση σε πεÏιεχόμενο του Overdrive" +od_account_problem = "ΥπάÏχει Ï€Ïόβλημα με τον λογαÏιασμό σας. %%message%%" +od_audiobook-mp3 = "Ηχητικό βιβλίο MP3" +od_audiobook-overdrive = "ΑκÏόαση Î·Ï‡Î·Ï„Î¹ÎºÎ¿Ï Î²Î¹Î²Î»Î¯Î¿Ï… του OverDrive" +od_avail_avail = "Available:" +od_avail_holds = "ΚÏατήσεις:" +od_avail_total = "Συνολικά αντίτυπα:" +od_but_cancel_hold = "ΑκÏÏωση κÏάτησης" +od_but_checkout = "Δανεισμός μÎσω του Overdrive" +od_but_checkout_s = "Δανεισμός" +od_but_gettitle = "ΚατÎβασμα Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… πεÏιεχομÎνου" +od_but_gettitle_s = "ΚατÎβασμα" +od_but_hold = "ΚÏάτηση μÎσω του Overdrive" +od_but_hold_s = "Αίτημα κÏάτησης" +od_but_return = "ΕπιστÏοφή τίτλου" +od_cancel_hold = "ΑκÏÏωση κÏάτησης μÎσω του Overdrive" +od_checkout = "Δανεισμός μÎσω του Overdrive" +od_code_connection_failed = "Η σÏνδεση με το Overdrive απÎτυχε. Εάν το Ï€Ïόβλημα επιμÎνει, παÏακαλώ επικοινωνήστε με την βιβλιοθήκη σας." +od_code_contentnotavail = "Αυτό το πεÏιεχόμενο δεν είναι διαθÎσιμο στην πεÏιοχή σας." +od_code_login_for_avail = "Συνδεθείτε για να δείτε διαθεσιμότητα" +od_code_resource_not_found = "Ο τίτλος δεν βÏÎθηκε" +od_content = "ΠεÏιεχόμενο του Overdrive" +od_dl_formats = "ΔιαθÎσιμοι μοÏφότυποι για κατÎβασμα" +od_docheckout_failure = "Αυτός ο τίτλος δεν ήταν δυνατό να δανειστεί." +od_docheckout_success = "Αυτός ο τίτλος δανείστηκε σε εσάς και λήγει στις %%expireDate%%" +od_early_return = "Î Ïώιμη επιστÏοφή μÎσω του Overdrive" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "Αυτός ο τίτλος λήγει στις %%due_date%%." +od_get_title = "ΚατÎβασμα μÎσω του Overdrive" +od_gettitle_failure = "Δεν υπάÏχει δυνατότητα κατεβάσματος Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… τίτλου." +od_help_linktext = "Βοήθεια για το Overdrive" +od_history = "ΙστοÏικό του Overdrive" +od_hold = "ΚÏάτηση μÎσω του Overdrive" +od_hold_cancel_failure = "Το αίτημα ακÏÏωσης της κÏάτησης απÎτυχε. " +od_hold_cancel_success = "Η κÏάτηση ακυÏώθηκε με επιτυχία." +od_hold_email = "ΔιεÏθυνση email για ειδοποίηση σχετικά με την κÏάτηση: %%holdEmailAddress%%." +od_hold_now_avail = "Αυτή η κÏάτηση είναι διαθÎσιμη για δανεισμό. Ο δανεισμός λήγει στις %%expireDate%%." +od_hold_place_failure = "Το αίτημα κÏάτησης απÎτυχε." +od_hold_place_success = "Αυτός ο τίτλος Îχει κÏατηθεί. Η θÎση κÏάτησής σας είναι %%holdListPosition%%" +od_hold_placed_on = "Έγινε κÏάτηση στις %%holdPlacedDate%%." +od_hold_queue = "ΘÎση %%holdPosition%% σε σÏνολο %%numberOfHolds%% στην ουÏά κÏατήσεων." +od_holds = "ΚÏατήσεις του Overdrive" +od_info_unavail = "Αυτή η πληÏοφοÏία δεν είναι διαθÎσιμη." +od_is_checkedout = "Έχετε δανειστεί αυτόν τον τίτλο. ΗμεÏομηνία επιστÏοφής %%due_date%%." +od_is_on_hold = "Έχετε κÏατήσει αυτόν τον τίτλο." +od_loans = "Δανεισμοί του Overdrive" +od_mycontent_help = "Για πληÏοφοÏίες και βοήθεια σχετικά με το κατÎβασμα αυτών των τίτλων, δείτε την <a href="%%url%%">βοήθεια του Overdrive</a>." +od_none_found = "Δεν βÏÎθηκαν τίτλοι." +od_return_failure = "Αυτός ο τίτλος δεν μπόÏεσε να επιστÏαφεί." +od_return_success = "Έγινε επιστÏοφή Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… τίτλου." +od_video-streaming = "αÏχείο streaming video" of_num_results = "#%%position%% από %%total%% ΑποτελÎσματα" old_password = "ΤÏÎχων κωδικός" On Reserve = "ΔεσμευμÎνο" @@ -761,6 +845,7 @@ pagination_label = "Σελιδοποίηση" Password = "Κωδικός" Password Again = "Κωδικός ξανά" Password cannot be blank = "Ο κωδικός δεν μποÏεί να είναι κενός" +password_error_auth_old = "Ο κωδικός που εισάγατε δεν είναι ÎγκυÏος" password_error_invalid = "Ο νÎος κωδικός Ï€Ïόσβασης δεν είναι ÎγκυÏος (πχ πεÏιÎχει μή ÎγκυÏους χαÏακτήÏες)" password_error_not_unique = "Ο κωδικός δεν άλλαξε" password_maximum_length = "Ο μÎγιστος αÏιθμός χαÏακτήÏων για τον κωδικό είναι %%maxlength%%" @@ -826,6 +911,7 @@ Read the full review online... = "Διαβάστε ολόκληÏη την ÎºÏ Recall This = "Κάντε ανάκληση" recaptcha_not_passed = "ΛανθασμÎνο CAPTCHA" recently_returned_channel_title = "ΕπιστÏάφηκαν Ï€Ïόσφατα" +recommend_links_text = "ΜποÏείτε επίσης να δοκιμάσετε:" Record Citations = "ΑναφοÏÎÏ‚ εγγÏαφής" Record Count = "ΑÏιθμός εγγÏαφών" Record Type = "ΤÏπος εγγÏαφής" @@ -844,6 +930,7 @@ recovery_title = "Ανάκτηση κωδικοÏ" recovery_too_soon = "Έχουν ήδη υποβληθεί πολλά αιτήματα ανάκτησης, δοκιμάστε αÏγότεÏα" recovery_user_not_found = "Ο λογαÏιασμός σας δεν βÏÎθηκε" rectangle_center_message = "Αυτό είναι το κÎντÏο του επιλεγμÎνου πλαισίου" +Reference Material = "Υλικό ΑναφοÏάς" Refine Results = "ΠεÏιοÏισμός αποτελεσμάτων" Region = "ΧώÏα / ΠεÏιοχή" relais_available = "Το τεκμήÏιο αυτό είναι διαθÎσιμο μÎσω διαδανεισμοÏ. ΘÎλετε να το ζητήσετε;" @@ -884,6 +971,7 @@ Requests = "Αιτήσεις" Reserves = "ΚÏατήσεις" Reserves Search = "Αναζήτηση κÏατήσεων" Reserves Search Results = "ΑποτελÎσματα αναζήτησης κÏατήσεων" +reset_filters_button = "ΚατάÏγηση φίλτÏων" result_checkbox_label = "Επιλογή αποτελÎσματος με αÏιθμό %%number%%" result_count = "%%count%% αποτελÎσματα" Results = "ΑποτελÎσματα" @@ -899,6 +987,11 @@ Save Comment = "Αποθήκευση σχολίου" save_search = "Αποθήκευση αναζήτησης" save_search_remove = "ΚατάÏγηση αποθηκευμÎνης αναζήτησης" Saved in = "ΑποθηκεÏτηκε σε" +schedule_daily = "ΗμεÏησίως" +schedule_explanation = "ΠαÏαλαβή ειδοποιήσεων μÎσω email για νÎα αποτελÎσματα σχετικά με την αναζήτηση." +schedule_none = "ΧωÏίς" +schedule_weekly = "Εβδομαδιαίως" +Scheduled Alert Results = "ΑποτελÎσματα Ï€ÏογÏαμματισμÎνων ειδοποιήσεων" scholarly_limit = "Μόνο άÏθÏα επιστημονικών πεÏιοδικών" Scroll to Load More = "Εμφάνιση πεÏισσοτÎÏων Ï€Ïος τα κάτω" Search = "Αναζήτηση" @@ -913,6 +1006,7 @@ search results of = "αποτελÎσματα αναζήτησης για" Search Tips = "ΣυμβουλÎÏ‚ αναζήτησης" Search Tools = "ΕÏγαλεία αναζήτησης" Search Type = "ΤÏπος αναζήτησης" +Search within collection = "Αναζήτηση μÎσα στην συλλογή" search_AND = "όλοι οι ÏŒÏοι" search_groups = "Ομάδες φίλτÏων" search_match = "Îα πεÏιλαμβάνονται" @@ -938,6 +1032,7 @@ Sensor Image = "Εικόνα αισθητήÏα" Serial = "ΠεÏιοδική Îκδοση" Series = "ΣειÏά" Set = "ΕφαÏμογή" +show_filters_html = "Εμφάνιση φίλτÏων (%%count%%)" showing_items_html = "Εμφανίζονται <strong>%%start%% - %%end%%</strong> ΤεκμήÏια" showing_items_of_html = "Εμφανίζονται <strong>%%start%% - %%end%%</strong> από <strong>%%total%%</strong> ΤεκμήÏια" showing_results_for_html = "Εμφανίζονται <strong>%%start%% - %%end%%</strong> ΑποτελÎσματα για την αναζήτηση '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "Ξεκινήστε νÎα αναζήτηση" Start Page = "ΑÏχική" starting from = "αÏχίζοντας με τον ÏŒÏο" Status = "Κατάσταση" +status_transit = "Σε Διαμετακόμιση" status_unknown_message = "Η Ï„ÏÎχουσα κατάσταση είναι άγνωστη" Storage Retrieval Requests = "Αιτήματα ανάκτησης από αποθήκη" storage_retrieval_request_available = "ΔιαθÎσιμο" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "Η εγγÏαφή The record you selected is not part of the selected list. = "Το τεκμήÏιο που επιλÎξατε δεν υπάÏχει σε κάποια από τις λίστες σας" The system is currently unavailable due to system maintenance = "Το σÏστημα δεν είναι διαθÎσιμο για λόγους συντήÏησης" Theme = "ΘÎμα" +Thesis = "Thesis" This email was sent from = "Το email στάλθηκε από" This field is required = "Αυτό το πεδίο είναι απαÏαίτητο" This item is already part of the following list/lists = "Αυτό το τεκμήÏιο υπάÏχει ήδη σε λίστα(-ες) σας:" @@ -1090,6 +1187,9 @@ unique_tags = "ΜοναδικÎÏ‚ ετικÎτες" University Library = "Πανεπιστημιακή Βιβλιοθήκη" Unknown = "Άγνωστο" unrecognized_facet_label = "Άλλο" +unsubscribe_confirmation = "ΘÎλετε σίγουÏα να διαγÏαφεί το email σας από την λίστα ειδοποιήσεων;" +unsubscribe_description = "Δεν θÎλετε να λαμβάνετε αντίστοιχα μηνÏματα στο μÎλλον; ΔιαγÏαφείτε από την λίστα χÏησιμοποιώντας τον παÏακάτω σÏνδεσμο" +unsubscribe_successful = "Η διαγÏαφή ολοκληÏώθηκε" Upgrade VuFind = "Αναβάθμιση VuFind" upgrade_description = "Αν αναβαθμίζετε από Ï€ÏοηγοÏμενη Îκδοση του VuFind, μποÏείτε να εισάγετε τις παλιÎÏ‚ σας Ïυθμίσεις με αυτό το εÏγαλείο." URL = "ΣÏνδεσμος" @@ -1099,6 +1199,14 @@ User Account = "ΛογαÏιασμός χÏήστη" Username = "Όνομα χÏήστη" Username cannot be blank = "Το όνομα χÏήστη δεν μποÏεί να είναι κενό" Username is already in use in another library card = "Το όνομα χÏήστη χÏησιμοποιείται ήδη σε άλλη κάÏτα βιβλιοθήκης" +verification_done = "Η διεÏθυνση email σας επιβεβαιώθηκε με επιτυχία." +verification_email_change_sent = "Οδηγίες για την επιβεβαίωση του email Îχουν σταλεί στην νÎα διεÏθυνση. Απαιτείται επιβεβαίωση της νÎας διεÏθυνσης email ώστε η αλλαγή να τεθεί σε ισχÏ." +verification_email_notification = "ΔημιουÏγήθηκε αίτημα για να επιβεβαιώσει την διεÏθυνσή email του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚ στην %%library%%." +verification_email_sent = "Οδηγίες για την επιβεβαίωση του email σας Îχει σταλεί στην ηλεκτÏονική διεÏθυνση που σχετίζεται με αυτό τον λογαÏιασμό." +verification_email_subject = "Επιβεβαίωση email διεÏθυνσης του VuFind" +verification_email_url_pretext = "ΜποÏείτε να επιβεβαιώσετε το email σας ακολουθώντας τον παÏακάτω σÏνδεσμο: %%url%%" +verification_too_soon = "Απαιτείται επιβεβαίωση του email σας. Σας Îχει σταλεί σχετικό μήνυμα στην ηλεκτÏονική διεÏθυνση του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚. Εάν δεν το λάβατε, παÏακαλώ πεÏιμÎνετε λίγη ÏŽÏα και Ï€Ïοσπαθήστε ξανά." +verification_user_not_found = "Δεν ήταν δυνατή η εÏÏεση του λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï ÏƒÎ±Ï‚" VHS = "VHS" Video = "Βίντεο" Video Clips = "Βιντεοκλιπ" diff --git a/languages/en-gb.ini b/languages/en-gb.ini index 2db8ed142743162dd09e7b06730153cabcbd2dec..99dec0fee0903669f3452be7599b4bf4dc522f74 100644 --- a/languages/en-gb.ini +++ b/languages/en-gb.ini @@ -37,6 +37,9 @@ hold_error_fail = "Your request failed. Please contact the issue desk for furthe hold_place_fail_missing = "Your request failed. Some data was missing. Please contact the issue desk for further assistance" ill_request_available = "Available for Collection" ill_request_cancel_fail = "Your request was not cancelled. Please contact the issue desk for further assistance" +ill_request_cancel_success = "Your request was successfully cancelled" +ill_request_cancel_success_items = "%%count%% request(s) were successfully cancelled" +ill_request_canceled = "Cancelled" ill_request_error_fail = "Your request failed. Please contact the issue desk for further assistance" ill_request_error_technical = "Your request failed due to a system error. Please contact the issue desk for further assistance" ill_request_place_fail_missing = "Your request failed. Some data was missing. Please contact the issue desk for further assistance" @@ -52,13 +55,17 @@ Library Catalog Username = "Library Catalogue Username" More catalog results = "More catalogue results" Multiple Call Numbers = "Multiple Classmarks" My Favorites = "My Favourites" +od_hold_cancel_success = "The hold was cancelled successfully." pick_up_location = "Collection Location" sort_callnumber = "Classmark" storage_retrieval_request_available = "Available for Collection" storage_retrieval_request_cancel_fail = "Your request was not cancelled. Please contact the issue desk for further assistance" +storage_retrieval_request_cancel_success = "Your request was successfully cancelled" +storage_retrieval_request_cancel_success_items = "%%count%% request(s) were successfully cancelled" +storage_retrieval_request_canceled = "Cancelled" storage_retrieval_request_error_fail = "Your request failed. Please contact the issue desk for further assistance" storage_retrieval_request_place_fail_missing = "Your request failed. Some data was missing. Please contact the issue desk for further assistance" storage_retrieval_request_profile_html = "For storage retrieval request information, please establish your <a href="%%url%%">Library Catalogue Profile</a>." too_many_favorites = "This list is too large to display all at once. Try rearranging your favourites into more lists or limiting using tags." -You do not have any saved resources = "You do not have any saved resources. Perform a search and use the Add to Favourites button to save items." +unsubscribe_successful = "Subscription cancelled" Your Favorites = "Your Favourites" diff --git a/languages/en.ini b/languages/en.ini index 3859e1f8d513766b8cefc68cfa726a39d90a25cf..defc6d49c91a5c6f07e71a7536d527389e8b928f 100644 --- a/languages/en.ini +++ b/languages/en.ini @@ -50,6 +50,7 @@ advSearchError_notFound = "The search you have requested was not found." ajax_load_interrupted = "Loading interrupted" ajaxview_label_information = "Information" ajaxview_label_tools = "Tools" +alert_email_address = "Scheduled alert results will be sent to email address" All = "All" All Fields = "All Fields" All Pages Loaded = "All Pages Loaded" @@ -59,6 +60,7 @@ alphabrowselink_html = "Browse entries by %%index%% starting from <a href="%%url An error has occurred = "An error has occurred" An error occurred during execution; please try again later. = "An error occurred during execution; please try again later." AND = "AND" +and = "and" anonymous_tags = "Anonymous Tags" APA Citation = "APA Citation" applied_filter = "Applied Filter" @@ -71,6 +73,8 @@ authentication_error_admin = "We cannot log you in at this time. Please contact authentication_error_blank = "Login information cannot be blank." authentication_error_creation_blocked = "You do not have permission to create an account." authentication_error_denied = "Credentials do not match! Access denied." +authentication_error_email_not_verified_html = "Your email address has not been verified yet. Please check your spam filter for the verification message. If necessary, we can <a href="%%url%%">Resend the Verification Email</a>." +authentication_error_in_progress = "Authentication request is already being processed. Please try again later if you need to start over." authentication_error_invalid = "Invalid login -- please try again." authentication_error_loggedout = "You have logged out." authentication_error_technical = "We cannot log you in at this time. Please try again later." @@ -96,7 +100,6 @@ Backtrace = "Backtrace" Bag = "Bag" Balance = "Balance" Barcode = "Barcode" -basic_search_keep_filters = "Retain current filters" Be the first to leave a comment = "Be the first to leave a comment" Be the first to tag this record = "Be the first to tag this record" Bibliographic Details = "Bibliographic Details" @@ -166,6 +169,7 @@ Call Number = "Call Number" callnumber_abbrev = "Call #" Cannot find record = "Cannot find record" Cannot find similar records = "Cannot find similar records" +cannot set = "Cannot set" Cassette = "Cassette" cat_establish_account = "In order to establish your account profile, please enter the following information:" cat_password_abbrev = "Catalog Password" @@ -174,7 +178,12 @@ Catalog Login = "Catalog Login" Catalog Results = "Catalog Results" catalog_login_desc = "Enter your library catalog credentials." CD = "CD" +Change Email Address = "Change Email Address" Change Password = "Change Password" +change_email_disabled = "You are not allowed to change your email address at this time" +change_email_verification_reminder = "Submitting this form will send an email to the new address; you will have to click on a link in the email before the change will take effect." +change_notification_email_message = "A request was just made to change your email address at %%library%%. If you did not initiate this request, you may wish to log in at %%url%% and confirm the integrity of your account. Please contact support at %%email%% if you have questions or concerns." +change_notification_email_subject = "Account Email Change Notification" channel_add_more = "Add more channels like this" channel_browse = "Browse more records" channel_expand = "Explore related channels" @@ -183,6 +192,7 @@ channel_search = "Show items as search results" channel_searchbox_label = "Search for more channels:" Check Hold = "Check Hold" Check Recall = "Check Recall" +check_profile = "Check user information." Checked Out = "Checked Out" Checked Out Items = "Checked Out Items" Checkedout = "Checked Out" @@ -216,6 +226,7 @@ comment_error_load = "Error: Could Not Redraw Comment List" comment_error_save = "Error: Could Not Save Comment" Comments = "Comments" Company/Entity = "Company/Entity" +Conference Proceeding = "Conference Proceeding" Configuration = "Configuration" confirm_delete = "Are you sure you want to delete this?" confirm_delete_brief = "Delete Item?" @@ -296,6 +307,7 @@ Due Date = "Due Date" DVD = "DVD" eBook = "eBook" Edit = "Edit" +edit = "edit" Edit Library Card = "Edit Library Card" Edit this Advanced Search = "Edit this Advanced Search" edit_list = "Edit List" @@ -324,8 +336,14 @@ Email address is invalid = "Email address is invalid" Email Record = "Email Record" Email this = "Email this" Email this Search = "Email this Search" +email_change_pending_html = "You have a pending email change to %%pending%%. Please click the link in the verification email sent to this address to complete the change. If necessary, we can <a href="%%url%%">Resend the Verification Email</a>." email_failure = "Error - Message Cannot Be Sent" email_link = "Link" +email_login_desc = "Please use the following link to log in. If you did not initiate login, you may safely ignore this message. Please note that the link is only valid for a limited time and only with the device you used to enter the email address." +email_login_link = "Link to login: <%%url%%>" +email_login_link_sent = "We have sent a login link to your email address. It may take a few moments for the link to arrive. If you don't receive the link shortly, please check also your spam filter." +email_login_requested = "Login has been requested with your email address at %%title%%." +email_login_subject = "Login to %%title%%" email_maximum_recipients_note = "At most %%max%% recipients are allowed." email_multiple_recipients_note = "You may specify multiple recipients separated by commas." email_selected = "Email Selected" @@ -335,6 +353,7 @@ email_subject = "Subject" email_success = "Message Sent" Empty = "Empty" Empty Book Bag = "Empty Book Bag" +empty_search_disallowed = "An empty query is not allowed with the current search target" Enable Auto Config = "Enable Auto Config" End Page = "End Page" Era = "Era" @@ -387,12 +406,13 @@ Favorites = "Saved Items" Fee = "Fee" Feedback = "Feedback" feedback_email = "Email" -feedback_login_required = "You must be logged in first." feedback_name = "Name" Field of activity = "Field of activity" File Description = "File Description" Filter = "Filter" +Filter Collection = "Filter collection" filter_tags = "Filter Tags" +filter_toggle_entries = "%%count%% filters" filter_wildcard = "Any" Find = "Find" Find More = "Find More" @@ -428,6 +448,7 @@ Go to Standard View = "Go to Standard View" go_to_list = "Go to List" google_map_cluster = "Cluster" google_map_cluster_points = "Cluster Points" +Government Document = "Government Document" Grid = "Grid" Group = "Group" group_AND = "ALL Groups" @@ -454,6 +475,7 @@ history_results = "Results" history_save = "Save?" history_save_link = "Save" history_saved_searches = "Saved Searches" +history_schedule = "Alert schedule" history_search = "Search" history_time = "Time" hold_available = "Available for Pickup" @@ -600,6 +622,7 @@ Library Web Search = "Library Web Search" library_card_edit_password_placeholder = "New Password" lightbox_error = "Error: Cannot Load Popup Box" Limit To = "Limit To" +Link to full results = "Link to full results" List = "List" List Tags = "List Tags" list_access_denied = "You do not have permission to view this list." @@ -663,9 +686,12 @@ New Item Feed = "New Item Feed" New Item Search = "New Item Search" New Item Search Results = "New Item Search Results" New Items = "New Items" +New results found for search = "New results found for search" New Title = "New Title" +new_email_success = "Your email address has been changed successfully" new_password = "New Password" new_password_success = "Your password has successfully been changed" +new_results_heading = "%%count%% newest results" new_user_welcome_subject = "Your new account at %%library%%" new_user_welcome_text = "Welcome to %%library%%. A new account has been opened for %%firstname%% %%lastname%%. Your username is %%username%%. Please set a password on this page: %%url%%" Newspaper = "Newspaper" @@ -681,6 +707,7 @@ No Preference = "No Preference" No reviews were found for this record = "No reviews were found for this record" No Tags = "No Tags" no_description = "Description not available." +no_email_address = "Email address missing." no_items_selected = "No Items were Selected" nohit_active_filters = "One or more facet filters have been applied to this search. If you remove filters, you may retrieve more results." nohit_change_tab = "You have been searching in the "%%activeTab%%" tab. You may find something in one of the other tabs:" @@ -732,6 +759,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "OAI Server" Occupation = "Occupation" +od_account_noaccess = "This library card does not have access to content in Overdrive" +od_account_problem = "There is a problem with your account. %%message%%" +od_audiobook-mp3 = "MP3 audiobook" +od_audiobook-overdrive = "OverDrive Listen audiobook" +od_avail_avail = "Available:" +od_avail_holds = "Holds:" +od_avail_total = "Total Copies:" +od_but_cancel_hold = "Cancel this hold" +od_but_checkout = "Checkout Through Overdrive" +od_but_checkout_s = "Checkout" +od_but_gettitle = "Download this content" +od_but_gettitle_s = "Download" +od_but_hold = "Place A Hold Through Overdrive" +od_but_hold_s = "Place Hold" +od_but_return = "Return this title" +od_cancel_hold = "Cancel Overdrive Hold" +od_checkout = "Overdrive Checkout" +od_code_connection_failed = "Connection to Overdrive failed. If the problem persists, please contact your library." +od_code_contentnotavail = "This content is not available in your area." +od_code_login_for_avail = "Login for availability" +od_code_resource_not_found = "Title not found" +od_content = "Overdrive Content" +od_dl_formats = "Supported Download Formats" +od_docheckout_failure = "This title could not be checked out." +od_docheckout_success = "This title has been checked out to you. It expires on %%expireDate%%" +od_early_return = "Overdrive Early Return" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "This title expires on %%due_date%%." +od_get_title = "Overdrive Download" +od_gettitle_failure = "This title could not be downloaded." +od_help_linktext = "Overdrive Help" +od_history = "Overdrive History" +od_hold = "Overdrive Hold" +od_hold_cancel_failure = "The hold cancellation request failed. " +od_hold_cancel_success = "The hold was canceled successfully." +od_hold_email = "Email Address for hold notification: %%holdEmailAddress%%." +od_hold_now_avail = "This hold is available for checkout. Checkout expires on %%expireDate%%." +od_hold_place_failure = "The hold request failed." +od_hold_place_success = "This title has been placed on hold. Your hold position is %%holdListPosition%%" +od_hold_placed_on = "Hold placed on %%holdPlacedDate%%." +od_hold_queue = "Position %%holdPosition%% of %%numberOfHolds%% in holds queue." +od_holds = "Overdrive Holds" +od_info_unavail = "This information is currently unavailable." +od_is_checkedout = "You have this title checked out. It's due on %%due_date%%." +od_is_on_hold = "You have this title on hold." +od_loans = "Overdrive Loans" +od_mycontent_help = "For information about and help with downloading these titles, see <a href="%%url%%">Overdrive Help</a>." +od_none_found = "No titles found." +od_return_failure = "This title could not be returned." +od_return_success = "This title has been returned." +od_video-streaming = "streaming video file" of_num_results = "#%%position%% of %%total%% results" old_password = "Old Password" On Reserve = "On Reserve" @@ -761,6 +845,7 @@ pagination_label = "Pagination" Password = "Password" Password Again = "Password Again" Password cannot be blank = "Password cannot be blank" +password_error_auth_old = "The previously used password is invalid" password_error_invalid = "New password is invalid (e.g. contains invalid characters)" password_error_not_unique = "Password was not changed" password_maximum_length = "Maximum password length is %%maxlength%% characters" @@ -826,6 +911,7 @@ Read the full review online... = "Read the full review online..." Recall This = "Recall This" recaptcha_not_passed = "CAPTCHA not passed" recently_returned_channel_title = "Recently Returned" +recommend_links_text = "You can also try:" Record Citations = "Record Citations" Record Count = "Record Count" Record Type = "Record Type" @@ -844,6 +930,7 @@ recovery_title = "Password Recovery" recovery_too_soon = "Too many recovery requests have been made, please try again later" recovery_user_not_found = "We could not find your account" rectangle_center_message = "This is the center point for the highlighted rectangle" +Reference Material = "Reference Material" Refine Results = "Refine Results" Region = "Region" relais_available = "This item is available through Interlibrary Loan. Would you like to request it?" @@ -884,6 +971,7 @@ Requests = "Requests" Reserves = "Reserves" Reserves Search = "Reserves Search" Reserves Search Results = "Reserves Search Results" +reset_filters_button = "Reset Filters" result_checkbox_label = "Select result number %%number%%" result_count = "%%count%% results" Results = "Results" @@ -899,6 +987,11 @@ Save Comment = "Save Comment" save_search = "Save Search" save_search_remove = "Remove Saved Search" Saved in = "Saved in" +schedule_daily = "Daily" +schedule_explanation = "Receive alert emails about new results for search." +schedule_none = "None" +schedule_weekly = "Weekly" +Scheduled Alert Results = "Scheduled alert results" scholarly_limit = "Limit to articles from scholarly journals" Scroll to Load More = "Scroll to Load More" Search = "Search" @@ -913,6 +1006,7 @@ search results of = "search results of" Search Tips = "Search Tips" Search Tools = "Search Tools" Search Type = "Search Type" +Search within collection = "Search within collection" search_AND = "ALL Terms" search_groups = "Search Groups" search_match = "Match" @@ -938,6 +1032,7 @@ Sensor Image = "Sensor Image" Serial = "Serial" Series = "Series" Set = "Set" +show_filters_html = "Show filters (%%count%%)" showing_items_html = "Showing <strong>%%start%% - %%end%%</strong> Items" showing_items_of_html = "Showing <strong>%%start%% - %%end%%</strong> of <strong>%%total%%</strong> Items" showing_results_for_html = "Showing <strong>%%start%% - %%end%%</strong> results for search '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "Start a new Basic Search" Start Page = "Start Page" starting from = "starting from" Status = "Status" +status_transit = "In Transit" status_unknown_message = "Live Status Unavailable" Storage Retrieval Requests = "Storage Retrieval Requests" storage_retrieval_request_available = "Available for Pickup" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "The record you sele The record you selected is not part of the selected list. = "The record you selected is not part of the selected list." The system is currently unavailable due to system maintenance = "The system is currently unavailable due to system maintenance" Theme = "Theme" +Thesis = "Thesis" This email was sent from = "This email was sent from" This field is required = "This field is required" This item is already part of the following list/lists = "This item is already part of the following list/lists" @@ -1090,6 +1187,9 @@ unique_tags = "Unique Tags" University Library = "University Library" Unknown = "Unknown" unrecognized_facet_label = "Other" +unsubscribe_confirmation = "Do you want to cancel the email subscription?" +unsubscribe_description = "Don't want to receive this message in the future? Cancel the subscription by using the following link" +unsubscribe_successful = "Subscription canceled" Upgrade VuFind = "Upgrade VuFind" upgrade_description = "If you are upgrading a previous VuFind version, you can load your old settings with this tool." URL = "URL" @@ -1099,6 +1199,14 @@ User Account = "User Account" Username = "Username" Username cannot be blank = "Username cannot be blank" Username is already in use in another library card = "Username is already in use in another library card" +verification_done = "Your email address has been verified successfully." +verification_email_change_sent = "Email address verification instructions have been sent to the new email address. You must verify the address before the change will take effect." +verification_email_notification = "A request was just made to verify your email address for your account with %%library%%." +verification_email_sent = "Email address verification instructions have been sent to the email address registered with this account." +verification_email_subject = "VuFind Email Verification" +verification_email_url_pretext = "You can verify your email address at this URL: %%url%%" +verification_too_soon = "Your email requires validation. An email was recently sent to your registered email address. If you did not receive it, please wait a few minutes and try again." +verification_user_not_found = "We could not find your account" VHS = "VHS" Video = "Video" Video Clips = "Video Clips" @@ -1135,7 +1243,7 @@ You do not have any holds or recalls placed = "You do not have any holds or reca You do not have any interlibrary loan requests placed = "You do not have any interlibrary loan requests placed" You do not have any items checked out = "You do not have any items checked out" You do not have any library cards = "You do not have any library cards" -You do not have any saved resources = "You do not have any saved resources. Perform a search and use the Add to Favorites button to save items." +You do not have any saved resources = "You do not have any saved resources. Perform a search and use the Save to List button to save items." You do not have any storage retrieval requests placed = "You do not have any storage retrieval requests placed" You must be logged in first = "You must be logged in first" Your Account = "Your Account" diff --git a/languages/es.ini b/languages/es.ini index a5cfc97bc0977c48a578fc18337a190e7d8e6125..6865cbd9dcdca1dda8b90b67e09ead42668225fc 100644 --- a/languages/es.ini +++ b/languages/es.ini @@ -51,6 +51,7 @@ advSearchError_notFound = "No se ha encontrado la búsqueda solicitada." ajax_load_interrupted = "Carga interrumpida" ajaxview_label_information = "Información" ajaxview_label_tools = "Herramientas" +alert_email_address = "Los resultados de las alertas programadas se enviarán a la dirección de correo electrónico" All = "Todo" All Fields = "Todos los Campos" All Pages Loaded = "Todas las páginas cargadas" @@ -60,6 +61,7 @@ alphabrowselink_html = "Navegar por las entradas %%index%% iniciando desde <a hr An error has occurred = "Ha ocurrido un error" An error occurred during execution; please try again later. = "Un error ha ocurrido durante la ejecución; por favor intente más tarde" AND = "Y" +and = "y" anonymous_tags = "Etiquetas Anónimas" APA Citation = "Cita APA" applied_filter = "Filtro Aplicado" @@ -72,6 +74,8 @@ authentication_error_admin = "No podemos iniciar su sesión en este momento. Por authentication_error_blank = "La información para su Ingreso no pueden estar vacÃos." authentication_error_creation_blocked = "No tiene permisos para crear una cuenta." authentication_error_denied = "Las Credenciales no coinciden! Acceso negado." +authentication_error_email_not_verified_html = "Su dirección de correo electrónico todavÃa no se ha verificado. Revise su filtro de correo no deseado para ver el mensaje de verificación. Si es necesario, podemos <a href="%%url%%"> Reenviar el correo electrónico de verificación </a>." +authentication_error_in_progress = "La solicitud de autenticación ya se está procesando. Intente nuevamente más tarde si necesita comenzar de nuevo." authentication_error_invalid = "Ingreso inválido -- por favor intente nuevamente." authentication_error_loggedout = "Ha cerrado la sesión." authentication_error_technical = "No podemos ingresarlo ahora. Por favor inténtelo más tarde." @@ -97,7 +101,6 @@ Backtrace = "Rastrear" Bag = "Bolsa" Balance = "Balance" Barcode = "Código de Barras" -basic_search_keep_filters = "retener mis filtros" Be the first to leave a comment = "Sea el primero en dejar un comentario" Be the first to tag this record = "Sea el primero en etiquetar este registro" Bibliographic Details = "Detalles Bibliográficos" @@ -167,6 +170,7 @@ Call Number = "Número de Clasificación" callnumber_abbrev = "No. Clasificación" Cannot find record = "Registro no encontrado" Cannot find similar records = "No se encontraron registros similares" +cannot set = "No se puede establecer" Cassette = "Cassette" cat_establish_account = "A fin de establecer el perfil de su cuenta, por favor ingrese lo siguiente:" cat_password_abbrev = "Clave" @@ -175,7 +179,12 @@ Catalog Login = "Ingreso al Catálogo" Catalog Results = "Resultados" catalog_login_desc = "Introduzca sus credenciales del catálogo de la biblioteca." CD = "CD" +Change Email Address = "Cambiar dirección de correo electrónico" Change Password = "Cambiar Contraseña" +change_email_disabled = "No está permitido cambiar su dirección de correo electrónico en este momento" +change_email_verification_reminder = "Al enviar este formulario, se enviará un correo electrónico a la nueva dirección; deberá hacer clic en enlace para que el cambio surta efecto." +change_notification_email_message = "Se acaba de hacer una solicitud para cambiar su dirección de correo electrónico en %%library%%. Si no inició esta solicitud, puede iniciar sesión en %%url%% y confirmar veracidad. Si tiene preguntas o inquietudes, comunÃquese con el soporte al %%email%%." +change_notification_email_subject = "Notificación de cambio de cuenta de correo correo electrónico" channel_add_more = "Añadir más canales como este" channel_browse = "Buscar más registros" channel_expand = "Explorar canales relacionados" @@ -184,6 +193,7 @@ channel_search = "Mostrar Ãtemes como resultados de búsqueda" channel_searchbox_label = "Buscar más canales:" Check Hold = "Comprobar Reserva" Check Recall = "Comprobar Solicitud" +check_profile = "Verifique la información del usuario." Checked Out = "Prestado" Checked Out Items = "Copias Prestadas" Checkedout = "Prestados" @@ -217,6 +227,7 @@ comment_error_load = "Error: no podemos visualizar comentario" comment_error_save = "Error: no podemos guardar comentario" Comments = "Comentarios" Company/Entity = "Empresa/Entidad" +Conference Proceeding = "Procedimiento de la Conferencia" Configuration = "Configuración" confirm_delete = "¿Seguro que desea borrar esto?" confirm_delete_brief = "¿Borrar elemento?" @@ -297,6 +308,7 @@ Due Date = "Fecha de Vencimiento" DVD = "DVD" eBook = "eBook" Edit = "Editar" +edit = "editar" Edit Library Card = "Editar Tarjeta de la Biblioteca" Edit this Advanced Search = "Editar esta Búsqueda Avanzada" edit_list = "Editar Lista" @@ -325,8 +337,14 @@ Email address is invalid = "Dirección de Correo electrónico no es válida" Email Record = "Enviar Registro por Correo electrónico" Email this = "Enviar este por Correo electrónico" Email this Search = "Enviar por Correo electrónico esta Búsqueda" +email_change_pending_html = "You have a pending email change to %%pending%%. Please click the link in the verification email sent to this address to complete the change. If necessary, we can <a href="%%url%%">Resend the Verification Email</a>." "Tiene un cambio de correo electrónico pendiente a %%pendiente%%. Haga clic en el enlace de verificación enviado a esta dirección para completar el cambio. Si es necesario, podemos <a href="%%url%%"> reenviar el correo electrónico de verificación </a>." email_failure = "Error - El mensaje no puede ser enviado" email_link = "Liga" +email_login_desc = "Utilice el siguiente enlace para iniciar sesión. Si no inició sesión, puede ignorar este mensaje de forma segura. Tenga en cuenta que el enlace solo es válido por un tiempo limitado y solo con el dispositivo que utilizó para ingresar la dirección de correo electrónico." +email_login_link = "Enlace para iniciar sesión: <%%url%%>" +email_login_link_sent = "Hemos enviado un enlace de inicio de sesión a su dirección de correo electrónico. El enlace puede tardar unos minutos en llegar. Si no recibe el enlace en breve, verifique también su filtro de spam." +email_login_requested = "Se ha solicitado iniciar sesión con su dirección de correo electrónico en %%title%%." +email_login_subject = "Inicie sesión en %%title%%" email_maximum_recipients_note = "A lo sumo %%max%% destinatarios están permitidos" email_multiple_recipients_note = "Puede especificar varios destinatarios separados por comas" email_selected = "Enviar por correo electrónico lo seleccionado" @@ -336,6 +354,7 @@ email_subject = "Materia" email_success = "Mensaje Enviado" Empty = "VacÃo" Empty Book Bag = "Bolsa para libros vacÃa" +empty_search_disallowed = "No se permite una consulta vacÃa en la búsqueda actual" Enable Auto Config = "Habilitar auto configuración" End Page = "Fin de Página" Era = "Época" @@ -388,12 +407,13 @@ Favorites = "Favoritos" Fee = "Cuota" Feedback = "Comentarios" feedback_email = "Correo Electrónico" -feedback_login_required = "Primero debe ingresar al sistema." feedback_name = "Nombre" Field of activity = "Campo de actividad" File Description = "Descripción del Archivo" Filter = "Filtro" +Filter Collection = "Filtros de colección" filter_tags = "Filtro de Etiquetas" +filter_toggle_entries = "%%count%% filtros" filter_wildcard = "Cualquier" Find = "Buscar" Find More = "Buscar Más" @@ -429,6 +449,7 @@ Go to Standard View = "ir a Vista Normal" go_to_list = "Ir a la Lista" google_map_cluster = "Grupo" google_map_cluster_points = "Puntos de agrupamiento" +Government Document = "Documento de Gobierno" Grid = "CuadrÃcula" Group = "Grupo" group_AND = "Todos los Grupos" @@ -455,6 +476,7 @@ history_results = "Resultados" history_save = "¿Guardar?" history_save_link = "Guardar" history_saved_searches = "Sus Búsquedas Guardadas" +history_schedule = "Programar alerta" history_search = "Buscar" history_time = "Tiempo" hold_available = "Disponible para llevar" @@ -601,6 +623,7 @@ Library Web Search = "Búsqueda en internet de la Biblioteca" library_card_edit_password_placeholder = "Nueva Contraseña" lightbox_error = "Error: No se puede cargar el cuadro de diálogo emergente" Limit To = "Limitar" +Link to full results = "Enlace a resultados completos" List = "Lista" List Tags = "Lista de Etiquetas" list_access_denied = "Sin permiso para ver esta lista" @@ -663,9 +686,12 @@ New Item Feed = "Nuevo elemento agregado" New Item Search = "Nuevo ejemplar encontrado" New Item Search Results = "Resultado de Búsqueda de Nuevo ejemplar" New Items = "Nuevos ejemplares" +New results found for search = "Nuevos resultados encontrados para la búsqueda" New Title = "Nuevo TÃtulo" +new_email_success = "Su dirección de correo electrónico ha sido modificada con éxito." new_password = "Nueva Contraseña" new_password_success = "Su contraseña ha sido cambiada con éxito" +new_results_heading = "%%count%% resultados más recientes" new_user_welcome_subject = "Su nueva cuenta en %%library%%" new_user_welcome_text = "Bienvenido a %%library%%. Se ha abierto una nueva cuenta para %%firstname%% %%lastname%%. Tu nombre de usuario es %%username%%. Establezca una contraseña en esta página: %%url%%" Newspaper = "Periódico" @@ -681,6 +707,7 @@ No Preference = "Sin Preferencia" No reviews were found for this record = "No se encontraron crÃticas para este registro" No Tags = "Sin Etiquetas" no_description = "Descripción no disponible." +no_email_address = "Falta dirección de correo electrónico." no_items_selected = "No se han seleccionado elementos" nohit_active_filters = "Uno o más filtros de faceta se han aplicado a esta búsqueda. Si remueve filtros, es posible recuperar más resultados." nohit_change_tab = "Ha estado buscando en %%activeTab%% tab. PodrÃa encontrar algo en uno de los otros tabs:" @@ -732,6 +759,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "Servidor OAI" Occupation = "Ocupación" +od_account_noaccess = "Esta tarjeta de biblioteca no tiene acceso al contenido en Overdrive" +od_account_problem = "Hay un problema con su cuenta. %%message%%" +od_audiobook-mp3 = "MP3 audiolibro" +od_audiobook-overdrive = "Escuchar audiolibro OverDrive" +od_avail_avail = "Disponible" +od_avail_holds = "Apartados" +od_avail_total = "Total de copias" +od_but_cancel_hold = "Cancelar este apartado" +od_but_checkout = "Checkout Through Overdrive" +od_but_checkout_s = "Préstamos" +od_but_gettitle = "descargar este contenido" +od_but_gettitle_s = "Descargas" +od_but_hold = "Coloque una resrva a través de Overdrive" +od_but_hold_s = "Poner una resrva" +od_but_return = "Devolver este tÃtulo" +od_cancel_hold = "Cancelar reserva de Overdrive" +od_checkout = "Préstanmo de OverDrive" +od_code_connection_failed = "Falló conexión con Overdrive. Si el problema persiste, comunÃquese con su biblioteca." +od_code_contentnotavail = "Este contenido no está disponible en su área." +od_code_login_for_avail = "Login para disponibilidad" +od_code_resource_not_found = "TÃtulo no encontrado" +od_content = "Contenido de Overdrive" +od_dl_formats = "Formatos de descarga soportados" +od_docheckout_failure = "Este tÃtulo no pudo ser prestado." +od_docheckout_success = "Este tÃtulo te ha sido prestado. Vence el %%expireDate%%" +od_early_return = "OverDrive devolución temprana" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "Este tÃtulo expira en %%due_date%%." +od_get_title = "Descargas Overdrive" +od_gettitle_failure = "Este tÃtulo no se pudo descargar." +od_help_linktext = "Ayuda Overdrive" +od_history = "Historia Overdrive" +od_hold = "Apartado Overdrive" +od_hold_cancel_failure = "Falló la solicitud de cancelación de reserva." +od_hold_cancel_success = "La resrva se ha cancelado exitosamente." +od_hold_email = "Dirección de correo electrónico para la notificación de resrva: %%holdEmailAddress%%." +od_hold_now_avail = "Esta reserva está disponible para préstamo. El préstamo expira el %%expireDate%%." +od_hold_place_failure = "La solicitud de reserva falló." +od_hold_place_success = "Este tÃtulo se ha puesto en reserva. Su posición de reserva es %%holdListPosition%%" +od_hold_placed_on = "Reserva puesta en %%holdPlacedDate%%." +od_hold_queue = "Posición %%holdPosition%% de %%numberOfHolds%% en la lista de reservas." +od_holds = "Reservas Overdrive" +od_info_unavail = "Esta información no está disponible actualmente." +od_is_checkedout = "Tiene este tÃtulo en préstamo. Vence el %%due_date%%." +od_is_on_hold = "Usted tiene este tÃtulo en reserva." +od_loans = "Préstamos Overdrive" +od_mycontent_help = "For information about and help with downloading these titles, see <a href="%%url%%">Overdrive Help</a>." "Para obtener información sobre y ayuda con la descarga de estos tÃtulos, consulte <a href="%%url%%"> Overdrive Help </a>." +od_none_found = "No se encontraron tÃtulos." +od_return_failure = "Este tÃtulo no pudo ser devuelto." +od_return_success = "Este tÃtulo ha sido devuelto." +od_video-streaming = "archivo de video streaming" of_num_results = "#%%position%% de %%total%% Resultados" old_password = "Contraseña anterior" On Reserve = "En Reserva" @@ -761,6 +845,7 @@ pagination_label = "Paginación" Password = "Contraseña" Password Again = "Nuevamente contraseña" Password cannot be blank = "La contraseña no puede quedar en blanco" +password_error_auth_old = "La contraseña utilizada anteriormente no es válida" password_error_invalid = "Nueva contraseña es inválida (p.e. contiene caracteres no válidos)" password_error_not_unique = "La contraseña no se ha cambiado" password_maximum_length = "Longitud máxima de la contraeña son %%maxlength%% caracteres" @@ -826,6 +911,7 @@ Read the full review online... = "Leer en lÃnea la crÃtica completa..." Recall This = "Recordar esto" recaptcha_not_passed = "CAPTCHA inválida" recently_returned_channel_title = "Recientemente devuelto" +recommend_links_text = "También puede probar:" Record Citations = "Registro de Citas" Record Count = "Conteo de registro" Record Type = "Tipo de registro" @@ -844,6 +930,7 @@ recovery_title = "Contraseña recuperada" recovery_too_soon = "Se han realizado demasiadas solicitudes de recuperación, por favor inténtelo más tarde" recovery_user_not_found = "No hemos podido encontrar su cuenta" rectangle_center_message = "Este es el punto central del rectángulo resaltado" +Reference Material = "Material de referencia" Refine Results = "Limitar resultados" Region = "Región" relais_available = "Este Ãtem está disponible a través de préstamo interbibliotecario. ¿Te gustarÃa solicitarlo?" @@ -884,6 +971,7 @@ Requests = "Solicitar" Reserves = "Reservas" Reserves Search = "Buscar Reservas" Reserves Search Results = "Resultado de Búsquedas de Reservas" +reset_filters_button = "Restablecer filtros" result_checkbox_label = "Seleccione el número de resultado %%number%%" result_count = "%%count%% resultados" Results = "Resultados" @@ -899,6 +987,11 @@ Save Comment = "Guardar comentario" save_search = "Guardar Búsqueda" save_search_remove = "Borrar Búsquedas guardadas" Saved in = "Guardado en" +schedule_daily = "Diario" +schedule_explanation = "Reciba correos electrónicos de alerta sobre nuevos resultados para la búsqueda." +schedule_none = "Ninguno" +schedule_weekly = "Semanal" +Scheduled Alert Results = "Resultados de alertas programadas" scholarly_limit = "Limitar los artÃculos por campo académico" Scroll to Load More = "Deslizar para ver más" Search = "Buscar" @@ -913,6 +1006,7 @@ search results of = "Resultados de búsqueda de" Search Tips = "Consejos de búsqueda" Search Tools = "Herramientas de búsqueda" Search Type = "Tipo de búsqueda" +Search within collection = "Buscar dentro de la colección" search_AND = "Todos los Términos" search_groups = "Grupo de Búsqueda" search_match = "Coincide" @@ -938,6 +1032,7 @@ Sensor Image = "Sensor de imagen" Serial = "Seriadas" Series = "Series" Set = "Ingresar" +show_filters_html = "Mostrar filtros (%%count%%)" showing_items_html = "Mostrando <strong>%%start%% - %%end%%</strong> Elementos" showing_items_of_html = "Mostrando <strong>%%start%% - %%end%%</strong> de <strong>%%total%%</strong> Elementos" showing_results_for_html = "Mostrando <strong>%%start%% - %%end%%</strong> Resultados Para Buscar '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "Iniciar nueva Búsqueda Básica" Start Page = "Página de inicio" starting from = "iniciando desde" Status = "Estado" +status_transit = "En Transito" status_unknown_message = "Estatus de actividad no disponible" Storage Retrieval Requests = "Solicitudes de recuperación de almacenamiento" storage_retrieval_request_available = "Disponible para llevar" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "El registro selecci The record you selected is not part of the selected list. = "El registro seleccionado no es parte de la lista." The system is currently unavailable due to system maintenance = "El sistema no está disponible por razones de mantenimiento." Theme = "Tema" +Thesis = "Tesis" This email was sent from = "Este correo electrónico fue enviado desde" This field is required = "Este campo es requerido" This item is already part of the following list/lists = "Esta copia es parte de las siguientes listas" @@ -1090,6 +1187,9 @@ unique_tags = "Etiquetas únicas" University Library = "Biblioteca Universitaria" Unknown = "Desconocido" unrecognized_facet_label = "Otro" +unsubscribe_confirmation = "Quiere cancelar la suscripción de correo electrónico?" +unsubscribe_description = "¿No quieres recibir este mensaje en el futuro? Cancele la suscripción utilizando el siguiente enlace" +unsubscribe_successful = "Suscripción cancelada" Upgrade VuFind = "Actualizar VuFind" upgrade_description = "Si va a actualizar desde una versión previa de VuFind, podrá cargar su configuración anterior con esta herramienta." URL = "URL" @@ -1099,6 +1199,14 @@ User Account = "Cuenta de usuario" Username = "Usuario" Username cannot be blank = "Nombre de usuario no puede estar vacÃo" Username is already in use in another library card = "El nombre de usuario ya está usado por otra trajeta de la biblioteca" +verification_done = "Su dirección de correo electrónico ha sido verificada con éxito." +verification_email_change_sent = "Las instrucciones de verificación de la dirección de correo electrónico se han enviado a la nueva dirección de correo electrónico. Debe verificar la dirección antes de que el cambio surta efecto." +verification_email_notification = "Se acaba de hacer una solicitud para verificar su dirección de correo electrónico para su cuenta con %%library%%." +verification_email_sent = "Las instrucciones de verificación de la dirección de correo electrónico se han enviado a la dirección de correo electrónico registrada con esta cuenta." +verification_email_subject = "VuFind Verificación de Email" +verification_email_url_pretext = "Puede verificar su dirección de correo electrónico en esta URL: %%url%%" +verification_too_soon = "Su correo electrónico requiere validación. Recientemente se envió un correo electrónico a su dirección de correo registrada. Si no lo recibió, espere unos minutos e intente nuevamente." +verification_user_not_found = "No pudimos encontrar tu cuenta" VHS = "VHS" Video = "Video" Video Clips = "Video Clips" diff --git a/languages/eu.ini b/languages/eu.ini index e53f0ecd645ddd0a6687b4ffe0cfaa60d8d52c75..93e0775ad46ea6081030acf983bdc279b69324e4 100644 --- a/languages/eu.ini +++ b/languages/eu.ini @@ -717,7 +717,6 @@ Backtrace = "Aztarna" Bag = "Poltsa" Balance = "Balantzea" Barcode = "Barra-kodea" -basic_search_keep_filters = "Iragazkia indarrean dago" Be the first to leave a comment = "Izan zaitez lehena ohar bat uzten" Be the first to tag this record = "Izan zaitez lehena erregistro honi etiketa jartzen" Bibliographic Details = "Xehetasun bibliografikoak" @@ -1007,7 +1006,6 @@ Favorites = "Gogokoenak" Fee = "Kuota" Feedback = "Feedback" feedback_email = "Posta elektronikoa" -feedback_login_required = "Lehenik saioa ireki behar duzu." feedback_name = "Feedback-Izena" Field of activity = "Jarduera-eremua" File Description = "Fitxategi deskribapena" diff --git a/languages/fi.ini b/languages/fi.ini index cd22dcf332a953b934e5a487d6cad6b8b0e3bb50..3e5924d7371af3debfe565f93a3d4d4c9c692736 100644 --- a/languages/fi.ini +++ b/languages/fi.ini @@ -30,7 +30,7 @@ Address = "Osoite" adv_search_all = "Kaikki kentät" adv_search_author = "Tekijä" adv_search_callnumber = "Hyllypaikka" -adv_search_filters = "Käytetyt suodattimet" +adv_search_filters = "Käytetyt rajaukset" adv_search_isn = "ISBN/ISSN" adv_search_journaltitle = "Lehden nimi" adv_search_label = "Hakuehdot" @@ -49,6 +49,7 @@ advSearchError_notFound = "Hakua ei löydy." ajax_load_interrupted = "Lataus keskeytyi" ajaxview_label_information = "Tietoa" ajaxview_label_tools = "Työkalut" +alert_email_address = "Uutuusvahtien tulokset lähetetään sähköpostiosoitteeseen" All = "Kaikki" All Fields = "Kaikki kentät" All Pages Loaded = "Kaikki sivut ladattu" @@ -58,6 +59,7 @@ alphabrowselink_html = "Selaa %%index%%-indeksiä alkaen kohdasta <a href="%%url An error has occurred = "Tapahtui virhe" An error occurred during execution; please try again later. = "Virhe toimintoa suoritettaessa. Yritä myöhemmin uudelleen." AND = "JA" +and = "ja" anonymous_tags = "Tuntemattomien tagit" APA Citation = "APA-viite" applied_filter = "Käytetty suodatus" @@ -69,7 +71,9 @@ Audio = "Ääni" authentication_error_admin = "Sisäänkirjautuminen epäonnistui. Ota yhteyttä järjestelmän ylläpitäjään." authentication_error_blank = "Käyttäjätunnus/salasana ei voi olla tyhjä." authentication_error_creation_blocked = "Sinulla ei ole oikeutta luoda käyttäjätiliä." -authentication_error_denied = "Käyttäjätunnus/salasana ei täsmää! Ei pääsyä järjestelmään." +authentication_error_denied = "Käyttäjätiedot eivät täsmää. Ei pääsyä järjestelmään." +authentication_error_email_not_verified_html = "Sähköpostiosoitettasi ei ole vielä vahvistettu. Tarkista myös roskapostikansio siltä varalta, että varmistusviesti on mennyt sinne. Tarvittaessa voit <a href="%%url%%">pyytää uuden vahvistusviestin</a>." +authentication_error_in_progress = "Kirjautuminen on jo käynnissä. Yritä myöhemmin uudelleen, jos haluat aloittaa alusta." authentication_error_invalid = "Käyttäjätunnus/salasana ei täsmää. Yritä uudestaan." authentication_error_loggedout = "Olet kirjautunut ulos." authentication_error_technical = "Sisäänkirjautuminen epäonnistui. Yritä uudestaan hetken kuluttua." @@ -95,7 +99,6 @@ Backtrace = "Kutsupino" Bag = "Kori" Balance = "Maksettavaa" Barcode = "Viivakoodi" -basic_search_keep_filters = "Säilytä käytössä oleva rajaus" Be the first to leave a comment = "Lisää ensimmäinen kommentti" Be the first to tag this record = "Lisää ensimmäinen tagi" Bibliographic Details = "Bibliografiset tiedot" @@ -165,6 +168,7 @@ Call Number = "Sijainti" callnumber_abbrev = "Hyllypaikka" Cannot find record = "Tietuetta ei löydy" Cannot find similar records = "Samankaltaisia tietueita ei löydy" +cannot set = "Ei voi asettaa" Cassette = "Kasetti" cat_establish_account = "Syötä seuraavat tiedot käyttäjätietojen hakua varten:" cat_password_abbrev = "Kirjastokortin salasana" @@ -173,7 +177,12 @@ Catalog Login = "Kirjautuminen kirjastokortilla" Catalog Results = "Luettelon tulokset" catalog_login_desc = "Syötä kirjastokortin tiedot." CD = "CD" +Change Email Address = "Vaihda sähköpostiosoite" Change Password = "Vaihda salasana" +change_email_disabled = "Sähköpostiosoitteen vaihtaminen ei ole mahdollista" +change_email_verification_reminder = "Lähetettyäsi tämän lomakkeen saat sähköpostiisi vahvistusviestin. Sinun tulee klikata viestin linkkiä, jotta uusi sähköpostiosoite tulee voimaan." +change_notification_email_message = "Sähköpostiosoitteen muutospyyntö on tehty palvelussa %%library%%. Jos et tehnyt muutospyyntöä itse, suosittelemme varmistamaan tilisi osoitteessa %%url%%. Ota yhteyttä tukeen sähköpostiosoitteella %%email%%, jos sinulla on kysyttävää tai aihetta huoleen." +change_notification_email_subject = "Tilin sähköpostiosoitteen muutospyyntö" channel_add_more = "Lisää tämänkaltaisia kanavia" channel_browse = "Näytä lisää tuloksia" channel_expand = "Tutki tähän liittyviä kanavia" @@ -182,9 +191,10 @@ channel_search = "Näytä hakutuloksena" channel_searchbox_label = "Etsi lisää kanavia:" Check Hold = "Tarkista varaus" Check Recall = "Tarkista varaus" -Checked Out = "Lainat" +check_profile = "Tarkista käyttäjätiedot." +Checked Out = "Lainattu" Checked Out Items = "Lainat" -Checkedout = "Lainat" +Checkedout = "Lainattu" Checkout Date = "Lainauspäivä" Chicago Citation = "Chicago-tyylinen lähdeviittaus" child_record_count = "%%count%% tietuetta" @@ -301,6 +311,7 @@ Due Date = "Eräpäivä" DVD = "DVD" eBook = "E-kirja" Edit = "Muokkaa" +edit = "muokkaa" Edit Library Card = "Muokkaa kirjastokorttia" Edit this Advanced Search = "Muokkaa tarkennettua hakua" edit_list = "Muokkaa listaa" @@ -329,8 +340,14 @@ Email address is invalid = "Sähköpostiosoite on virheellinen" Email Record = "Lähetä tietue sähköpostilla" Email this = "Lähetä sähköpostilla" Email this Search = "Lähetä haku sähköpostilla" +email_change_pending_html = "Sähköpostiositteen vaihto osoitteeseen %%pending%% odottaa vahvistusta. Klikkaa viestin linkkiä vahvistaaksesi uuden osoitteen. Tarvittaessa voimme <a href="%%url%%">lähettää vahvistusviestin uudelleen</a>." email_failure = "Virhe - viestiä ei voitu lähettää" email_link = "Linkki" +email_login_desc = "Käytä seuraavaa linkkiä kirjautuaksesi sisään. Jos et ole kirjautumassa sisään, voit huoletta jättää tämän viestin huomiotta. Huomaa, että linkki on voimassa rajoitetun ajan ja toimii vain laitteessa, jossa annoit sähköpostiosoitteesi." +email_login_link = "Linkki kirjautumiseen: <%%url%%>" +email_login_link_sent = "Lähetimme kirjautumislinkin sähköpostiisi. Linkin saapuminen voi kestää hetken. Jos linkkiä ei ala kuulua, kannattaa tarkistaa sähköpostin roskapostikansio." +email_login_requested = "Sähköpostiosoitteellasi on tehty kirjautumispyyntö palvelussa %%title%%." +email_login_subject = "Kirjaudu palveluun %%title%%" email_maximum_recipients_note = "Korkeintaan %%max%% vastaanottajaa sallittu." email_multiple_recipients_note = "Voit luetella useita vastaanottajia pilkuilla eroteltuina." email_selected = "Lähetä valitut sähköpostilla" @@ -340,6 +357,7 @@ email_subject = "Aihe" email_success = "Viesti lähetetty" Empty = "Tyhjä" Empty Book Bag = "Tyhjennä kirjakori" +empty_search_disallowed = "Käytetystä hakukohteesta ei voi hakea ilman hakuehtoja" Enable Auto Config = "Ota käyttöön automaattinen asennus" End Page = "Viimeinen sivu" Era = "Aikakausi" @@ -392,12 +410,13 @@ Favorites = "Suosikit" Fee = "Maksu" Feedback = "Palaute" feedback_email = "Sähköposti" -feedback_login_required = "Kirjaudu sisään ensin." feedback_name = "Nimi" Field of activity = "Toimiala" File Description = "Tiedoston kuvaus" Filter = "Suodatin" +Filter Collection = "Rajaa kokoelman sisältöä" filter_tags = "Suodata tageja" +filter_toggle_entries = "%%count%% rajausta" filter_wildcard = "Mikä tahansa" Find = "Hae" Find More = "Hae lisää" @@ -459,6 +478,7 @@ history_results = "Tulokset" history_save = "Tallenna?" history_save_link = "Tallenna" history_saved_searches = "Tallennetut haut" +history_schedule = "Uutuusvahti" history_search = "Haku" history_time = "Aika" hold_available = "Noudettavissa" @@ -605,6 +625,7 @@ Library Web Search = "Web-haku" library_card_edit_password_placeholder = "Uusi salasana" lightbox_error = "Virhe: Ponnahdusikkunan lataaminen epäonnistui." Limit To = "Rajaukset" +Link to full results = "Linkki koko tulokseen" List = "Lista" List Tags = "Listaa tagit" list_access_denied = "Sinulla ei ole oikeuksia katsoa tätä listaa." @@ -667,9 +688,12 @@ New Item Feed = "Syöte uutuuksista" New Item Search = "Uutuushaku" New Item Search Results = "Uutuushaun tulokset" New Items = "Uutuusluettelo" +New results found for search = "Uusia tuloksia haulle" New Title = "Uusi nimeke" +new_email_success = "Sähköpostiosoitteesi on vaihdettu" new_password = "Uusi salasana" new_password_success = "Salasana vaihdettu" +new_results_heading = "%%count%% uusinta tulosta" new_user_welcome_subject = "Uusi tili organisaatiossa %%library%%" new_user_welcome_text = "Tervetuloa käyttäjäksi organisaatioon %%library%%. Uusi tili on luotu nimellä %%firstname%% %%lastname%%. Käyttäjätunnuksesi on %%username%%. Aseta salasana seuraavalla sivulla: %%url%%" Newspaper = "Sanomalehti" @@ -685,6 +709,7 @@ No Preference = "Mikä tahansa" No reviews were found for this record = "Ei arvosteluja tälle tietueelle" No Tags = "Ei tageja" no_description = "Kuvausta ei saatavissa." +no_email_address = "Sähköpostiosoite puuttuu." no_items_selected = "Ei tietueita valittuna" nohit_active_filters = "Tässä haussa on käytetty suodattimia. Poistamalla suodattimet voit saada enemmän tuloksia." nohit_change_tab = "Haku tehtiin välilehdellä "%%activeTab%%". Voit löytää jotain muilta välilehdiltä:" @@ -736,6 +761,63 @@ number_decimal_point = "," number_thousands_separator = " " OAI Server = "OAI-palvelin" Occupation = "Ammatti" +od_account_noaccess = "Tällä kirjastolla ei ole pääsyä Overdrive-sisältöön" +od_account_problem = "Tililläsi on ongelma. %%message%%" +od_audiobook-mp3 = "MP3-äänikirja" +od_audiobook-overdrive = "OverDrive Kuuntele äänikirja" +od_avail_avail = "Saatavilla:" +od_avail_holds = "Varauksia:" +od_avail_total = "Kappaleita yhteensä:" +od_but_cancel_hold = "Peru varaus" +od_but_checkout = "Lainaa Overdrivesta" +od_but_checkout_s = "Lainaa" +od_but_gettitle = "Lataa tämä sisältö" +od_but_gettitle_s = "Lataa" +od_but_hold = "Varaa Overdrivesta" +od_but_hold_s = "Varaa" +od_but_return = "Palauta laina" +od_cancel_hold = "Peruuta Overdrive-varaus" +od_checkout = "Overdrive-lainaus" +od_code_connection_failed = "Yhteys Overdriveen epäonnistui. Jos tilanne jatkuu, ota yhteyttä kirjastoon." +od_code_contentnotavail = "Tämä sisältö ei ole saatavilla alueellasi." +od_code_login_for_avail = "Kirjaudu sisään nähdäksesi saatavuuden" +od_code_resource_not_found = "Nimekettä ei löytynyt" +od_content = "Overdrive-sisältö" +od_dl_formats = "Tuetut latausmuodot" +od_docheckout_failure = "Nimekkeen lainaaminen epäonnistui." +od_docheckout_success = "Nimeke on lainattu sinulle. Laina erääntyy %%expireDate%%" +od_early_return = "Palaute Overdrive-laina etuajassa" +od_ebook-epub-adobe = "Adobe EPUB -sähkökirja" +od_ebook-epub-open = "Open EPUB -sähkökirja" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader -sähkökirja" +od_ebook-overdrive = "OverDrive Read -sähkökirja" +od_ebook-pdf-adobe = "Adobe PDF -sähkökirja" +od_ebook-pdf-open = "Open PDF -sähkökirja" +od_expires_on = "Nimeke vanhenee %%due_date%%." +od_get_title = "Overdrive-lataus" +od_gettitle_failure = "Nimekettä ei voitu ladata." +od_help_linktext = "Overdrive-opaste" +od_history = "Overdrive-historia" +od_hold = "Overdrive-varaus" +od_hold_cancel_failure = "Varauksen peruminen epäonnistui. " +od_hold_cancel_success = "Varaus on peruttu." +od_hold_email = "Sähköpostiosoite varausilmoitukselle: %%holdEmailAddress%%." +od_hold_now_avail = "Tämä varaus on nyt lainattavissa. Laina vanhenee %%expireDate%%." +od_hold_place_failure = "Varauspyyntö epäonnistui." +od_hold_place_success = "Sinulla on varaus tähän teokseen. Paikkasi varausjonossa on %%holdListPosition%%" +od_hold_placed_on = "Varaus tehty %%holdPlacedDate%%." +od_hold_queue = "Sijainti varausjonossa on %%holdPosition%% / %%numberOfHolds%%." +od_holds = "Overdrive-varaukset" +od_info_unavail = "Tätä tietoa ei ole juuri nyt saatavissa." +od_is_checkedout = "Sinulla on tämä teos lainassa. Sen eräpäivä on %%due_date%%." +od_is_on_hold = "Sinulla on tämä teos lainassa." +od_loans = "Overdrive-lainat" +od_mycontent_help = "Saadaksesi tietoa näiden teosten lataamisesta ja käyttämisestä, katso <a href="%%url%%">Overdrive Help</a>." +od_none_found = "Ei löytyneitä teoksia." +od_return_failure = "Teosta ei voitu palauttaa." +od_return_success = "Teos on palautettu." +od_video-streaming = "suoratoistovideo" of_num_results = "%%position%%. yhteensä %%total%% tuloksesta" old_password = "Vanha salasana" On Reserve = "Rajatussa käytössä" @@ -830,6 +912,7 @@ Read the full review online... = "Lue arvostelu kokonaisuudessaan..." Recall This = "Tee varaus" recaptcha_not_passed = "CAPTCHA-tarkistus virheellinen" recently_returned_channel_title = "Äskettäin palautetut" +recommend_links_text = "Voit myös kokeilla:" Record Citations = "Tietueen sitaatit" Record Count = "Tietueiden lukumäärä" Record Type = "Tietueen tyyppi" @@ -903,6 +986,11 @@ Save Comment = "Tallenna kommentti" save_search = "Tallenna haku" save_search_remove = "Poista tallennettu haku" Saved in = "Tallennettuna" +schedule_daily = "Päivittäin" +schedule_explanation = "Vastaanota sähköposti-ilmoituksia uusista hakutuloksista." +schedule_none = "Ei mitään" +schedule_weekly = "Viikottain" +Scheduled Alert Results = "Uutuusvahdin tulokset" scholarly_limit = "Rajaa artikkeleihin tieteellisistä lehdistä" Scroll to Load More = "Vieritä ladataksesi lisää" Search = "Haku" @@ -917,6 +1005,7 @@ search results of = "yhteensä" Search Tips = "Hakuohje" Search Tools = "Työkalut" Search Type = "Haun tyyppi" +Search within collection = "Hae kokoelmasta" search_AND = "Kaikilla ehdoilla (AND)" search_groups = "Hakuryhmät" search_match = "Hae" @@ -942,6 +1031,7 @@ Sensor Image = "Kaukokartoituskuva" Serial = "Kausijulkaisu" Series = "Sarja" Set = "Aseta" +show_filters_html = "Näytä rajaukset (%%count%%)" showing_items_html = "Näytetään tulokset <strong>%%start%% - %%end%%</strong>" showing_items_of_html = "Näytetään <strong>%%start%% - %%end%%</strong> yhteensä <strong>%%total%%</strong> tuloksesta" showing_results_for_html = "Näytetään tulokset <strong>%%start%% - %%end%%</strong> haulle '<strong>%%lookfor%%</strong>'" @@ -989,6 +1079,7 @@ Start a new Basic Search = "Aloita uusi perushaku" Start Page = "Ensimmäinen sivu" starting from = "Alkaen" Status = "Tila" +status_transit = "Kuljetuksessa" status_unknown_message = "Reaaliaikaista tietoa ei saatavissa" Storage Retrieval Requests = "Varastotilaukset" storage_retrieval_request_available = "Noudettavissa" @@ -1094,6 +1185,9 @@ unique_tags = "Yksilöllisiä tageja yhteensä" University Library = "Yliopiston kirjasto" Unknown = "Tuntematon" unrecognized_facet_label = "Muu" +unsubscribe_confirmation = "Haluatko varmasti perua tilauksen?" +unsubscribe_description = "Etkö halua tätä viestiä jatkossa? Peru tilaus alla olevasta linkistä" +unsubscribe_successful = "Tilaus peruttu" Upgrade VuFind = "Päivitä VuFind" upgrade_description = "Jos olet päivittämässä aiemmasta VuFind-versiosta, voit ladata vanhat asetukset tällä työkalulla." URL = "URL" @@ -1103,6 +1197,14 @@ User Account = "Käyttäjätili" Username = "Käyttäjätunnus / viivakoodi" Username cannot be blank = "Käyttäjätunnus ei voi olla tyhjä" Username is already in use in another library card = "Käyttäjätunnus on jo käytössä toisessa kirjastokortissa" +verification_done = "Sähköpostiosoitteesi on vahvistettu." +verification_email_change_sent = "Ohje sähköpostiosoitteen vahvistamiseksi on lähetetty uuteen osoitteeseen. Sinun tulee vahvistaa muutos, jotta uusi sähköpostiosoite tulisi voimaan." +verification_email_notification = "Pyyntö sähköpostiosoitteesi vahvistamiseksi on juuri tehty (%%library%%)." +verification_email_sent = "Ohjeet sähköpostiosoitteen vahvistamiseksi on lähetetty tilillesi määritettyyn sähköpostiosoitteeseen." +verification_email_subject = "VuFindin sähköpostiosoitteen vahvistaminen" +verification_email_url_pretext = "Voit vahvistaa sähköpostiosoitteesi seuraavassa osoitteessa: %%url%%" +verification_too_soon = "Sähköpostiosoitteesi tulee vahvistaa. Sähköpostiosoitteeseesi on lähetetty viesti vahvistamisesta. Jos et saanut viestiä, yritä uudelleen muutaman minuutin kuluttua." +verification_user_not_found = "Tiliäsi ei löytynyt" VHS = "VHS" Video = "Video" Video Clips = "Videoleikkeet" diff --git a/languages/fr.ini b/languages/fr.ini index 33d479dc57d0673afed58c375ec199fe15a37a6b..b2fcbf94c614ad786be6d166af1d9ea543ee5513 100644 --- a/languages/fr.ini +++ b/languages/fr.ini @@ -45,11 +45,12 @@ adv_search_year = "Année de publication" Advanced = "Recherche avancée" Advanced Search = "Recherche avancée" advSearchError_noRights = "Hélas, vous n'avez pas le droit de modifier cette recherche. Peut-être la session n'est-elle plus valide ?" -advSearchError_notAdvanced = "La recherche choisie ne peut pas être éditée." +advSearchError_notAdvanced = "La recherche choisie ne peut pas être modifiée." advSearchError_notFound = "La recherche demandée n'existe pas." ajax_load_interrupted = "Chargement interrompu" ajaxview_label_information = "Information" ajaxview_label_tools = "Outils" +alert_email_address = "Les résultats de l'alerte seront envoyés régulièrement à l'adresse" All = "Tout" All Fields = "Tous les champs" All Pages Loaded = "Toutes les pages chargées" @@ -59,6 +60,7 @@ alphabrowselink_html = "Parcourir par %%index%% à partir de <a href="%%url%%">% An error has occurred = "Une erreur est survenue" An error occurred during execution; please try again later. = "Erreur lors de l'exécution de la commande. Veuillez réessayer plus tard." AND = "ET" +and = "et" anonymous_tags = "Tags anonymes" APA Citation = "Style de citation APA" applied_filter = "Appliquer le filtre" @@ -69,8 +71,10 @@ Audience = "Public" Audio = "Audio" authentication_error_admin = "Vous ne pouvez pas vous connecter en ce moment. Veuillez contacter votre administrateur pour obtenir de l'aide" authentication_error_blank = "Ces champs ne peuvent être vides." -authentication_error_creation_blocked = "Vous n'avez pas les autorisations nécessaires pour créer un compte." +authentication_error_creation_blocked = "Vous n'avez pas les droits nécessaires pour créer un compte." authentication_error_denied = "Les informations de connexion ne sont pas valides ! Accès refusé." +authentication_error_email_not_verified_html = "Votre adresse de courriel n'a pas encore été vérifiée. Veuillez chercher dans votre dossier de spams si le message de vérification a été filtré. Si besoin, nous pouvons <a href="%%url%%">renvoyer le courriel de vérification</a>." +authentication_error_in_progress = "Une demande de connexion est déjà en cours de traitement. Merci de recommencer plus tard, s'il vous faut relancer le processus." authentication_error_invalid = "Erreur de connexion -- veuillez réessayer." authentication_error_loggedout = "Vous êtes maintenant déconnecté." authentication_error_technical = "Connexion impossible pour le moment. Veuillez réessayer plus tard." @@ -96,7 +100,6 @@ Backtrace = "Historique" Bag = "Panier" Balance = "Solde du compte" Barcode = "Code-barres" -basic_search_keep_filters = "Je garde les filtres." Be the first to leave a comment = "Soyez le premier à ajouter un commentaire" Be the first to tag this record = "Soyez le premier à ajouter un tag" Bibliographic Details = "Détails bibliographiques" @@ -166,6 +169,7 @@ Call Number = "Cote" callnumber_abbrev = "Cote :" Cannot find record = "Notice introuvable" Cannot find similar records = "Pas de notices similaires trouvées" +cannot set = "Ne peut pas être modifié" Cassette = "Cassette" cat_establish_account = "Afin de créer un compte d'utilisateur, veuillez saisir les informations suivantes :" cat_password_abbrev = "Mot de passe dans le catalogue" @@ -174,7 +178,12 @@ Catalog Login = "Connexion au catalogue" Catalog Results = "Résultats du catalogue" catalog_login_desc = "Entrez vos codes d'accès au catalogue" CD = "CD" +Change Email Address = "Modifier l'adresse de courriel" Change Password = "Modifier le mot de passe" +change_email_disabled = "Vous n'avez pas les droits pour modifier votre adresse de courriel pour le moment." +change_email_verification_reminder = "La validation de ce formulaire enverra un courriel à la nouvelle adresse ; il vous faudra cliquer sur un lien contenu dans ce courriel pour que la modification soit prise en compte." +change_notification_email_message = "Une demande vient d'être faite pour modifier votre adresse de courriel auprès de %%library%%. Si vous n'êtes pas à l'origine de cette demande, vous pouvez vous connecter à %%url%% et confirmer les données de votre compte. Si vous avez des questions, vous pouvez contacter notre support à l'adresse %%email%%." +change_notification_email_subject = "Information sur le changement d'adresse de courriel pour votre compte" channel_add_more = "Afficher davantage de canaux de ce type" channel_browse = "Afficher davantage de notices" channel_expand = "Explorer des canaux similaires" @@ -183,6 +192,7 @@ channel_search = "Afficher les éléments dans la liste de résultats" channel_searchbox_label = "Chercher d'autres canaux:" Check Hold = "Voir les réservations" Check Recall = "Voir les rappels" +check_profile = "Vérifier les informations de votre profil." Checked Out = "Emprunté" Checked Out Items = "Documents empruntés" Checkedout = "Documents empruntés" @@ -216,6 +226,7 @@ comment_error_load = "Erreur: Impossible de mettre à jour la liste des commenta comment_error_save = "Erreur: Enregistrement du commentaire impossible" Comments = "Commentaires" Company/Entity = "Entreprise/Institution" +Conference Proceeding = "Actes de congrès" Configuration = "Configuration" confirm_delete = "Êtes-vous sûr de vouloir supprimer cet élément ?" confirm_delete_brief = "Supprimer l'élément ?" @@ -296,10 +307,11 @@ Due Date = "Délais d'emprunt" DVD = "DVD" eBook = "eBook" Edit = "Modifier" +edit = "modifier" Edit Library Card = "Modifier la carte de bibliothèque" Edit this Advanced Search = "Modifier cette recherche" edit_list = "Modifier la liste" -edit_list_fail = "Hélas vous n'êtes pas autorisé à modifier cette liste" +edit_list_fail = "Hélas vous n'avez pas les droits pour modifier cette liste" edit_list_success = "La liste a été mise à jour avec succès." Edition = "Édition" eds_expander_fulltext = "Chercher aussi à l'intérieur des articles" @@ -324,8 +336,14 @@ Email address is invalid = "Cette adresse de courriel n'est pas valide" Email Record = "Envoyer la notice par courriel" Email this = "Envoyer par courriel" Email this Search = "Envoyer cette recherche par courriel" +email_change_pending_html = "Une demande de modification d'adresse de courriel est en cours pour %%pending%%. Merci de cliquer sur le lien contenu dans le message de vérification qui a été envoyé à cette adresse, afin de valider la modification. Si besoin, nous pouvons <a href="%%url%%">Renvoyer le message de vérification</a>." email_failure = "Erreur - Le message n'a pu être envoyé" email_link = "Lien" +email_login_desc = "Merci d'utiliser le lien suivant pour vous connecter. Si vous n'êtes pas à l'origine d'une demande de connexion, veuillez ignorer ce message. Attention, le lien n'est valide que pour une durée limitée, et uniquement avec l'appareil que vous avez utilisé pour saisir l'adresse de courriel." +email_login_link = "Lien de connexion : <%%url%%>" +email_login_link_sent = "Nous avons envoyé un lien de connexion à votre adresse de courriel. Il peut se passer quelques instants avant l'arrivée de ce lien. Si vous ne le recevez pas d'ici peu, vérifiez également votre filtre antispam." +email_login_requested = "Une demande de connexion à %%title%% a été faite avec votre adresse de courriel." +email_login_subject = "Connexion à %%title%%" email_maximum_recipients_note = "Vous pouvez saisir au maximum %%max%% adresses." email_multiple_recipients_note = "Séparez plusieurs adresses de courriel par des virgules." email_selected = "Envoyer la sélection par courriel" @@ -335,6 +353,7 @@ email_subject = "Sujet" email_success = "Message envoyé avec succès" Empty = "Vide" Empty Book Bag = "Vider le panier" +empty_search_disallowed = "Il n'est pas possible de faire une recherche vide avec la cible de recherche actuellement choisie." Enable Auto Config = "Activer la configuration automatique" End Page = "Dernière page" Era = "Période" @@ -365,8 +384,8 @@ export_success = "Export terminé" export_unsupported_format = "Format d'export non supporté" external_auth_heading = "Accès aux contenus qui font l'objet d'une licence" external_auth_login_message = "Connectez-vous pour accéder aux contenus qui font l'objet d'une licence" -external_auth_unauthorized = "Vous n'êtes pas autorisé à accéder aux contenus qui font l'objet d'une licence" -external_auth_unauthorized_desc = "Le mode de connexion que vous avez utilisé ne donne pas accès aux contenus qui font l'objet d'une licence. Merci de vous déconnecter et d'utiliser une autre méthode d'authentification." +external_auth_unauthorized = "Vous n'avez pas l'autorisation d'accéder aux contenus qui font l'objet d'une licence" +external_auth_unauthorized_desc = "Le mode de connexion que vous avez utilisé ne donne pas accès aux contenus qui font l'objet d'une licence. Merci de vous déconnecter et d'utiliser un autre mode de connexion." facet_list_empty = "Pas de valeurs disponibles" facet_list_for = "Liste des valeurs pour %%field%%" FAQs = "FAQ" @@ -387,12 +406,13 @@ Favorites = "Favoris" Fee = "Frais" Feedback = "Remarques" feedback_email = "Courriel" -feedback_login_required = "Il faut se connecter d'abord." feedback_name = "Nom" Field of activity = "Domaine d'activité" File Description = "Description du fichier" Filter = "Filtre" +Filter Collection = "Filtre par collection" filter_tags = "Filtrer les tags" +filter_toggle_entries = "%%count%% filtres" filter_wildcard = "Tous" Find = "Rechercher" Find More = "Autres modes de recherche" @@ -428,6 +448,7 @@ Go to Standard View = "Retour à la liste des résultats" go_to_list = "Aller à la liste" google_map_cluster = "Cluster" google_map_cluster_points = "Cluster Points" +Government Document = "Publication officielle" Grid = "Grille" Group = "Groupe" group_AND = "Tous les groupes" @@ -454,6 +475,7 @@ history_results = "Résultats" history_save = "Sauvegarder ?" history_save_link = "Sauvegarder" history_saved_searches = "Recherches sauvegardées" +history_schedule = "Planification de l'alerte" history_search = "Recherche" history_time = "Date" hold_available = "Disponible pour le retrait" @@ -467,12 +489,12 @@ hold_cancel_success_items = "%%count%% la ou les réservations ont bien été an hold_date_invalid = "Veuillez saisir une date valide" hold_date_past = "Veuillez saisir une date dans le futur" hold_empty_selection = "Aucune réservation n'est sélectionnée" -hold_error_blocked = "Vous n'avez pas les autorisations nécessaires pour réserver ce document." +hold_error_blocked = "Vous n'avez pas les droits nécessaires pour réserver ce document." hold_error_fail = "Votre demande a échoué. Veuillez contacter la bibliothèque pour plus d'informations." hold_invalid_pickup = "Le lieu de retrait saisi est incorrect. Veuillez réessayer" hold_invalid_request_group = "Le groupe utilisé pour la demande de réservation est incorrect. Veuillez réessayer." hold_items_available = "La réservation n'est pas possible, car des exemplaires sont disponibles." -hold_login = "Pour les informations sur les réservations" +hold_login = "Connectez-vous pour connaître les informations sur les réservations" hold_place = "Réserver" hold_place_fail_missing = "Votre demande a échoué. Il manquait certaines données. Veuillez contacter la bibliothèque pour plus d'informations" hold_place_success_html = "La réservation a bien été enregistrée. <a href="%%url%%">Vos réservations et rappels</a>." @@ -506,7 +528,7 @@ ill_request_comments = "Commentaires" ill_request_date_invalid = "Veuillez saisir une date valide" ill_request_date_past = "Veuillez saisir une date dans le futur" ill_request_empty_selection = "Aucune demande de prêt entre bibliothèques n'est sélectionnée." -ill_request_error_blocked = "Vous n'avez pas les autorisations suffisantes pour faire une demande de prêt entre bibliothèques pour ce document." +ill_request_error_blocked = "Vous n'avez pas les droits nécessaires pour faire une demande de prêt entre bibliothèques pour ce document." ill_request_error_fail = "Votre demande a échoué. Veuillez contacter la bibliothèque pour plus d'informations." ill_request_error_technical = "Votre requête a échoué en raison d'un problème technique. Veuillez contacter votre bibliothèque pour plus de précisions." ill_request_error_unknown_patron_source = "La bibliothèque de l'usager n'est pas reconnue dans les demandes de prêt entre bibliothèques." @@ -600,6 +622,7 @@ Library Web Search = "Recherche sur le site de la bibliothèque" library_card_edit_password_placeholder = "Nouveau mot de passe" lightbox_error = "Erreur: impossible d'ouvrir la pop-up" Limit To = "Limiter à " +Link to full results = "Lien vers les résultats complets" List = "Liste" List Tags = "Liste des tags" list_access_denied = "Vous n'avez pas les droits pour consulter cette liste." @@ -662,9 +685,12 @@ New Item Feed = "Flux RSS des nouveautés" New Item Search = "Recherche des nouveautés" New Item Search Results = "Résultats de la recherche des nouveautés" New Items = "Nouveautés" +New results found for search = "Nouveaux résultats trouvés pour la recherche" New Title = "Nouveau titre" +new_email_success = "Votre adresse de courriel a été modifiée avec succès." new_password = "Nouveau mot de passe" new_password_success = "Mot de passe mis à jour avec succès" +new_results_heading = "%%count%% nouveaux résultats" new_user_welcome_subject = "Votre nouveau compte à la %%library%%" new_user_welcome_text = "Bienvenue à %%library%%. Un nouveau compte a été ouvert pour %%firstname%% %%lastname%%. Votre nom d'utilisateur est %%username%%. Merci de créer un mot de passe sur cette page: %%url%%" Newspaper = "Journal" @@ -680,6 +706,7 @@ No Preference = "Pas de préférence" No reviews were found for this record = "Pas de commentaire trouvé pour cette notice" No Tags = "Pas de tags" no_description = "Aucune description n'est disponible." +no_email_address = "Il manque une adresse de courriel." no_items_selected = "Aucun élément n'est sélectionné" nohit_active_filters = "Au moins un filtre a été appliqué à cette recherche. Supprimez ce filtre pour obtenir plus de résultats." nohit_change_tab = "Vous avez recherché dans l'onglet "%%activeTab%%". Vous trouverez peut-être des résultats supplémentaires dans les autres onglets :" @@ -731,7 +758,64 @@ number_decimal_point = "," number_thousands_separator = " " OAI Server = "Serveur OAI" Occupation = "Activité" -of_num_results = "#%%position%% de %%total%% résultas" +od_account_noaccess = "Cette carte de bibliothèque n'a pas accès aux contenus d'Overdrive" +od_account_problem = "Il y a un problème avec votre compte. %%message%%" +od_audiobook-mp3 = "Livre audio MP3" +od_audiobook-overdrive = "Livre audio OverDrive" +od_avail_avail = "Disponible :" +od_avail_holds = "Demandes :" +od_avail_total = "Nombre total d'exemplaires :" +od_but_cancel_hold = "Annuler cette demande" +od_but_checkout = "Emprunter via Overdrive" +od_but_checkout_s = "Emprunter" +od_but_gettitle = "Télécharger le contenu" +od_but_gettitle_s = "Télécharger" +od_but_hold = "Faire une demande via Overdrive" +od_but_hold_s = "Faire une demande" +od_but_return = "Retourner ce titre" +od_cancel_hold = "Annuler la demande Overdrive" +od_checkout = "Emprunt Overdrive" +od_code_connection_failed = "La connexion à Overdrive a échoué. Si le problème persiste, veuillez contacter votre bibliothèque." +od_code_contentnotavail = "Ce contenu n'est pas disponible dans votre zone géographique." +od_code_login_for_avail = "Connectez-vous pour connaître la disponibilité" +od_code_resource_not_found = "Titre introuvable" +od_content = "Contenus Overdrive" +od_dl_formats = "Formats de téléchargement disponibles" +od_docheckout_failure = "Ce titre n'a pas pu être emprunté." +od_docheckout_success = "Ce titre vous a été prêté. Cet emprunt arrive à échéance le %%expireDate%%" +od_early_return = "Retour précoce Overdrive" +od_ebook-epub-adobe = "eBook EPUB Adobe" +od_ebook-epub-open = "eBook EPUB Open" +od_ebook-kindle = "eBook Kindle" +od_ebook-mediado = "eBook MediaDo Reader" +od_ebook-overdrive = "eBook OverDrive Read" +od_ebook-pdf-adobe = "eBook Adobe PDF" +od_ebook-pdf-open = "eBook Open PDF" +od_expires_on = "Ce titre arrive à échéance le %%due_date%%." +od_get_title = "Téléchargement Overdrive" +od_gettitle_failure = "Ce titre n'a pas pu être téléchargé." +od_help_linktext = "Aide Overdrive" +od_history = "Historique Overdrive" +od_hold = "Demande Overdrive" +od_hold_cancel_failure = "L'annulation de la demande a échoué. " +od_hold_cancel_success = "La demande a été annulée avec succès." +od_hold_email = "Adresse de courriel pour les notifications concernant les demandes : %%holdEmailAddress%%." +od_hold_now_avail = "Cette demande est disponible pour l'emprunt. Cette disponibilité expire le %%expireDate%%." +od_hold_place_failure = "La demande a échoué." +od_hold_place_success = "Ce titre a fait l'objet d'une ou plusieurs demandes. Le rang de votre demande dans la liste d'attente est : %%holdListPosition%%" +od_hold_placed_on = "Demande faite le %%holdPlacedDate%%." +od_hold_queue = "Demande %%holdPosition%% sur %%numberOfHolds%% demandes dans la liste d'attente." +od_holds = "Demandes Overdrive" +od_info_unavail = "Cette information n'est pas disponible pour le moment." +od_is_checkedout = "Vous avez emprunté ce titre. Il doit être retourné pour le %%due_date%%." +od_is_on_hold = "Vous avez fait une demande pour ce titre." +od_loans = "Emprunts Overdrive" +od_mycontent_help = "Pour trouver des informations et une aide pour le téléchargement de ces titres, veuillez consulter <a href="%%url%%">l'aide Overdrive</a>." +od_none_found = "Aucun titre n'a été trouvé." +od_return_failure = "Ce titre ne peut pas être retourné." +od_return_success = "Ce titre a été retourné." +od_video-streaming = "fichier vidéo en streaming" +of_num_results = "#%%position%% de %%total%% résultats" old_password = "Ancien mot de passe" On Reserve = "Mis en réserve" On Reserve - Ask at Circulation Desk = "Mis en réserve - Contactez la bibliothèque" @@ -760,6 +844,7 @@ pagination_label = "Pagination" Password = "Mot de passe" Password Again = "Confirmer le mot de passe" Password cannot be blank = "Veuillez saisir un mot de passe" +password_error_auth_old = "Le mot de passe utilisé jusqu'à présent est invalide." password_error_invalid = "Nouveau mot de passe non valide (contient p. ex. des caractères interdits)" password_error_not_unique = "Le mot de passe n'a pas été changé" password_maximum_length = "Longueur maximum du mot de passe : %%maxlength%% caractères" @@ -825,6 +910,7 @@ Read the full review online... = "Lire l'avis complet en ligne..." Recall This = "Rappeler" recaptcha_not_passed = "L'identification du CAPTCHA a échoué" recently_returned_channel_title = "Revenus récemment" +recommend_links_text = "Vous pouvez également essayer :" Record Citations = "Citations de notices" Record Count = "Nombre de notices" Record Type = "Type de notice" @@ -838,11 +924,12 @@ recovery_email_subject = "Récupération de compte VuFind" recovery_email_url_pretext = "Veuillez visiter cette page pour définir un nouveau mot de passe : %%url%%" recovery_expired_hash = "Le lien de récupération n'est plus valide" recovery_invalid_hash = "Le lien de récupération n'est pas reconnu" -recovery_new_disabled = "Vous n'êtes pas autorisé à modifier votre mot de passe pour le moment" +recovery_new_disabled = "Vous n'avez pas les droits pour modifier votre mot de passe pour le moment" recovery_title = "Récupération de mot de passe" recovery_too_soon = "Trop d'essais de récupération de mot de passe. Veuillez réessayer plus tard" -recovery_user_not_found = "Nous n'avons pu identifier votre compte" +recovery_user_not_found = "Nous n'avons pas pu identifier votre compte" rectangle_center_message = "Point central du rectangle surligné" +Reference Material = "Ouvrage de référence" Refine Results = "Affiner les résultats" Region = "Région" relais_available = "Ce document est disponible par le Prêt entre bibliothèques. Voulez-vous faire une demande ?" @@ -883,6 +970,7 @@ Requests = "Demandes" Reserves = "Réserves" Reserves Search = "Rechercher dans les réserves" Reserves Search Results = "Résultats de la recherche dans les réserves" +reset_filters_button = "Réinitialiser les filtres" result_checkbox_label = "Merci de choisir la recherche numéro %%number%%" result_count = "%%count%% résultats" Results = "Résultats" @@ -898,6 +986,11 @@ Save Comment = "Sauvegarder le commentaire" save_search = "Enregistrer la recherche" save_search_remove = "Effacer la recherche" Saved in = "Enregistré dans" +schedule_daily = "Une fois par jour" +schedule_explanation = "Recevoir des courriels d'alerte en cas de nouveaux résultats pour une recherche." +schedule_none = "Jamais" +schedule_weekly = "Une fois par semaine" +Scheduled Alert Results = "Résultats de l'alerte planifiée" scholarly_limit = "Limiter à des articles scientifiques" Scroll to Load More = "Faire défiler pour charger plus d'éléments" Search = "Rechercher" @@ -912,6 +1005,7 @@ search results of = "Résultats de recherche de " Search Tips = "Astuces pour la recherche" Search Tools = "Outils de recherche" Search Type = "Type de recherche" +Search within collection = "Chercher dans la collection" search_AND = "Tous les termes" search_groups = "Groupes de recherches" search_match = "Correspondances" @@ -937,12 +1031,13 @@ Sensor Image = "Image du capteur" Serial = "Publication en série" Series = "Collection" Set = "Changer" +show_filters_html = "Montrer les filtres (%%count%%)" showing_items_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> Éléments" showing_items_of_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> de <strong>%%total%%</strong> Éléments" -showing_results_for_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultas pour la requête '<strong>%%lookfor%%</strong>'" -showing_results_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultas" -showing_results_of_for_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultas de <strong>%%total%%</strong> pour la requête '<strong>%%lookfor%%</strong>'" -showing_results_of_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultas de <strong>%%total%%</strong>" +showing_results_for_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultats pour la requête '<strong>%%lookfor%%</strong>'" +showing_results_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultats" +showing_results_of_for_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultats de <strong>%%total%%</strong> pour la requête '<strong>%%lookfor%%</strong>'" +showing_results_of_html = "Résultat(s) <strong>%%start%% - %%end%%</strong> résultats de <strong>%%total%%</strong>" sidebar_close = "Cacher la barre latérale" sidebar_expand = "Afficher la barre latérale" Similar Items = "Documents similaires" @@ -956,7 +1051,7 @@ sms_phone_number = "Numéro de téléphone (10 chiffres)" sms_sending = "Envoi en cours..." sms_success = "Message envoyé." Software = "Logiciel" -Sorry, but the help you requested is unavailable in your language. = "Désolé, mais l'aide demandée n'existe pas dans cette langue." +Sorry, but the help you requested is unavailable in your language. = "Hélas, l'aide demandée n'existe pas dans cette langue." Sort = "Trier" sort_alphabetic = "Alphabétique" sort_author = "Auteur" @@ -984,6 +1079,7 @@ Start a new Basic Search = "Nouvelle recherche simple" Start Page = "Page d'accueil" starting from = "à partir de" Status = "Statut" +status_transit = "En transit" status_unknown_message = "Statut en temps réel indisponible" Storage Retrieval Requests = "Demandes de communication en magasin" storage_retrieval_request_available = "Disponible pour le retrait" @@ -999,7 +1095,7 @@ storage_retrieval_request_comments = "Commentaires" storage_retrieval_request_date_invalid = "Veuillez saisir une date valide" storage_retrieval_request_date_past = "Veuillez saisir une date dans le futur" storage_retrieval_request_empty_selection = "Aucune demande de communication en magasin n'est sélectionnée" -storage_retrieval_request_error_blocked = "Vous n'êtes pas autorisé-e à faire une demande de communication en magasin sur ce document" +storage_retrieval_request_error_blocked = "Vous n'avez pas les droits pour faire une demande de communication en magasin sur ce document" storage_retrieval_request_error_fail = "Votre demande a échoué. Veuillez contacter la bibliothèque pour plus d'informations." storage_retrieval_request_invalid_pickup = "Le lieu de retrait saisi est incorrect. Veuillez essayer à nouveau" storage_retrieval_request_issue = "Date" @@ -1059,6 +1155,7 @@ The record you selected is not part of any of your lists. = "La notice sélectio The record you selected is not part of the selected list. = "La notice sélectionnée ne fait pas partie de la liste sélectionnée" The system is currently unavailable due to system maintenance = "Pour des raisons de maintenance, le système est momentanément indisponible" Theme = "Thème" +Thesis = "Thèse" This email was sent from = "Ce courriel a été envoyé par" This field is required = "Ce champ est obligatoire" This item is already part of the following list/lists = "Cette notice fait déjà partie des listes suivantes" @@ -1089,6 +1186,9 @@ unique_tags = "Tags uniques" University Library = "Bibliothèque universitaire" Unknown = "Inconnue" unrecognized_facet_label = "Autre" +unsubscribe_confirmation = "Voulez-vous annuler l'abonnement à l'alerte par courriel ?" +unsubscribe_description = "Si vous ne souhaitez plus recevoir ce message à l'avenir, désabonnez-vous en utilisant le lien suivant" +unsubscribe_successful = "Abonnement annulé" Upgrade VuFind = "Mettre à jour VuFind" upgrade_description = "Si vous effectuez une mise à jour depuis une ancienne version de VuFind, vous pouvez charger vos anciens paramètres à l'aide de cet outil." URL = "URL" @@ -1098,6 +1198,14 @@ User Account = "Compte d'utilisateur" Username = "Nom d'utilisateur" Username cannot be blank = "Il faut saisir un nom d'utilisateur" Username is already in use in another library card = "Ce nom d'utilisateur est déjà utilisé pour une autre carte" +verification_done = "Votre adresse de courriel a été vérifiée." +verification_email_change_sent = "Les informations pour la vérification de l'adresse de courriel ont été envoyées à la nouvelle adresse. Il vous faut procéder à cette vérification pour que la modification soit prise en compte." +verification_email_notification = "Une demande de vérification de votre adresse de courriel vient d'être faite pour votre compte %%library%%." +verification_email_sent = "Des instructions pour la vérification de votre adresse de courriel ont été envoyées à l'adresse associée à ce compte." +verification_email_subject = "Courriel de vérification pour VuFind" +verification_email_url_pretext = "Vous pouvez vérifier votre adresse de courriel à cette adresse : %%url%%" +verification_too_soon = "Il faut faire valider votre adresse de courriel. Un message vient d'être envoyé à l'adresse de courriel enregistrée sur votre compte. Si vous ne l'avez pas reçu, veuillez attendre quelques minutes et essayer à nouveau." +verification_user_not_found = "Nous n'avons pas pu identifier votre compte" VHS = "VHS" Video = "Vidéo" Video Clips = "vidéo-clips" diff --git a/languages/ga.ini b/languages/ga.ini index b32de6baef0e42aa563d1f43f8bf2bdba21e87e2..f11df41ca854b159ad9057889d826843d901ea77 100644 --- a/languages/ga.ini +++ b/languages/ga.ini @@ -64,7 +64,6 @@ Back to Search Results = "Ar ais chuig Torthaà Cuardaigh" Backtrace = "Lorg Siar" Bag = "Mála" Balance = "Iarmhéid" -basic_search_keep_filters = "Coinnigh mo scagairà reatha" Be the first to leave a comment = "Bà ar an gcéad duine le tuairim a scrÃobh" Be the first to tag this record = "Bà ar an gcéad duine leis an taifead seo a chlibeáil" Bibliographic Details = "Sonraà BibleagrafaÃochta" @@ -241,7 +240,6 @@ fav_list_delete_fail = "Ãr leithscéal, ach tharla earráid. NÃor scriosadh do Favorites = "Ceanáin" Fee = "Táille" feedback_email = "RÃomhphost" -feedback_login_required = "Nà mór a bheith logáilte amach i gcónaÃ." Find = "Aimsigh" Find More = "Aimsigh Tuilleadh" Find New Items = "Aimsigh MÃreanna Nua" diff --git a/languages/gl.ini b/languages/gl.ini index 71e1d63dbe72ce91ff56fb3b10396beff6f066f7..8e956fd4e758980d2bd78c664321b06555785440 100644 --- a/languages/gl.ini +++ b/languages/gl.ini @@ -86,7 +86,6 @@ Back to Search Results = "Volver a Resultados de Procura" Backtrace = "Rastrexar" Bag = "Bolsa" Balance = "Balance" -basic_search_keep_filters = "Reter os meus filtros" Be the first to leave a comment = "Sexa o primeiro en deixar un comentario" Be the first to tag this record = "Sexa o primeiro en etiquetar este rexistro" Bibliographic Details = "Detalles Bibliográficos" diff --git a/languages/he.ini b/languages/he.ini index 748e7f101eb1721b1d042325c25cf2c84befdbc8..e62d08681a95af943ef14f472ae1afdd3577126c 100644 --- a/languages/he.ini +++ b/languages/he.ini @@ -73,7 +73,6 @@ Back to Search Results = "חזרה לתוצ×ות חיפוש" Backtrace = "Backtrace" Bag = "תיק" Balance = "×יזון" -basic_search_keep_filters = "שמירת ×”×ž×¡× × ×™× ×”× ×•×›×—×™× ×©×œ×™" Be the first to leave a comment = "×”×™×”/×™ הר××©×•× /×” לכתוב הערה" Be the first to tag this record = "×”×™×”/×™ הר××©×•× /×” לתייג ×ת הרשומה" Bibliographic Details = "מידע ביבליוגרפי" @@ -276,7 +275,6 @@ fav_list_delete_fail = "מצטערי×. ×רעה שגי××”. הרשימה שלך Favorites = "מועדפי×" Fee = "דמי שימוש" feedback_email = "דו×ל" -feedback_login_required = "צריך להיות מחובר מר×ש" filter_wildcard = "כל ×חד" Find = "מצ×" Find More = "×ž×¦× ×¢×•×“" diff --git a/languages/hi.ini b/languages/hi.ini new file mode 100644 index 0000000000000000000000000000000000000000..61e80af554bba3b62d813c0a6bb4ae66253c8dc5 --- /dev/null +++ b/languages/hi.ini @@ -0,0 +1,1263 @@ +; -- Note: This file represents Hindi as spoken in India -- +; -- Translations courtesy of Md Nurul Alam, Pragya, Komal and Parthasarathi Mukhopadhyay +;For future reference: +;Hindi = हिंदी +Abstract = "सार" +Access = "अà¤à¤¿à¤—मन" +Access URL = "यूआरà¤à¤² में पà¥à¤°à¤µà¥‡à¤¶ करें " +access_denied = "अà¤à¤¿à¤—मन असà¥à¤µà¥€à¤•à¥ƒà¤¤" +Accession Number = "परिगà¥à¤°à¤¹à¤£ संखà¥à¤¯à¤¾" +Account = "खाता" +account_block_options_missing = "खाता बà¥à¤²à¥‰à¤• होने के कारण कà¥à¤› विकलà¥à¤ª हटा दिठगठहैं। विवरण : %%details%%" +account_has_alerts = "आपके खाते में सनà¥à¤¦à¥‡à¤¶ हैं " +Add a Library Card = "लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ जोड़ें" +Add a Note = "नोट जोड़े" +Add Tag = "टैग जोड़ें " +Add Tags = "टैग लगाà¤à¤‚" +Add to another list = "अनà¥à¤¯ सूची में शामिल करें " +Add to Book Bag = "बà¥à¤• बैग में शामिल करें " +Add to favorites = "सूची में सहेजें" +Add your comment = "अपनी टिपà¥à¤ªà¤£à¥€ में शामिल करें " +add_comment_fail_blank = "टिपà¥à¤ªà¤£à¥€ रिकà¥à¤¤ नहीं हो सकती." +add_comment_success = "टिपà¥à¤ªà¤£à¥€ जोड़ी गयी " +add_favorite_fail = "तà¥à¤°à¥à¤Ÿà¤¿: रिकॉरà¥à¤¡ नहीं सहेजा गया " +add_list_fail = "तà¥à¤°à¥à¤Ÿà¤¿: सूची नहीं बनाई गई " +add_other_libraries = "अनà¥à¤¯ पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥‹à¤‚ के लेख शामिल करें" +add_search = "खोज फ़ीलà¥à¤¡ जोड़ें " +add_search_group = "खोज समूह जोड़ें" +add_tag_error = "तà¥à¤°à¥à¤Ÿà¤¿:टैग को सहेजा नहीं जा सका " +add_tag_note = "रिकà¥à¤¤ सà¥à¤¥à¤¾à¤¨ टैग अलग कर देंगे। बहà¥-शबà¥à¤¦ टैग के लिठउदà¥à¤§à¤°à¤£à¥‹à¤‚ का उपयोग करें" +add_tag_success = "टैग सहेजा गया" +add_to_favorites_html = " जोड़ें <em>%%title%%</em> फेवरिटà¥à¤¸ में " +Address = "पता" +adv_search_all = "सà¤à¥€ कà¥à¤·à¥‡à¤¤à¥à¤°" +adv_search_author = "लेखक" +adv_search_callnumber = "बोधानक " +adv_search_filters = "फ़िलà¥à¤Ÿà¤° लागू किया गया" +adv_search_isn = "आईà¤à¤¸à¤¬à¥€à¤à¤¨/आईà¤à¤¸à¤à¤¸à¤à¤¨" +adv_search_journaltitle = "जरà¥à¤¨à¤² शीरà¥à¤·à¤•" +adv_search_label = "निमà¥à¤¨ को खोजें " +adv_search_publisher = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +adv_search_select_all = "सà¤à¥€ का चयन करे" +adv_search_series = "शृंखला" +adv_search_subject = "विषय" +adv_search_title = "शीरà¥à¤·à¤•" +adv_search_toc = "विषय - सूची" +adv_search_year = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ का वरà¥à¤·" +Advanced = "उनà¥à¤¨à¤¤" +Advanced Search = "उनà¥à¤¨à¤¤ खोज" +advSearchError_noRights = "कà¥à¤·à¤®à¤¾ करें, परनà¥à¤¤à¥ आपके पास उस खोज को संपादित करने की अनà¥à¤®à¤¤à¤¿ नहीं है। शायद आपका बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° सतà¥à¤° समापà¥à¤¤ हो गया है?" +advSearchError_notAdvanced = "जिस खोज को आप समà¥à¤ªà¤¾à¤¦à¤¿à¤¤ करना चाहते हैं वो उनà¥à¤¨à¤¤ खोज नहीं है " +advSearchError_notFound = "आपके दà¥à¤µà¤¾à¤°à¤¾ अनà¥à¤°à¥‹à¤§ की गई खोज उपलबà¥à¤§ नहीं है " +ajax_load_interrupted = "लोडिंग बाधित हो रही है" +ajaxview_label_information = "सूचना" +ajaxview_label_tools = "साधन" +alert_email_address = "निरà¥à¤§à¤¾à¤°à¤¿à¤¤ अलरà¥à¤Ÿ के परिणाम ईमेल पर à¤à¥‡à¤œà¥‡ जाà¤à¤‚गे" +All = "सà¤à¥€" +All Fields = "सà¤à¥€ फ़ीलà¥à¤¡à¥à¤¸" +All Pages Loaded = "सà¤à¥€ पेज लोड किठगà¤" +All Text = "पूरा टेकà¥à¤¸à¥à¤Ÿ" +alphabrowse_matches = "परिणाम" +alphabrowselink_html = "पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿà¤¿à¤¯à¥‹à¤‚ को बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें %%index%% से शà¥à¤°à¥‚ <a href="%%url%%">%%from%%</a>." +An error has occurred = "तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ है " +An error occurred during execution; please try again later. = "निषà¥à¤ªà¤¾à¤¦à¤¨ के दौरान à¤à¤• तà¥à¤°à¥à¤Ÿà¤¿ हà¥à¤ˆ; बाद में पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +AND = "और" +and = "और" +anonymous_tags = "अनाम टैग" +APA Citation = "à¤à¤ªà¥€à¤ उदà¥à¤§à¤°à¤£" +applied_filter = "फ़िलà¥à¤Ÿà¤° पà¥à¤°à¤¯à¥à¤•à¥à¤¤ किया गया " +Article = "लेख" +Ask a Librarian = "सहयता के लिठसंपरà¥à¤• करें" +Associated country = "संबदà¥à¤§ देश" +Audience = "शà¥à¤°à¥‹à¤¤à¤¾à¤—ण" +Audio = "ऑडियो" +authentication_error_admin = "हम इस समय आपको लॉग इन नहीं कर सकते हैं। सहायता के लिठकृपया अपने सिसà¥à¤Ÿà¤® वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾à¤ªà¤• से संपरà¥à¤• करें" +authentication_error_blank = "लॉगिन जानकारी रिकà¥à¤¤ नहीं हो सकती" +authentication_error_creation_blocked = "आपको खाता बनाने की अनà¥à¤®à¤¤à¤¿ नहीं है" +authentication_error_denied = "परिचय सूचना मेल नहीं खाती है! अà¤à¤¿à¤—मन असà¥à¤µà¥€à¤•à¥ƒà¤¤" +authentication_error_email_not_verified_html = "आपका ईमेल अà¤à¥€ तक सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ नहीं किया गया है। सतà¥à¤¯à¤¾à¤ªà¤¨ संदेश के लिठकृपया अपना सà¥à¤ªà¥ˆà¤® फ़िलà¥à¤Ÿà¤° देखें।. यदि आवशà¥à¤¯à¤• है, हम <a href="%%url%%"> पà¥à¤¨à¤ƒ सतà¥à¤¯à¤¾à¤ªà¤¨ ईमेल à¤à¥‡à¤œ सकते हैं </a>." +authentication_error_in_progress = "पà¥à¤°à¤®à¤¾à¤£à¥€à¤•à¤°à¤£ अनà¥à¤°à¥‹à¤§ संसाधित किया जा रहा है। यदि आपको पà¥à¤¨à¤ƒ शà¥à¤°à¥‚ करने की आवशà¥à¤¯à¤•à¤¤à¤¾ है तो कृपया बाद में पà¥à¤°à¤¯à¤¾à¤¸ करें ।" +authentication_error_invalid = "अमानà¥à¤¯ लॉगिन, कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +authentication_error_loggedout = "आप लोग आउट हो चà¥à¤•à¥‡ हैं " +authentication_error_technical = "हम इस समय आपको लॉग इन नहीं कर सकते हैं। पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +Author = "लेखक" +Author Browse = "लेखक बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +Author Notes = "लेखक नोटà¥à¤¸" +Author Results for = "लेखक परिणाम" +Author Search Results = "लेखक खोज परिणाम" +Authority File = "पà¥à¤°à¤¾à¤§à¤¿à¤•à¤°à¤£ फ़ाइल" +Authors = "लेखकों" +Authors Related to Your Search = "आपकी खोज से संबंधित लेखक" +Auto configuration is currently disabled = "वरà¥à¤¤à¤®à¤¾à¤¨ में ऑटो कॉनà¥à¤«à¤¼à¤¿à¤—रेशन अकà¥à¤·à¤® है" +auto_configure_description = "यदि यह à¤à¤• नया इंसà¥à¤Ÿà¥‰à¤²à¥‡à¤¶à¤¨ है, तो आप वीयू खोज के ऑटो कॉनà¥à¤«à¤¼à¤¿à¤—र उपकरण का उपयोग करके तà¥à¤°à¥à¤Ÿà¤¿ को ठीक करने में सकà¥à¤·à¤® हो सकते हैं " +auto_configure_disabled = "ऑटो कॉनà¥à¤«à¤¼à¤¿à¤—रेशन अकà¥à¤·à¤® है" +auto_configure_title = "ऑटो कॉनà¥à¤«à¤¼à¤¿à¤—र करें" +Availability = "उपलबà¥à¤§à¤¤à¤¾" +Available = "उपलबà¥à¤§" +Available Functionality = "उपलबà¥à¤§ कारà¥à¤¯à¤•à¥à¤·à¤®à¤¤à¤¾" +Awards = "पà¥à¤°à¤¸à¥à¤•à¤¾à¤°" +Back to Record = "वापस रिकॉरà¥à¤¡ करने के लिà¤" +Back to Search Results = "जाà¤à¤š परिणाम में वापस" +Backtrace = "पशà¥à¤µ-अनà¥à¤°à¥‡à¤–न" +Bag = "बैग" +Balance = "शेष" +Barcode = "बारकोड" +Be the first to leave a comment = "टिपà¥à¤ªà¤£à¥€ देने वाले पहले वà¥à¤¯à¤•à¥à¤¤à¤¿ बनें" +Be the first to tag this record = "इस रिकॉरà¥à¤¡ को टैग करने वाले पहले वà¥à¤¯à¤•à¥à¤¤à¤¿ बनें" +Bibliographic Details = "गà¥à¤°à¤‚थसूची विवरण" +Bibliography = "गà¥à¤°à¤¨à¥à¤¥à¤¸à¥‚ची" +Blu-ray Disc = "बà¥à¤²à¥‚ - रे डिसà¥à¤•" +Book = "पà¥à¤¸à¥à¤¤à¤•" +Book Bag = "किताब का बैग" +Book Chapter = "पà¥à¤¸à¥à¤¤à¤• अधà¥à¤¯à¤¾à¤¯" +Book Cover = "पà¥à¤¸à¥à¤¤à¤• आवरण" +bookbag_confirm_empty = "कà¥à¤¯à¤¾ आप वाकई अपने बà¥à¤• बैग को खाली करना चाहते हैं?" +bookbag_delete = "चयनित पà¥à¤¸à¥à¤¤à¤• बैग आइटम हटाà¤à¤‚ " +bookbag_delete_selected = "चयनित मिटाà¤à¤‚" +bookbag_email = "ईमेल चयनित बà¥à¤• बैग आइटम" +bookbag_email_selected = "ईमेल का चयन किया गया " +bookbag_export = "चयनित पà¥à¤¸à¥à¤¤à¤• बैग आइटम निरà¥à¤¯à¤¾à¤¤ करें " +bookbag_export_selected = "निरà¥à¤¯à¤¾à¤¤ चयनित है" +bookbag_full = "पूरà¥à¤£" +bookbag_full_msg = "आपका बà¥à¤• बैग फà¥à¤² है" +bookbag_is_empty = "आपका बà¥à¤• बैग खाली है" +bookbag_print_selected = "पà¥à¤°à¤¿à¤‚ट चà¥à¤¨à¤¾ गया" +bookbag_save = "चयनित बà¥à¤• बैग आइटम सहेजें " +bookbag_save_selected = "सहेजें" +Bookmark = "बà¥à¤•à¤®à¤¾à¤°à¥à¤•" +Books = "पà¥à¤¸à¥à¤¤à¤•à¥‡à¤‚" +Borrowing Location = "उधार सà¥à¤¥à¤¾à¤¨" +Braille = "बà¥à¤°à¥‡à¤²" +Breadcrumbs = "बà¥à¤°à¥ˆà¤¡à¤•à¥à¤°à¤‚बà¥à¤¸" +Brief View = "संकà¥à¤·à¤¿à¤ªà¥à¤¤ दृशà¥à¤¯" +Browse = "खोज" +Browse Alphabetically = "वरà¥à¤£à¤•à¥à¤°à¤® में बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +Browse for Authors = "लेखकों के लिठखोज करें" +Browse Home = "होम बà¥à¤°à¤¾à¤‰à¤œà¤¼" +Browse the Catalog = "कैटलॉग बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +Browse the Collection = "संगà¥à¤°à¤¹ बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +Browse the Collection Alphabetically = "वरà¥à¤£à¤¾à¤¨à¥à¤•à¥à¤°à¤® में संगà¥à¤°à¤¹ बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +browse_author = "लेखक" +browse_dewey = "कॉल नंबर (डेवी)" +browse_format = "सà¥à¤µà¤°à¥‚प" +browse_lcc = "कॉल नंबर( à¤à¤² सी)" +browse_publishDate = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ का वरà¥à¤· " +browse_title = "शीरà¥à¤·à¤•" +browse_topic = "विषय" +bulk_email_success = "आपका आइटम ईमेल किया गया था" +bulk_email_title = "लाइबà¥à¤°à¥‡à¤°à¥€ कैटलॉग आइटम" +bulk_error_missing = "कà¥à¤› डेटा गायब था। आपका अनà¥à¤°à¥‹à¤§ सफल नहीं हà¥à¤†" +bulk_export_not_supported = "आपके दà¥à¤µà¤¾à¤°à¤¾ चयनित रिकॉरà¥à¤¡ थोक निरà¥à¤¯à¤¾à¤¤ का समरà¥à¤¥à¤¨ नहीं करता है।" +bulk_fail = "माफ कीजिà¤, à¤à¤• गलती हà¥à¤ˆ है। कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +bulk_noitems_advice = "कोई आइटम नहीं चà¥à¤¨à¤¾ गया था। कृपया किसी आइटम के बगल वाले चेकबॉकà¥à¤¸ पर कà¥à¤²à¤¿à¤• करें और पà¥à¤¨à¤ƒ पà¥à¤°à¤¯à¤¾à¤¸ करें।" +bulk_save_error = "कà¥à¤› डेटा गायब था। आपके आइटम सहेजे नहीं गठथे।" +bulk_save_success = "आपका आइटम सफलतापूरà¥à¤µà¤• सहेजा गया था" +By = "दà¥à¤µà¤¾à¤°à¤¾" +by = "दà¥à¤µà¤¾à¤°à¤¾" +By Alphabetical = "वरà¥à¤£à¤®à¤¾à¤²à¤¾ दà¥à¤µà¤¾à¤°à¤¾" +By Author = "लेखक दà¥à¤µà¤¾à¤°à¤¾" +By Call Number = "बोधानक" +By Course = "By Course" +By Department = "कोरà¥à¤¸ दà¥à¤µà¤¾à¤°à¤¾" +By Era = "यà¥à¤— से" +By Genre = "शैली दà¥à¤µà¤¾à¤°à¤¾" +By Instructor = "पà¥à¤°à¤¶à¤¿à¤•à¥à¤·à¤• दà¥à¤µà¤¾à¤°à¤¾" +By Popularity = "लोकपà¥à¤°à¤¿à¤¯à¤¤à¤¾ से" +By Recent = "हाल ही में" +By Region = "कà¥à¤·à¥‡à¤¤à¥à¤° के आधार पर" +By Title = "शीरà¥à¤·à¤• से" +By Topic = "विषय दà¥à¤µà¤¾à¤°à¤¾" +Call Number = "बोधानक" +callnumber_abbrev = "कॉल " +Cannot find record = "रिकॉरà¥à¤¡ नहीं मिल रहा" +Cannot find similar records = "समान रिकॉरà¥à¤¡ नहीं मिल सकता है" +cannot set = "सेट नहीं कर सकता" +Cassette = "कैसेट" +cat_establish_account = "अपना खाता पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤² सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ करने के लिà¤, कृपया निमà¥à¤¨à¤²à¤¿à¤–ित जानकारी दरà¥à¤œ करें " +cat_password_abbrev = "कैटलॉग पासवरà¥à¤¡" +cat_username_abbrev = "कैटलॉग उपयोगकरà¥à¤¤à¤¾ नाम" +Catalog Login = "कैटलॉग लॉगिन" +Catalog Results = "कैटलॉग परिणाम" +catalog_login_desc = "अपनी लाइबà¥à¤°à¥‡à¤°à¥€ कैटलॉग साख पतà¥à¤° दरà¥à¤œ करें" +CD = "सीडी" +Change Email Address = "ईमेल पता बदलें" +Change Password = "पासवरà¥à¤¡ बदलें " +change_email_disabled = "आपको इस समय अपना ईमेल बदलने की अनà¥à¤®à¤¤à¤¿ नहीं है" +change_email_verification_reminder = "इस फॉरà¥à¤® को जमा करने से नठईमेल पते पर à¤à¤• ईमेल à¤à¥‡à¤œà¤¾ जाà¤à¤—ा; परिवरà¥à¤¤à¤¨ पà¥à¤°à¤à¤¾à¤µà¥€ करने के लिठआपको ईमेल में à¤à¤• लिंक पर कà¥à¤²à¤¿à¤• करना होगा।" +change_notification_email_message = "आपके ईमेल पते को बदलने के लिठ%%library%% को अनà¥à¤°à¥‹à¤§ किया गया था . यदि आपने यह अनà¥à¤°à¥‹à¤§ नहीं किया है, तो आप लॉग इन कर सकते हैं %url%% और अपने खाते की अखंडता की पà¥à¤·à¥à¤Ÿà¤¿ कर सकते हैं । यदि आपके पास कोई पà¥à¤°à¤¶à¥à¤¨ है तो कृपया सहायता के लिठ%%email%% से संपरà¥à¤• करें " +change_notification_email_subject = "खाता ईमेल परिवरà¥à¤¤à¤¨ अधिसूचना" +channel_add_more = "इस तरह और à¤à¥€ चैनल जोड़ें" +channel_browse = "अधिक रिकॉरà¥à¤¡ बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +channel_expand = "संबंधित चैनलों का अनà¥à¤µà¥‡à¤·à¤£ करें " +channel_explore = "चैनल का अनà¥à¤µà¥‡à¤·à¤£ करें" +channel_search = "खोज परिणामों के रूप में आइटम दिखाà¤à¤‚" +channel_searchbox_label = "अधिक चैनलों के लिठखोजें" +Check Hold = "होलà¥à¤¡ को चेक करें" +Check Recall = "रिकॉल करना की जाà¤à¤š करें " +check_profile = "उपयोगकरà¥à¤¤à¤¾ की जानकारी की जाà¤à¤š करें।" +Checked Out = "बाहर की जाà¤à¤š" +Checked Out Items = "चेक आउट आइटम" +Checkedout = "चेक आउट" +Checkout Date = "जाने की तिथि" +Chicago Citation = "शिकागो सà¥à¤Ÿà¤¾à¤‡à¤² उदà¥à¤§à¤°à¤£" +child_record_count = "%%count%% अà¤à¤¿à¤²à¥‡à¤–" +child_records = "अंतरà¥à¤µà¤¸à¥à¤¤à¥ / टà¥à¤•à¤¡à¤¼à¥‡" +Choose a Category to Begin Browsing = "बà¥à¤°à¤¾à¤‰à¤œà¤¿à¤‚ग शà¥à¤°à¥‚ करने के लिठà¤à¤• शà¥à¤°à¥‡à¤£à¥€ चà¥à¤¨à¥‡à¤‚ " +Choose a Column to Begin Browsing = "बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤¿à¤‚ग शà¥à¤°à¥‚ करने के लिठà¤à¤• कॉलम चà¥à¤¨à¥‡à¤‚" +Choose a List = "à¤à¤• सूची चà¥à¤¨à¥‡à¤‚ " +choose_login_method = "कृपया à¤à¤• लॉगिन विधि चà¥à¤¨à¥‡à¤‚" +citation_issue_abbrev = "नहीं." +citation_multipage_abbrev = "पीपी" +citation_singlepage_abbrev = " पी" +citation_volume_abbrev = "वॉलà¥à¤¯à¥‚म" +Cite this = "इसे उदà¥à¤§à¥ƒà¤¤ करें " +City = "शहर" +Clear = "सà¥à¤ªà¤·à¥à¤Ÿ" +clear_tag_filter = "सà¥à¤ªà¤·à¥à¤Ÿ निसà¥à¤ªà¤‚दन" +close = "बंद करे" +Code = "कोड" +Collection = "संगà¥à¤°à¤¹" +Collection Browse = "संगà¥à¤°à¤¹ बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें" +Collection Items = "संगà¥à¤°à¤¹ आइटम" +collection_disambiguation = "कई मिलान संगà¥à¤°à¤¹ मिले" +collection_empty = "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिठकोई आइटम नहीं है" +collection_view_record = "रिकॉरà¥à¤¡ देखें" +Collections = "संगà¥à¤°à¤¹" +comment_anonymous_user = "अनाम " +comment_error_load = "तà¥à¤°à¥à¤Ÿà¤¿: पà¥à¤¨: आरेखित टिपà¥à¤ªà¤£à¥€ सूची नहीं कर सका" +comment_error_save = "तà¥à¤°à¥à¤Ÿà¤¿: टिपà¥à¤ªà¤£à¥€ नहीं बचा सकता है" +Comments = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +Company/Entity = "कंपनी / à¤à¤‚टिटी" +Conference Proceeding = "समà¥à¤®à¥‡à¤²à¤¨ की कारà¥à¤¯à¤µà¤¾à¤¹à¥€" +Configuration = "वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¾ का पà¥à¤°à¤¾à¤°à¥‚प" +confirm_delete = "कà¥à¤¯à¤¾ आप वाकई इसे हटाना चाहते हैं ?" +confirm_delete_brief = "चीज़ें हटाà¤à¤‚ ?" +confirm_delete_library_card_brief = "लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ हटाà¤à¤‚ ?" +confirm_delete_library_card_text = "कà¥à¤¯à¤¾ आप वाकई इस लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ को हटाना चाहते हैं ?" +confirm_delete_list_brief = "सूची हटाà¤à¤ ?" +confirm_delete_list_text = "कà¥à¤¯à¤¾ आप वाकई इस सूची को हटाना चाहते हैं ?" +confirm_delete_tags_brief = "टैग हटाà¤à¤‚" +confirm_dialog_no = "नहीं " +confirm_dialog_yes = "हाà¤" +confirm_hold_cancel_all_text = "कà¥à¤¯à¤¾ आप अपने सà¤à¥€ वरà¥à¤¤à¤®à¤¾à¤¨ होलà¥à¤¡ को रदà¥à¤¦ करना चाहते हैं ?" +confirm_hold_cancel_selected_text = "कà¥à¤¯à¤¾ आप अपने चयनित होलà¥à¤¡ को रदà¥à¤¦ करना चाहते हैं ?" +confirm_ill_request_cancel_all_text = "कà¥à¤¯à¤¾ आप अपने सà¤à¥€ मौजूदा इंटरलाकिंग ऋण अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को रदà¥à¤¦ करना चाहते हैं ? " +confirm_ill_request_cancel_selected_text = "कà¥à¤¯à¤¾ आप अपने चयनित इंटरलॉनà¥à¤¡à¥à¤°à¥€ ऋण अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को रदà¥à¤¦ करना चाहते हैं ? " +confirm_new_password = "नठपासवरà¥à¤¡ की पà¥à¤·à¥à¤Ÿà¤¿ करें" +confirm_storage_retrieval_request_cancel_all_text = "कà¥à¤¯à¤¾ आप अपने सà¤à¥€ वरà¥à¤¤à¤®à¤¾à¤¨ संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को रदà¥à¤¦ करना चाहते हैं ?" +confirm_storage_retrieval_request_cancel_selected_text = "कà¥à¤¯à¤¾ आप अपने चयनित संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को रदà¥à¤¦ करना चाहते हैं ?" +Contents = "अंतरà¥à¤µà¤¸à¥à¤¤à¥ " +Contributing Source = "योगदान करने वाला सà¥à¤°à¥‹à¤¤" +Contributors = "योगदानकरà¥à¤¤à¤¾" +Coordinates = "निरà¥à¤¦à¥‡à¤¶à¤¾à¤‚क" +Copies = "पà¥à¤°à¤¤à¤¿à¤¯à¤¾à¤‚" +Copy = "पà¥à¤°à¤¤à¤¿" +Copyright = "पà¥à¤°à¤¤à¤¿à¤²à¤¿à¤ªà¥à¤¯à¤¾à¤§à¤¿à¤•à¤¾à¤°" +Corporate Author = "निगमित लेखक" +Corporate Authors = "निगमित लेखकों" +Country = "देश" +Course = "कोरà¥à¤¸" +Course Reserves = "आरकà¥à¤·à¤¿à¤¤ पाठà¥à¤¯à¤•à¥à¤°à¤®" +course_reserves_empty_list = "कोई मिलान पाठà¥à¤¯à¤•à¥à¤°à¤® आरकà¥à¤·à¤¿à¤¤ नहीं मिला " +Cover Image = "कवर छवि" +Create a List = "à¤à¤• सूची बनाà¤à¤ " +Create New Account = "नया खाता बनाà¤à¤ " +Create New Password = "नया पासवरà¥à¤¡ बनाà¤à¤‚ " +Created = "बनाया था" +Database = "डेटाबेस" +Date = "तिथि" +Date of birth = "जनà¥à¤® की तारीख" +Date of death = "मृतà¥à¤¯à¥ तिथि" +date_day_placeholder = "डी " +date_from = "से" +date_month_placeholder = "à¤à¤®" +date_to = "से" +date_year_placeholder = "बाई" +Debug Information = "डिबग जानकारी" +del_search = "खोज समूह निकालें " +Delete = "हटाना" +delete_account_confirm = "कà¥à¤¯à¤¾ आप इस खाते को हटाने के लिठसà¥à¤¨à¤¿à¤¶à¥à¤šà¤¿à¤¤ हैं ?" +delete_account_description_html = "सहेजे गठखोजों और पसंदीदा सूचियों को हटा दिया जाà¤à¤—ा। यदि आप चाहें तो आप अà¤à¥€ à¤à¥€ बाद में à¤à¤• नया खाता सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ कर सकते हैं।" +delete_account_failure = "खाता हटाने में विफल" +delete_account_success_message = "आपका खाता हटा दिया गया है। लॉग आउट कर रहा हूं..." +delete_account_title = "खाता हटा दो" +delete_all = "सà¤à¥€ हटा दो " +delete_comment_failure = "टिपà¥à¤ªà¤£à¥€ को हटाया नहीं जा सका।" +delete_comment_success = "टिपà¥à¤ªà¤£à¥€ हटा दी गई " +delete_list = "सूची हटाà¤à¤ " +delete_page = "पृषà¥à¤ हटाà¤à¤‚" +delete_selected = "चयनित मिटाà¤à¤‚" +delete_selected_favorites = "चयनित पसंदीदा हटाà¤à¤‚ " +delete_tag = "टैग हटाà¤à¤‚" +delete_tags = "टैग हटाà¤à¤‚" +delete_tags_by = "दà¥à¤µà¤¾à¤°à¤¾ टैग हटाà¤à¤‚" +Department = "विà¤à¤¾à¤—" +Description = "विवरण" +Desired Username = "वांछित उपयोगकरà¥à¤¤à¤¾ का नाम " +Detailed View = "विसà¥à¤¤à¥ƒà¤¤ विवरण" +Details = "सà¥à¤Ÿà¤¾à¤« देखें" +Displaying the top = "शीरà¥à¤· पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करना" +Document Inspector = "दसà¥à¤¤à¤¾à¤µà¥‡à¤œà¤¼ निरीकà¥à¤·à¤•" +Document Type = "दसà¥à¤¤à¤¾à¤µà¥‡à¤œà¤¼ का पà¥à¤°à¤•à¤¾à¤°" +DOI = "डिजिटल ऑबà¥à¤œà¥‡à¤•à¥à¤Ÿ पहचानकरà¥à¤¤à¤¾" +doi_detected_html = "आपकी खोज में डिजिटल ऑबà¥à¤œà¥‡à¤•à¥à¤Ÿ पहचानकरà¥à¤¤à¤¾ समà¥â€à¤®à¤¿à¤²à¤¿à¤¤ है। संसाधन की उपलबà¥à¤§à¤¤à¤¾ की जांच करने के लिठयहां कà¥à¤²à¤¿à¤• करें <a href="%%url%%">%%doi%%</a>" +Draw Search Box = "खोज बॉकà¥à¤¸ डà¥à¤°à¤¾ करें " +draw_searchbox_end = "डà¥à¤°à¤¾à¤‡à¤‚ग खतà¥à¤® करने के लिठमाउस छोड़ें " +draw_searchbox_start = "à¤à¤• कà¥à¤·à¥‡à¤¤à¥à¤° का चयन करने के लिठकà¥à¤²à¤¿à¤• करें और खींचें" +Due = "देय" +Due Date = "à¤à¥à¤—तान तिथि " +DVD = "डीवीडी " +eBook = "ई-पà¥à¤¸à¥à¤¤à¤•" +Edit = "संपादित करें " +edit = "संपादित करें" +Edit Library Card = "लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ संपादित करें" +Edit this Advanced Search = "इस उनà¥à¤¨à¤¤ खोज को संपादित करें" +edit_list = "संपादन सूची" +edit_list_fail = "कà¥à¤·à¤®à¤¾ करें, आपको इस सूची को संपादित करने की अनà¥à¤®à¤¤à¤¿ नहीं है" +edit_list_success = "सूची को सफलतापूरà¥à¤µà¤• अपडेट किया गया" +Edition = "संसà¥à¤•à¤°à¤£" +eds_expander_fulltext = "लेखों के पूरà¥à¤£ पाठके à¤à¥€à¤¤à¤° à¤à¥€ खोजें " +eds_expander_relatedsubjects = "समककà¥à¤· विषय लागू करें" +eds_expander_thesaurus = "संबंधित शबà¥à¤¦à¥‹à¤‚ को लागू करें" +eds_limiter_FC = "केवल सूचीपतà¥à¤° " +eds_limiter_FC1 = "केवल संसà¥à¤¥à¤¾à¤—त रिपोजिटरी " +eds_limiter_FM6 = "ऑडियो उपलबà¥à¤§ है" +eds_limiter_FR = "संदरà¥à¤ उपलबà¥à¤§ हैं " +eds_limiter_FT = "पूरà¥à¤£ पाठ" +eds_limiter_FT1 = "लाइबà¥à¤°à¥‡à¤°à¥€ कलेकà¥à¤¶à¤¨ में उपलबà¥à¤§ है" +eds_limiter_RV = "सहकरà¥à¤®à¥€ दà¥à¤µà¤¾à¤°à¤¾ समीकà¥à¤·à¤¿à¤¤ किया" +eds_mode_all = "सà¤à¥€ खोज शबà¥à¤¦ खोजें " +eds_mode_any = "किसी à¤à¥€ खोज शबà¥à¤¦ को खोजें " +eds_mode_bool = "बूलियन / वाकà¥à¤¯à¤¾à¤‚श" +eds_mode_smart = "सà¥à¤®à¤¾à¤°à¥à¤Ÿà¤Ÿà¥‡à¤•à¥à¤¸à¥à¤Ÿ सरà¥à¤šà¤¿à¤‚ग" +eds_modes_and_expanders = "खोज मोड और विसà¥à¤¤à¤¾à¤°à¤•" +Electronic = "इलेकà¥à¤Ÿà¥à¤°à¥‹à¤¨à¤¿à¤•" +Email = "ईमेल" +Email Address = "ईमेल पता " +Email address is invalid = "ईमेल पता अवैध है" +Email Record = "ईमेल रिकॉरà¥à¤¡ " +Email this = "इसे ईमेल करें" +Email this Search = "इस खोज को ईमेल करें" +email_change_pending_html = "आपके पास à¤à¤• %%pending%% ईमेल परिवरà¥à¤¤à¤¨ शेष है. परिवरà¥à¤¤à¤¨ को पूरा करने के लिठकृपया इस पते पर à¤à¥‡à¤œà¥‡ गठसतà¥à¤¯à¤¾à¤ªà¤¨ ईमेल के लिंक पर कà¥à¤²à¤¿à¤• करें। यदि आवशà¥à¤¯à¤• हो तो हम <a href="%%url%%"> सतà¥à¤¯à¤¾à¤ªà¤¨ ईमेल पà¥à¤¨à¤ƒ à¤à¥‡à¤œ सकते हैं </a>." +email_failure = "तà¥à¤°à¥à¤Ÿà¤¿ - संदेश à¤à¥‡à¤œà¤¾ नहीं जा सकता " +email_link = "संपरà¥à¤•" +email_login_desc = "कृपया लॉग इन करने के लिठनिमà¥à¤¨ लिंक का उपयोग करें। यदि आपने लॉगिन शà¥à¤°à¥‚ नहीं किया है, तो आप इस संदेश को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रूप से अनदेखा कर सकते हैं। कृपया धà¥à¤¯à¤¾à¤¨ दें कि लिंक केवल à¤à¤• सीमित समय के लिठवैध है और केवल उस डिवाइस के साथ जिससे आपने ईमेल पता दरà¥à¤œ किया था।" +email_login_link = "लॉगिन करने के लिठलिंक: <%%url%%>" +email_login_link_sent = "हमने आपके ईमेल पते पर à¤à¤• लॉगिन लिंक à¤à¥‡à¤œà¤¾ है। लिंक आने में कà¥à¤› समय लग सकता है। यदि आपको शीघà¥à¤° ही लिंक पà¥à¤°à¤¾à¤ªà¥à¤¤ नहीं होता है, तो कृपया अपना सà¥à¤ªà¥ˆà¤® फ़िलà¥à¤Ÿà¤° à¤à¥€ जांचें।" +email_login_requested = "आपके %%title%% ईमेल पते पर लॉगिन का अनà¥à¤°à¥‹à¤§ किया गया है." +email_login_subject = "%%title%% पे पà¥à¤°à¤µà¥‡à¤¶ करें " +email_maximum_recipients_note = "जà¥à¤¯à¤¾à¤¦à¤¾ से जà¥à¤¯à¤¾à¤¦à¤¾ %%max%% पà¥à¤°à¤¾à¤ªà¥à¤¤à¤•à¤°à¥à¤¤à¤¾ की अनà¥à¤®à¤¤à¤¿ है।" +email_multiple_recipients_note = "आप अलà¥à¤ªà¤µà¤¿à¤°à¤¾à¤® दà¥à¤µà¤¾à¤°à¤¾ अलग किठगठकई पà¥à¤°à¤¾à¤ªà¥à¤¤à¤•à¤°à¥à¤¤à¤¾ निरà¥à¤¦à¤¿à¤·à¥à¤Ÿ कर सकते हैं।" +email_selected = "ईमेल का चयन किया गया " +email_selected_favorites = "ईमेल चयनित पसंदीदा" +email_sending = "संदेश à¤à¥‡à¤œà¤¨à¤¾..." +email_subject = "विषय" +email_success = "मैसेज à¤à¥‡à¤œà¤¾ गया" +Empty = "खाली" +Empty Book Bag = "किताब की थैली खाली हैं।" +empty_search_disallowed = "वरà¥à¤¤à¤®à¤¾à¤¨ खोज लकà¥à¤·à¥à¤¯ के साथ रिकà¥à¤¤ पà¥à¤°à¤¶à¥à¤¨ की अनà¥à¤®à¤¤à¤¿ नहीं है" +Enable Auto Config = "ऑटो कॉनà¥à¤«à¤¼à¤¿à¤—रेशन सकà¥à¤·à¤® करें" +End Page = "अंतिम पृषà¥à¤ " +Era = "यà¥à¤— " +error_inconsistent_parameters = "माफ कीजिà¤, à¤à¤• गलती हà¥à¤ˆ है। असंगत मापदंडों का पता चला। " +error_page_parameter_list_heading = "पैरामीटर अनà¥à¤°à¥‹à¤§ " +Exception = "अपवाद" +Excerpt = "अंश" +exclude_facet = "मिलान परिणामों को छोड़ दें" +exclude_newspapers = "अखबार के लेखों को छोड़ दें" +Expires = "समय-सीमा समापà¥à¤¤" +Export = "निरà¥à¤¯à¤¾à¤¤ " +Export Favorites = "पसंदीदा निरà¥à¤¯à¤¾à¤¤ करें" +Export Items = "निरà¥à¤¯à¤¾à¤¤ आइटम" +Export Record = "निरà¥à¤¯à¤¾à¤¤ रिकॉरà¥à¤¡" +Export to = "को निरà¥à¤¯à¤¾à¤¤" +export_choose_format = "कृपया à¤à¤• निरà¥à¤¯à¤¾à¤¤ पà¥à¤°à¤¾à¤°à¥‚प चà¥à¤¨à¥‡à¤‚" +export_download = "फ़ाइल डाउनलोड करें" +export_exporting = "निरà¥à¤¯à¤¾à¤¤ फ़ाइल बनाना" +export_fail = "आपके आइटम निरà¥à¤¯à¤¾à¤¤ नहीं किठगठथे " +export_invalid_format = "चयनित निरà¥à¤¯à¤¾à¤¤ पà¥à¤°à¤¾à¤°à¥‚प इस रिकॉरà¥à¤¡ दà¥à¤µà¤¾à¤°à¤¾ समरà¥à¤¥à¤¿à¤¤ नहीं है" +export_missing = "कà¥à¤› डेटा गायब था। आपके आइटम निरà¥à¤¯à¤¾à¤¤ नहीं किठगठथे" +export_no_formats = "यह रिकॉरà¥à¤¡ निरà¥à¤¯à¤¾à¤¤ का समरà¥à¤¥à¤¨ नहीं करता है" +export_save = "फाइल सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ करें" +export_selected = "निरà¥à¤¯à¤¾à¤¤ चयनित है" +export_selected_favorites = "पसंदीदा निरà¥à¤¯à¤¾à¤¤ करें" +export_send = "à¤à¥‡à¤œà¤¨à¤¾ %%service%%" +export_success = "निरà¥à¤¯à¤¾à¤¤ तैयार है" +export_unsupported_format = "असमरà¥à¤¥à¤¿à¤¤ निरà¥à¤¯à¤¾à¤¤ पà¥à¤°à¤¾à¤°à¥‚प" +external_auth_heading = "लाइसेंस पà¥à¤°à¤¾à¤ªà¥à¤¤ सामगà¥à¤°à¥€ तक पहà¥à¤‚च " +external_auth_login_message = "लाइसेंस पà¥à¤°à¤¾à¤ªà¥à¤¤ सामगà¥à¤°à¥€ का उपयोग करने के लिठलॉग इन करें " +external_auth_unauthorized = "आप लाइसेंस पà¥à¤°à¤¾à¤ªà¥à¤¤ सामगà¥à¤°à¥€ का उपयोग करने के लिठअधिकृत नहीं हैं " +external_auth_unauthorized_desc = "आपकी लॉगिन विधि लाइसेंस पà¥à¤°à¤¾à¤ªà¥à¤¤ सामगà¥à¤°à¥€ तक पहà¥à¤à¤š पà¥à¤°à¤¦à¤¾à¤¨ नहीं करती है। कृपया लॉग आउट करें और फिर किसी अनà¥à¤¯ विधि का उपयोग करके लॉग इन करें " +facet_list_empty = "कोई डेटा उपलबà¥à¤§ नहीं है" +facet_list_for = "के लिठपहलू की सूची %%field%%" +FAQs = "अकà¥à¤¸à¤° पूछे जाने वाले पà¥à¤°à¤¶à¥à¤¨" +fav_delete = "चयनित पसंदीदा हटाà¤à¤‚" +fav_delete_deleting = "आपके पसंदीदाओं को हटाया जा रहा है" +fav_delete_fail = "माफ कीजिà¤, à¤à¤• गलती हà¥à¤ˆ है। आपके पसंदीदा (ओं) को हटाया नहीं गया था" +fav_delete_missing = "कà¥à¤› डेटा गायब था। आपके पसंदीदा (ओं) को हटाया नहीं गया था " +fav_delete_success = "आपके पसंदीदा (ओं) को हटा दिया गया" +fav_delete_warn = "आप इन पसंदीदा को अपनी सà¤à¥€ सूचियों से हटाने वाले हैं - यदि आप केवल किसी विशिषà¥à¤Ÿ सूची से पसंदीदा हटाना चाहते हैं, तो कृपया हटाने से पहले सूची का चयन करें" +fav_email_fail = "माफ कीजिà¤, à¤à¤• गलती हà¥à¤ˆ है। आपके पसंदीदा (ईमेल) ईमेल नहीं किठगठथे" +fav_email_missing = "कà¥à¤› डेटा गायब था। आपके पसंदीदा (ईमेल) ईमेल नहीं किठगठथे " +fav_email_success = "आपके पसंदीदा (ओं) को अनà¥à¤°à¥‹à¤§ के रूप में ईमेल किया गया था" +fav_export = "पसंदीदा निरà¥à¤¯à¤¾à¤¤ करें" +fav_list_delete = "सूची हटा दी गई है" +fav_list_delete_cancel = "यह सूची नहीं हटाई गई" +fav_list_delete_fail = "माफ कीजिà¤, à¤à¤• गलती हà¥à¤ˆ है। आपकी सूची नहीं हटाई गई " +Favorites = "बची हà¥à¤ˆ वसà¥à¤¤à¥à¤à¤" +Fee = "शà¥à¤²à¥à¤•" +Feedback = "पà¥à¤°à¤¤à¤¿à¤ªà¥à¤·à¥à¤Ÿà¤¿" +feedback_email = "ईमेल" +feedback_name = "नाम" +Field of activity = "गतिविधि का कà¥à¤·à¥‡à¤¤à¥à¤°" +File Description = "फाइल विवरण" +Filter = "फ़िलà¥à¤Ÿà¤°" +Filter Collection = "फ़िलà¥à¤Ÿà¤° संगà¥à¤°à¤¹" +filter_tags = "फ़िलà¥à¤Ÿà¤° टैग" +filter_toggle_entries = "%%count%% फ़िलà¥à¤Ÿà¤°" +filter_wildcard = "कोई à¤à¥€" +Find = "खोज" +Find More = "और अधिक ढूंढें" +Find New Items = "नई वसà¥à¤¤à¥à¤“ं का पता लगाà¤à¤‚ " +Finding Aid = "सहायता ढूà¤à¤¢à¤¨à¤¾" +Fine = "ठीक" +Fine Date = "जà¥à¤°à¥à¤®à¤¾à¤¨à¥‡ की तारीख" +fine_limit_patron = "आप अपनी जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾ सीमा तक पहà¥à¤à¤š चà¥à¤•à¥‡ हैं और वसà¥à¤¤à¥à¤“ं का नवीनीकरण नहीं कर सकते हैं " +Fines = "जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾" +First = "पà¥à¤°à¤¥à¤®" +First Name = "पहला नाम" +First Search Result = "पहला खोज परिणाम" +fix_metadata = "हां, मेटाडेटा ठीक करें; मैं इंतजार करूà¤à¤—ा" +for search = "खोज के लिà¤" +Forgot Password = "पासवरà¥à¤¡ à¤à¥‚ल गà¤" +Form Submitted! = "फॉरà¥à¤® पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ किया गया !" +Format = "सà¥à¤µà¤°à¥‚प" +From = "से" +Full description = "पूरà¥à¤£ विवरण" +Full text is not displayed to guests = "पूरà¥à¤£ पाठमेहमानों को पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ नहीं किया जाता है" +fulltext_limit = "पूरà¥à¤£ पाठउपलबà¥à¤§ लेखों की सीमा" +Gender = "लिंग" +Genre = "शैली" +Geographic Search = "à¤à¥Œà¤—ोलिक खोज" +Geographic Terms = "à¤à¥Œà¤—ोलिक नियम" +Geography = "à¤à¥‚गोल" +Get full text = "पूरà¥à¤£ पाठपà¥à¤°à¤¾à¤ªà¥à¤¤ करें " +Get more information = "जà¥à¤¯à¤¾à¤¦à¤¾ जानकारी पाइये" +Get RSS Feed = "RSS फ़ीड पà¥à¤°à¤¾à¤ªà¥à¤¤ करें" +Globe = "गà¥à¤²à¥‹à¤¬" +Go = "जाओ" +Go to Standard View = "सà¥à¤Ÿà¥ˆà¤‚डरà¥à¤¡ वà¥à¤¯à¥‚ पर जाà¤à¤‚ " +go_to_list = "सूची पर जाà¤à¤‚" +google_map_cluster = "समूह" +google_map_cluster_points = "समूहअंक" +Government Document = "सरकारी दसà¥à¤¤à¤¾à¤µà¥‡à¤œ" +Grid = "जाल" +Group = "समूह" +group_AND = "सà¤à¥€ समूह" +group_OR = "कोई à¤à¥€ समूह" +Has Illustrations = "दृषà¥à¤Ÿà¤¾à¤‚त है" +Help = "मदद" +Help with Advanced Search = "उनà¥à¤¨à¤¤ खोज में सहायता करें" +Help with Search Operators = "खोज ऑपरेटरों के साथ मदद करें" +help_page_missing = "अनà¥à¤°à¥‹à¤§à¤¿à¤¤ सहायता पृषà¥à¤ मौजूद नहीं है" +hierarchy_hide_tree = "पूरà¥à¤£ पदानà¥à¤•à¥à¤°à¤® छà¥à¤ªà¤¾à¤à¤‚" +hierarchy_show_tree = "पूरà¥à¤£ पदानà¥à¤•à¥à¤°à¤® दिखाà¤à¤‚" +hierarchy_tree = "पà¥à¤°à¤¸à¤‚ग" +hierarchy_tree_error = "कà¥à¤·à¤®à¤¾ करें, हम पदानà¥à¤•à¥à¤°à¤®à¤¿à¤¤ पेड़ को लोड करने में असमरà¥à¤¥ थे" +hierarchy_view_context = "पà¥à¤°à¤¸à¤‚ग देखें" +History = "इतिहास" +history_delete = "हटाना / मिटाना" +history_delete_link = "हटाना " +history_empty_search = "कà¥à¤› à¤à¥€ (खाली खोज)" +history_limits = "सीमाà¤à¤‚" +history_no_searches = "वरà¥à¤¤à¤®à¤¾à¤¨ में आपके इतिहास में कोई खोज नहीं है" +history_purge = "बेशà¥à¤®à¤¾à¤° खोजों को मिटा दें" +history_recent_searches = "हाल की खोजें" +history_results = "परिणाम" +history_save = "सहेजें ?" +history_save_link = "सहेजें " +history_saved_searches = "सहेजी गई खोज" +history_schedule = "चेतावनी अनà¥à¤¸à¥‚ची" +history_search = "खोज" +history_time = "समय" +hold_available = "उठाने के लिठउपलबà¥à¤§" +hold_available_until = "उठाने के लिठउपलबà¥à¤§ %%date%%" +hold_cancel = "होलà¥à¤¡ रदà¥à¤¦ करें" +hold_cancel_all = "सà¤à¥€ होलà¥à¤¡ रदà¥à¤¦ करें" +hold_cancel_fail = "आपका अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ नहीं किया गया था अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें " +hold_cancel_selected = "चयनित होलà¥à¤¡ रदà¥à¤¦ करें" +hold_cancel_success = "आपका अनà¥à¤°à¥‹à¤§ सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया था" +hold_cancel_success_items = "%%count%% अनà¥à¤°à¥‹à¤§ (ओं) को सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया" +hold_date_invalid = "कृपया à¤à¤• मानà¥à¤¯ तिथि पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें" +hold_date_past = "कृपया à¤à¤µà¤¿à¤·à¥à¤¯ में à¤à¤• तारीख दरà¥à¤œ करें " +hold_empty_selection = "कोई होलà¥à¤¡ नहीं चà¥à¤¨à¤¾ गया था" +hold_error_blocked = "इस आइटम पर पकड़ रखने के लिठआपके पास परà¥à¤¯à¤¾à¤ªà¥à¤¤ विशेषाधिकार नहीं हैं " +hold_error_fail = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें " +hold_invalid_pickup = "à¤à¤• अमानà¥à¤¯ पिकअप सà¥à¤¥à¤¾à¤¨ दरà¥à¤œ किया गया था। कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +hold_invalid_request_group = "à¤à¤• अमानà¥à¤¯ होलà¥à¤¡ अनà¥à¤°à¥‹à¤§ समूह दरà¥à¤œ किया गया था। कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +hold_items_available = "आइटम नहीं मिलने के कारण होलà¥à¤¡ नहीं कर सकते हैं " +hold_login = "जानकारी के लिठलॉग इन करें और याद रखें" +hold_place = "सà¥à¤¥à¤¾à¤¨ का अनà¥à¤°à¥‹à¤§" +hold_place_fail_missing = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। कà¥à¤› डेटा गायब था। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +hold_place_success_html = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा। <a href="%%url%%">आपका धारण और सà¥à¤®à¤°à¤£</a>." +hold_profile_html = "जानकारी को रखने और वापस बà¥à¤²à¤¾à¤¨à¥‡ के लिà¤, कृपया अपनी सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ करें <a href="%%url%%">लाइबà¥à¤°à¥‡à¤°à¥€ कैटलॉग पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤²</a>." +hold_queue_position = "कतार की सà¥à¤¥à¤¿à¤¤à¤¿ " +hold_record_already_on_loan = "आपके पास पहले से ही ऋण पर रिकॉरà¥à¤¡ है" +hold_request_group = "से अनà¥à¤°à¥‹à¤§" +hold_requested_group = "से निवेदन किया" +hold_required_by = "इसके बाद की आवशà¥à¤¯à¤•à¤¤à¤¾ नहीं है" +hold_success = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा" +Holdings = "होलà¥à¤¡à¤¿à¤‚गà¥à¤¸ " +Holdings at Other Libraries = "अनà¥à¤¯ पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥‹à¤‚ में धारण" +holdings_details_from = "होलà¥à¤¡à¤¿à¤‚गà¥à¤¸ विवरण से %%location%%" +Holdings_notes = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +Holds = "धारण है" +Holds and Recalls = "धारण और सà¥à¤®à¤°à¤£" +Home = "घर" +home_browse = "बà¥à¤°à¤¾à¤‰à¤œà¤¼ करें -" +HTML Full Text = "à¤à¤šà¤Ÿà¥€à¤à¤®à¤à¤² पूरà¥à¤£ पाठ" +Identifier = "पहचानकरà¥à¤¤à¤¾" +ill_request_available = "उठाने के लिठउपलबà¥à¤§" +ill_request_cancel = "ऋण अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ करें " +ill_request_cancel_all = "सà¤à¥€ अंतःकà¥à¤°à¤¿à¤¯à¤¾ ऋण अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ करें" +ill_request_cancel_fail = "आपका अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ नहीं किया गया था अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +ill_request_cancel_selected = "चयनित अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ करें" +ill_request_cancel_success = "आपका अनà¥à¤°à¥‹à¤§ सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया था" +ill_request_cancel_success_items = "%%count%% अनà¥à¤°à¥‹à¤§ (ओं) को सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया" +ill_request_canceled = "रदà¥à¤¦" +ill_request_check_text = "अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण अनà¥à¤°à¥‹à¤§ की जाà¤à¤š करें" +ill_request_comments = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +ill_request_date_invalid = "कृपया à¤à¤• मानà¥à¤¯ तिथि पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें" +ill_request_date_past = "कृपया à¤à¤µà¤¿à¤·à¥à¤¯ में à¤à¤• तारीख दरà¥à¤œ करें" +ill_request_empty_selection = "कोई अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण ऋण अनà¥à¤°à¥‹à¤§ चयनित नहीं थे" +ill_request_error_blocked = "आपके पास इस मद पर à¤à¤• अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण अनà¥à¤°à¥‹à¤§ रखने के लिठपरà¥à¤¯à¤¾à¤ªà¥à¤¤ विशेषाधिकार नहीं हैं " +ill_request_error_fail = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +ill_request_error_technical = "सिसà¥à¤Ÿà¤® तà¥à¤°à¥à¤Ÿà¤¿ के कारण आपका अनà¥à¤°à¥‹à¤§ विफल हो गया। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +ill_request_error_unknown_patron_source = "संरकà¥à¤·à¤• ऋण ऋण अनà¥à¤°à¥‹à¤§à¥‹à¤‚ में पहचान नहीं की है" +ill_request_invalid_pickup = "à¤à¤• अमानà¥à¤¯ पिकअप सà¥à¤¥à¤¾à¤¨ दरà¥à¤œ किया गया था। कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +ill_request_pick_up_library = "लाइबà¥à¤°à¥‡à¤°à¥€ को पिकअप करो" +ill_request_pick_up_location = "पिकअप की जगह " +ill_request_place_fail_missing = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। कà¥à¤› डेटा गायब था। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +ill_request_place_success = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा" +ill_request_place_success_html = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा। <a href="%%url%%">ऋण अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को बाधित करता है</a>." +ill_request_place_text = "à¤à¤• अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण अनà¥à¤°à¥‹à¤§ रखें" +ill_request_processed = "कारà¥à¤°à¤µà¤¾à¤ˆ कृत" +ill_request_profile_html = "अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ लोन अनà¥à¤°à¥‹à¤§ जानकारी के लिà¤, कृपया अपनी सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ करें <a href="%%url%%">लाइबà¥à¤°à¥‡à¤°à¥€ कैटलॉग पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤²</a>." +ill_request_submit_text = "सà¥à¤¥à¤¾à¤¨ का अनà¥à¤°à¥‹à¤§" +Illustrated = "उदाहरण से सà¥à¤ªà¤·à¥à¤Ÿ" +ils_account_create_error = "आपका खाता हमारी पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पà¥à¤°à¤¬à¤‚धन पà¥à¤°à¤£à¤¾à¤²à¥€ में नहीं बनाया जा सकता है। यदि समसà¥à¤¯à¤¾ बनी रहती है, तो कृपया अपने पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ से संपरà¥à¤• करें " +ils_action_unavailable = "अनà¥à¤°à¥‹à¤§à¤¿à¤¤ कारà¥à¤¯ सकà¥à¤°à¤¿à¤¯ लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ के साथ उपलबà¥à¤§ नहीं है " +ils_connection_failed = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पà¥à¤°à¤¬à¤‚धन पà¥à¤°à¤£à¤¾à¤²à¥€ के संपरà¥à¤• विफल रहे। आपके पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ खाते से संबंधित जानकारी पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ नहीं की जा सकती है। यदि समसà¥à¤¯à¤¾ बनी रहती है, तो कृपया अपने पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ से संपरà¥à¤• करें" +ils_offline_holdings_message = "वरà¥à¤¤à¤®à¤¾à¤¨ में होलà¥à¤¡à¤¿à¤‚गà¥à¤¸ और आइटम उपलबà¥à¤§à¤¤à¤¾ की जानकारी उपलबà¥à¤§ नहीं है। कृपया इससे होने वाली किसी à¤à¥€ असà¥à¤µà¤¿à¤§à¤¾ के लिठहमारी कà¥à¤·à¤®à¤¾ याचना सà¥à¤µà¥€à¤•à¤¾à¤° करें और आगे की सहायता के लिठहमसे संपरà¥à¤• करें " +ils_offline_home_message = "इस दौरान आपके खाते का विवरण और लाइव आइटम की जानकारी उपलबà¥à¤§ नहीं होगी। कृपया इससे होने वाली किसी à¤à¥€ असà¥à¤µà¤¿à¤§à¤¾ के लिठहमारी कà¥à¤·à¤®à¤¾ याचना सà¥à¤µà¥€à¤•à¤¾à¤° करें और आगे की सहायता के लिठहमसे संपरà¥à¤• करें" +ils_offline_login_message = "इस दौरान आपके खाते का विवरण उपलबà¥à¤§ नहीं होगा। कृपया इससे होने वाली किसी à¤à¥€ असà¥à¤µà¤¿à¤§à¤¾ के लिठहमारी कà¥à¤·à¤®à¤¾ याचना सà¥à¤µà¥€à¤•à¤¾à¤° करें और आगे की सहायता के लिठहमसे संपरà¥à¤• करें " +ils_offline_status = "हमारा लाइबà¥à¤°à¥‡à¤°à¥€ मैनेजमेंट सिसà¥à¤Ÿà¤® अà¤à¥€ रखरखाव के अधीन है" +ils_offline_title = "रखरखाव के तहत पà¥à¤°à¤£à¤¾à¤²à¥€" +ils_transaction_history_disabled = "सकà¥à¤°à¤¿à¤¯ लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ के लिठऋण इतिहास सकà¥à¤·à¤® नहीं है" +Import Record = "आयात रिकॉरà¥à¤¡ " +in = "में" +In This Collection = "इस संगà¥à¤°à¤¹ में" +in_collection_label = "संगà¥à¤°à¤¹ में " +include_synonyms = "समानारà¥à¤¥à¥€ शबà¥à¤¦ का उपयोग करके परिणामों का विसà¥à¤¤à¤¾à¤° करें" +Index Terms = "अनà¥à¤•à¥à¤°à¤® की शरà¥à¤¤à¥‡à¤‚" +Indexes = "सूचक" +information = "जानकारी" +Institution = "संसà¥à¤¥à¤¾à¤¨" +Institutional Login = "संसà¥à¤¥à¤¾à¤—त लॉगिन" +institutional_login_desc = "अपना परिसर में उपयोगकरà¥à¤¤à¤¾ नाम और पासवरà¥à¤¡ दरà¥à¤œ करें " +Instructor = "पà¥à¤°à¤¶à¤¿à¤•à¥à¤·à¤•" +Interlibrary Loan Requests = "ऋण अनà¥à¤°à¥‹à¤§à¥‹à¤‚ को बाधित करता है " +Internet = "इंटरनेट" +Invalid Patron Login = "संरकà¥à¤·à¤• लॉगिन अमानà¥à¤¯" +Invalid phone number. = "फोन नंबर अवैध" +Invalid Recipient Email Address = "पà¥à¤°à¤¾à¤ªà¥à¤¤à¤•à¤°à¥à¤¤à¤¾ ईमेल पता अमानà¥à¤¯ " +Invalid Sender Email Address = "पà¥à¤°à¥‡à¤·à¤• ईमेल पता अमानà¥à¤¯ " +ISBN = "आईà¤à¤¸à¤¬à¥€à¤à¤¨" +ISBN/ISSN = "आईà¤à¤¸à¤¬à¥€à¤à¤¨ / आईà¤à¤¸à¤à¤¸à¤à¤¨" +ISSN = "आईà¤à¤¸à¤à¤¸à¤à¤¨" +Issue = "निरà¥à¤—म" +Item Description = "वसà¥à¤¤à¥ वरà¥à¤£à¤¨" +Item Notes = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤ " +Item removed from favorites = "पसंदीदा से हटाया गया आइटम " +Item removed from list = "सूची से आइटम हटाया गया" +Items = " आइटम" +items = " आइटम" +items_added_to_bookbag = "%%count%% आइटम (ओं) को आपके बà¥à¤• बैग में जोड़ा गया" +items_already_in_bookbag = "%%count%% आइटम (ओं) या तो पहले से ही आपके बà¥à¤• बैग में हैं या नहीं जोड़े जा सकते हैं" +Journal = "पतà¥à¤°à¤¿à¤•à¤¾" +Journal Articles = "पतà¥à¤°à¤¿à¤•à¤¾ लेख" +Journal Info = "जरà¥à¤¨à¤² की जानकारी" +Journal Title = "जरà¥à¤¨à¤² शीरà¥à¤·à¤•" +Journals = "पतà¥à¤°à¤¿à¤•à¤¾à¤“ं" +Jump to = "जाना" +just_cataloged = "सिरà¥à¤« कैटलॉग किया" +Keyword = "संकेत शबà¥à¤¦" +Keyword Filter = "संकेत शबà¥à¤¦ निसà¥à¤ªà¤‚दन" +Kit = "सामगà¥à¤°à¥€ का à¤à¥‹à¤²à¤¾" +Language = "à¤à¤¾à¤·à¤¾" +large = "विशाल" +Last = "अंतिम" +Last Modified = "अंतिम बार संशोधित" +Last Name = "उपनाम" +Last Search Result = "पिछला खोज परिणाम" +less = "नà¥à¤¯à¥‚न" +libphonenumber_invalid = "फ़ोन नंबर अमानà¥à¤¯ है" +libphonenumber_invalidcountry = "देश कॉलिंग कोड अमानà¥à¤¯" +libphonenumber_invalidregion = "कà¥à¤·à¥‡à¤¤à¥à¤° कोड अमानà¥à¤¯" +libphonenumber_notanumber = "आपूरà¥à¤¤à¤¿ की गई सà¥à¤Ÿà¥à¤°à¤¿à¤‚ग फोन नंबर नहीं लगती थी" +libphonenumber_toolong = "आपूरà¥à¤¤à¤¿ की गई सà¥à¤Ÿà¥à¤°à¤¿à¤‚ग à¤à¤• फ़ोन नंबर होने के लिठबहà¥à¤¤ लंबी है" +libphonenumber_tooshort = "आपूरà¥à¤¤à¤¿ की गई सà¥à¤Ÿà¥à¤°à¤¿à¤‚ग फ़ोन नंबर होने के लिठबहà¥à¤¤ कम है" +libphonenumber_tooshortidd = "आईडीडी के बाद फोन नंबर बहà¥à¤¤ कम" +Library = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯" +Library Card = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पतà¥à¤°à¤•" +Library Card Deleted = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पतà¥à¤°à¤• को हटा दिया गया" +Library Card Name = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पतà¥à¤°à¤• का नाम" +Library Cards = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पतà¥à¤°à¤•" +Library Cards Disabled = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ पतà¥à¤°à¤• अकà¥à¤·à¤®" +Library Catalog Password = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची पासवरà¥à¤¡" +Library Catalog Profile = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤² " +Library Catalog Record = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची रिकॉरà¥à¤¡" +Library Catalog Search = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची खोज" +Library Catalog Search Result = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची परिणाम खोजें" +Library Catalog Username = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ सूची उपयोगकरà¥à¤¤à¤¾ का नाम" +Library Web Search = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ वेब खोज" +library_card_edit_password_placeholder = "नया पासवरà¥à¤¡" +lightbox_error = "तà¥à¤°à¥à¤Ÿà¤¿: पॉपअप बॉकà¥à¤¸ लोड नहीं कर सकता " +Limit To = "सीमित रखो" +Link to full results = "पूरà¥à¤£ परिणामों के लिठलिंक" +List = "सूची" +List Tags = "सूची टैग" +list_access_denied = "आपको इस सूची को देखने की अनà¥à¤®à¤¤à¤¿ नहीं है" +list_edit_name_required = "सूची नाम आवशà¥à¤¯à¤• है" +load_tag_error = "तà¥à¤°à¥à¤Ÿà¤¿: टैग लोड नहीं कर सका" +Loading = "लोड हो रहा है" +Loan History = "ऋण का इतिहास" +loan_history_empty = "आपके पास ऋण इतिहास में कोई ऋण नहीं है" +Local Login = "सà¥à¤¥à¤¾à¤¨à¥€à¤¯ लॉगिन" +local_login_desc = "इस साइट के लिठआपके दà¥à¤µà¤¾à¤°à¤¾ बनाया गया उपयोगकरà¥à¤¤à¤¾ नाम और पासवरà¥à¤¡ दरà¥à¤œ करें" +Located = "सà¥à¤¥à¤¿à¤¤" +Location = "सà¥à¤¥à¤¾à¤¨" +Log Out = "लॉग आउट" +Login = "लॉग इन करें" +Login for full access = "पूरà¥à¤£ पहà¥à¤à¤š के लिठलॉगिन करें" +login_disabled = "इस समय लॉगिन उपलबà¥à¤§ नहीं है" +login_target = "पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯" +Logout = "लॉगआउट" +Main Author = "मà¥à¤–à¥à¤¯ लेखक" +Main Authors = "मà¥à¤–à¥à¤¯ लेखकों" +Major Categories = "पà¥à¤°à¤®à¥à¤– शà¥à¤°à¥‡à¤£à¤¿à¤¯à¤¾à¤ " +Manage Tags = "टैग पà¥à¤°à¤¬à¤‚धित करें" +Manuscript = "हसà¥à¤¤à¤²à¤¿à¤ªà¤¿ " +Map = "नकà¥à¤¶à¤¾" +Map View = "नकà¥à¤¶à¤¾ देखें " +map_results_label = "इस सà¥à¤¥à¤¾à¤¨ पर" +Maps = "मानचितà¥à¤°" +Media Format = "मीडिया पà¥à¤°à¤¾à¤°à¥‚प" +medium = "माधà¥à¤¯à¤®" +MeSH Terms = "à¤à¤®à¤‡à¤à¤¸à¤à¤š शरà¥à¤¤à¥‡à¤‚ " +Message = "संदेश" +Message From Sender = "पà¥à¤°à¥‡à¤·à¤• से संदेश" +Metadata Prefix = "मेटाडेटा उपसरà¥à¤—" +Microfilm = "माइकà¥à¤°à¥‹à¤«à¤¿à¤²à¥à¤®" +MLA Citation = "à¤à¤®à¤à¤²à¤ उदà¥à¤§à¤°à¤£" +Mobile Number = "मोबाइल नंबर" +mobile_link = "आप à¤à¤• मोबाइल डिवाइस पर दिखाई देते हैं; मोबाइल देखने के लिठसà¥à¤µà¤¿à¤š ?" +Monograph Title = "मोनोगà¥à¤°à¤¾à¤« शीरà¥à¤·à¤•" +more = "अधिक" +More catalog results = "अधिक सूची परिणाम" +More options = "अधिक विकलà¥à¤ª " +More Summon results = "अधिक समन परिणाम" +More Topics = "अधिक विषय" +more_authors_abbrev = "और अनà¥à¤¯" +more_info_toggle = "अधिक जानकारी दिखाà¤à¤‚ / छिपाà¤à¤ " +more_topics = "%%count%% अधिक विषय" +Most Recent Received Issues = "सबसे हाल ही में पà¥à¤°à¤¾à¤ªà¥à¤¤ मà¥à¤¦à¥à¤¦à¥‡ " +Multiple Call Numbers = "à¤à¤•à¤¾à¤§à¤¿à¤• बोधानक" +Multiple Locations = "à¤à¤•à¤¾à¤§à¤¿à¤• सà¥à¤¥à¤¾à¤¨" +Musical Score = "संगीत सà¥à¤•à¥‹à¤°" +My Favorites = "पसंदीदा" +My Fines = "जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾" +My Holds = "रखती है" +My Profile = "रूपरेखा" +Narrow Search = "संकीरà¥à¤£ खोज" +navigate_back = "पीछे की ओर" +nearby_items = "आइटम के पास "%%title%%"" +Need Help? = "सहायता चाहिठ?" +New Item Feed = "नई वसà¥à¤¤à¥ फ़ीड" +New Item Search = "नई वसà¥à¤¤à¥ खोज" +New Item Search Results = "नई वसà¥à¤¤à¥ खोज परिणाम" +New Items = "नई वसà¥à¤¤à¥à¤à¤‚" +New results found for search = "खोज के लिठनठपरिणाम मिले" +New Title = "नया शीरà¥à¤·à¤•" +new_email_success = "आपका ईमेल पता सफलतापूरà¥à¤µà¤• बदल दिया गया है" +new_password = "नया पासवरà¥à¤¡" +new_password_success = "आपका पासवरà¥à¤¡ सफलतापूरà¥à¤µà¤• बदल दिया गया है" +new_results_heading = "%%count%% नवीनतम परिणाम" +new_user_welcome_subject = "आपका नया खाता %%library%%" +new_user_welcome_text = "में सà¥à¤µà¤¾à¤—त %%library%%. के लिठà¤à¤• नया खाता खोला गया है %%firstname%% %%lastname%%. आपका उपयोगकरà¥à¤¤à¤¾ नाम है %%username%%. कृपया इस पृषà¥à¤ पर à¤à¤• पासवरà¥à¤¡ सेट करें: %%url%%"" +Newspaper = "समाचार पतà¥à¤°" +Next = "आगे" +Next Search Result = "अगला खोज परिणाम" +No citations are available for this record = "इस रिकॉरà¥à¤¡ के लिठकोई उदà¥à¤§à¤°à¤£ उपलबà¥à¤§ नहीं हैं" +No Cover Image = "कोई कवर छवि नहीं" +No dependency problems found = "कोई निरà¥à¤à¤°à¤¤à¤¾ समसà¥à¤¯à¤¾ नहीं मिली " +No excerpts were found for this record. = "इस रिकॉरà¥à¤¡ के लिठकोई अंश नहीं मिले" +No library account = "कोई पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ खाता नहीं" +No new item information is currently available. = "वरà¥à¤¤à¤®à¤¾à¤¨ में कोई नई आइटम / वसà¥à¤¤à¥ की जानकारी उपलबà¥à¤§ नहीं है " +No Preference = "कोई वरीयता नहीं" +No reviews were found for this record = "इस रिकॉरà¥à¤¡ के लिठकोई समीकà¥à¤·à¤¾ नहीं मिली" +No Tags = "कोई टैग नहीं" +no_description = "विवरण उपलबà¥à¤§ नहीं है " +no_email_address = "ईमेल पता लà¥à¤ªà¥à¤¤ है।" +no_items_selected = "कोई आइटम नहीं चà¥à¤¨à¤¾ गया था" +nohit_active_filters = "इस खोज में à¤à¤• या अधिक पहलू फ़िलà¥à¤Ÿà¤° लागू किठगठहैं। यदि आप फ़िलà¥à¤Ÿà¤° हटाते हैं, तो आप अधिक परिणाम पà¥à¤°à¤¾à¤ªà¥à¤¤ कर सकते हैं " +nohit_change_tab = "आप में खोज रहे हैं %%activeTab%% टैब। आपको अनà¥à¤¯ टैब में से कà¥à¤› मिल सकता है:" +nohit_filters = "फ़िलहाल इस खोज पर फ़िलà¥â€à¤Ÿà¤° किठगठहैं:" +nohit_heading = "कोई परिणाम नहीं !" +nohit_lookfor_html = "आपकी खोज - <stron>%%lookfor%%</strong> - किसी à¤à¥€ संसाधन से मेल नहीं खाता।" +nohit_no_filters = "इस खोज पर कोई फ़िलà¥à¤Ÿà¤° लागू नहीं किया गया था।" +nohit_parse_error = "आपकी खोज सवाल में कोई समसà¥à¤¯à¤¾ है। कृपया सिंटैकà¥à¤¸ की जाà¤à¤š करें। यदि आप उनà¥à¤¨à¤¤ सà¥à¤µà¤¿à¤§à¤¾à¤“ं का उपयोग करने का पà¥à¤°à¤¯à¤¾à¤¸ नहीं कर रहे हैं, तो सवाल को दोहरे उदà¥à¤§à¤°à¤£ चिहà¥à¤¨à¥‹à¤‚ के अंदर रखने से मदद मिल सकती है।" +nohit_query_without_filters = "इस खोज से सà¤à¥€ फ़िलà¥à¤Ÿà¤° निकालें" +nohit_spelling = "शायद आपको कà¥à¤› वरà¥à¤¤à¤¨à¥€ à¤à¤¿à¤¨à¥à¤¨à¤¤à¤¾à¤“ं को आज़माना चाहिà¤" +nohit_suggest = "आप कà¥à¤› शबà¥à¤¦à¥‹à¤‚ को हटाकर या अपनी वरà¥à¤¤à¤¨à¥€ की जांच करके अपने खोज वाकà¥à¤¯à¤¾à¤‚श को संशोधित करने का पà¥à¤°à¤¯à¤¾à¤¸ कर सकते हैं " +NOT = "नहीं" +Not Illustrated = "सचितà¥à¤° नहीं" +Not On Reserve = "रिजरà¥à¤µ पर नहीं" +not_applicable = "अनà¥à¤ªà¤²à¤¬à¥à¤§" +Note = "टिपà¥à¤ªà¤£à¥€" +note_760 = "मà¥à¤–à¥à¤¯ शà¥à¤°à¥ƒà¤‚खला" +note_762 = "उपशà¥à¤°à¥ƒà¤‚खला" +note_765 = "का अनà¥à¤µà¤¾à¤¦" +note_767 = "अनà¥à¤µà¤¾à¤¦" +note_770 = "का पूरक है" +note_772 = "के लिठपूरक" +note_773 = "इसमें रखा" +note_774 = "संविधान इकाई" +note_775 = "अनà¥à¤¯ संसà¥à¤•à¤°à¤£ उपलबà¥à¤§ हैं" +note_776 = "अतिरिकà¥à¤¤ रूप" +note_777 = "के साथ जारी किया" +note_780_0 = "बनाठरखना" +note_780_1 = "à¤à¤¾à¤— में जारी है " +note_780_2 = "अधिलंघित" +note_780_3 = "à¤à¤¾à¤— में लगा हà¥à¤†" +note_780_4 = "दà¥à¤µà¤¾à¤°à¤¾ गठित" +note_780_5 = "को अवशोषित" +note_780_6 = "अवशोषित à¤à¤¾à¤— में " +note_780_7 = "से अलग" +note_785_0 = "दà¥à¤µà¤¾à¤°à¤¾ जारी है" +note_785_1 = "दà¥à¤µà¤¾à¤°à¤¾ जारी रखा गया" +note_785_2 = "दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¿à¤¤ " +note_785_3 = "दà¥à¤µà¤¾à¤°à¤¾ à¤à¤¾à¤— में लिया गया" +note_785_4 = "दà¥à¤µà¤¾à¤°à¤¾ अवशोषित किया गया" +note_785_5 = "दà¥à¤µà¤¾à¤°à¤¾ à¤à¤¾à¤— में अवशोषित" +note_785_6 = "में विà¤à¤¾à¤œà¤¿à¤¤" +note_785_7 = "विलय किया गया / पà¥à¤°à¤ªà¤¤à¥à¤°à¥‹à¤‚ के साथ" +note_785_8 = "में बदल गया" +note_787 = "अनà¥à¤¯ संबंध" +Notes = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +Number = "संखà¥à¤¯à¤¾" +number_decimal_point = "." +number_thousands_separator = "," +OAI Server = "ओà¤à¤†à¤ˆ सरà¥à¤µà¤°" +Occupation = "वà¥à¤¯à¤µà¤¸à¤¾à¤¯" +od_account_noaccess = "इस लाइबà¥à¤°à¥‡à¤°à¥€ कारà¥à¤¡ में ओवरडà¥à¤°à¤¾à¤‡à¤µ की सामगà¥à¤°à¥€ तक पहà¥à¤‚च नहीं है" +od_account_problem = "आपके खाते में कोई समसà¥à¤¯à¤¾ है। %%message%%" +od_audiobook-mp3 = "à¤à¤®à¤ªà¥€ 3 ऑडियोबà¥à¤• " +od_audiobook-overdrive = "ओवरडà¥à¤°à¤¾à¤‡à¤µ सà¥à¤¨à¥‹ ऑडियोबà¥à¤•" +od_avail_avail = "उपलबà¥à¤§ :" +od_avail_holds = "रखती है:" +od_avail_total = "कà¥à¤² पà¥à¤°à¤¤à¤¿à¤¯à¤¾à¤:" +od_but_cancel_hold = "इस होलà¥à¤¡ को रदà¥à¤¦ करें" +od_but_checkout = "ओवरडà¥à¤°à¤¾à¤‡à¤µ के माधà¥à¤¯à¤® से चेकआउट करें" +od_but_checkout_s = "निरà¥à¤—म" +od_but_gettitle = "इस सामगà¥à¤°à¥€ को डाउनलोड करें" +od_but_gettitle_s = "डाउनलोड" +od_but_hold = "ओवरडà¥à¤°à¤¾à¤‡à¤µ के माधà¥à¤¯à¤® से à¤à¤• जगह रखें" +od_but_hold_s = "à¤à¤• जगह रखें " +od_but_return = "यह शीरà¥à¤·à¤• लौटाओ" +od_cancel_hold = "ओवरडà¥à¤°à¤¾à¤‡à¤µ होलà¥à¤¡ रदà¥à¤¦ करें" +od_checkout = "चेकआउट को ओवरडà¥à¤°à¤¾à¤‡à¤µ करें" +od_code_connection_failed = "ओवरडà¥à¤°à¤¾à¤‡à¤µ से कनेकà¥à¤¶à¤¨ विफल रहा। यदि समसà¥à¤¯à¤¾ बनी रहती है, तो कृपया अपने पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ से संपरà¥à¤• करें" +od_code_contentnotavail = "यह सामगà¥à¤°à¥€ आपके कà¥à¤·à¥‡à¤¤à¥à¤° में उपलबà¥à¤§ नहीं है " +od_code_login_for_avail = "उपलबà¥à¤§à¤¤à¤¾ के लिठलॉगिन करें " +od_code_resource_not_found = "शीरà¥à¤·à¤• नहीं मिला" +od_content = "ओवरडà¥à¤°à¤¾à¤‡à¤µ सामगà¥à¤°à¥€" +od_dl_formats = "पà¥à¤°à¤¾à¤°à¥‚प पà¥à¤°à¤¾à¤°à¥‚प डाउनलोड करें" +od_docheckout_failure = "इस शीरà¥à¤·à¤• की जाà¤à¤š नहीं की जा सकी" +od_docheckout_success = "यह शीरà¥à¤·à¤• आपके लिठचेक किया गया है। यह समापà¥à¤¤ हो रहा है %%expireDate%%" +od_early_return = "शà¥à¤°à¥à¤†à¤¤à¥€ रिटरà¥à¤¨ को ओवरडà¥à¤°à¤¾à¤‡à¤µ करें" +od_ebook-epub-adobe = "à¤à¤¡à¥‹à¤¬ ईपीयूबी ई-पà¥à¤¸à¥à¤¤à¤•" +od_ebook-epub-open = "खà¥à¤²à¤¾ ईपीयूबी ई-पà¥à¤¸à¥à¤¤à¤•" +od_ebook-kindle = "किंडल किताबें" +od_ebook-mediado = "मीडियाडॠपाठक इ-पà¥à¤¸à¥à¤¤à¤•" +od_ebook-overdrive = "ओवरडà¥à¤°à¤¾à¤‡à¤µ ईबà¥à¤• पढ़ें " +od_ebook-pdf-adobe = "à¤à¤¡à¥‹à¤¬ पीडीà¤à¤« ई-पà¥à¤¸à¥à¤¤à¤•" +od_ebook-pdf-open = "खà¥à¤²à¤¾ पीडीà¤à¤« ई-पà¥à¤¸à¥à¤¤à¤•" +od_expires_on = "यह शीरà¥à¤·à¤• समापà¥à¤¤ हो रहा है %%due_date%%." +od_get_title = "ओवरडà¥à¤°à¤¾à¤‡à¤µ डाउनलोड" +od_gettitle_failure = "यह शीरà¥à¤·à¤• डाउनलोड नहीं किया जा सका " +od_help_linktext = "ओवरडà¥à¤°à¤¾à¤‡à¤µ मदद" +od_history = "ओवरडà¥à¤°à¤¾à¤‡à¤µ इतिहास" +od_hold = "ओवरडà¥à¤°à¤¾à¤‡à¤µ होलà¥à¤¡" +od_hold_cancel_failure = "होलà¥à¤¡ रदà¥à¤¦ करने का अनà¥à¤°à¥‹à¤§ विफल रहा " +od_hold_cancel_success = "होलà¥à¤¡ को सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया था" +od_hold_email = "होलà¥à¤¡ अधिसूचना के लिठईमेल पता: %%holdEmailAddress%%." +od_hold_now_avail = "यह होलà¥à¤¡ चेकआउट के लिठउपलबà¥à¤§ है। चेकआउट समापà¥à¤¤ हो रहा है %%expireDate%%." +od_hold_place_failure = "होलà¥à¤¡ अनà¥à¤°à¥‹à¤§ विफल रहा" +od_hold_place_success = "इस उपाधि को होलà¥à¤¡ पर रखा गया है। आपकी होलà¥à¤¡ सà¥à¤¥à¤¿à¤¤à¤¿ है %%holdListPosition%%." +od_hold_placed_on = "होलà¥à¤¡ रखा है %%holdPlacedDate%%." +od_hold_queue = "सà¥à¤¥à¤¿à¤¤à¤¿ %%holdPosition%% का %%numberOfHolds%% कतार में है।" +od_holds = "ओवरडà¥à¤°à¤¾à¤‡à¤µ होलà¥à¤¡" +od_info_unavail = "यह जानकारी वरà¥à¤¤à¤®à¤¾à¤¨ में अनà¥à¤ªà¤²à¤¬à¥à¤§ है " +od_is_checkedout = "आपके पास यह शीरà¥à¤·à¤• है। इसकी वजह है %%due_date%%." +od_is_on_hold = "आपके पास यह शीरà¥à¤·à¤• है" +od_loans = "ओवरडà¥à¤°à¤¾à¤‡à¤µ ऋण" +od_mycontent_help = "इन शीरà¥à¤·à¤•à¥‹à¤‚ को डाउनलोड करने के बारे में जानकारी और मदद के लिà¤, देखें <a href="%%url%%">ओवरडà¥à¤°à¤¾à¤‡à¤µ मदद</a>." +od_none_found = "कोई शीरà¥à¤·à¤• नहीं मिला" +od_return_failure = "उनका शीरà¥à¤·à¤• वापस नहीं किया जा सका" +od_return_success = "यह शीरà¥à¤·à¤• वापस कर दिया गया है" +od_video-streaming = "सà¥à¤Ÿà¥à¤°à¥€à¤®à¤¿à¤‚ग वीडियो फ़ाइल" +of_num_results = "#%%position%% का %%total%% परिणाम" +old_password = "पà¥à¤°à¤¾à¤¨à¤¾ पासवरà¥à¤¡" +On Reserve = "आरकà¥à¤·à¤£ पर" +On Reserve - Ask at Circulation Desk = "आरकà¥à¤·à¤£ पर - संचलन डेसà¥à¤• पर पूछें" +on_reserve = "आरकà¥à¤·à¤£ - संचलन में पूछें" +on_topic = "%%count%% इस विषय पर आइटम" +Online Access = "ऑनलाइन पहà¥à¤‚च" +online_resources = "पूरà¥à¤£ पाठ" +open_access_limit = "पहà¥à¤‚च सामगà¥à¤°à¥€ को खोलने की सीमा" +operator_contains = "शामिल" +operator_exact = "है (सटीक) " +OR = "अथवा" +or create a new list = "या à¤à¤• नई सूची बनाà¤à¤‚" +original = "वासà¥à¤¤à¤µà¤¿à¤•" +Other associated place = "अनà¥à¤¯ संबदà¥à¤§ सà¥à¤¥à¤¾à¤¨" +Other Authors = "अनà¥à¤¯ लेखक" +Other Editions = "अनà¥à¤¯ संसà¥à¤•à¤°à¤£" +Other Libraries = "अनà¥à¤¯ पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯" +Other Sources = "अनà¥à¤¯ सà¥à¤°à¥‹à¤¤" +Page not found. = "पृषà¥à¤ नहीं मिला" +page_first = "पà¥à¤°à¤¥à¤® पृषà¥à¤ पर जाà¤à¤‚" +page_last = "अंतिम पृषà¥à¤ पर जाà¤à¤" +page_next = "अगले पृषà¥à¤ पर जाà¤à¤" +page_num = "पृषà¥à¤ %%page%%" +page_prev = "पिछले पृषà¥à¤ पर जाà¤à¤" +pagination_label = "पृषà¥à¤ ांकन" +Password = "पासवरà¥à¤¡" +Password Again = "पासवरà¥à¤¡ / कूटशबà¥à¤¦ दà¥à¤¬à¤¾à¤°à¤¾ à¤à¤°à¥‡à¤‚" +Password cannot be blank = "पासवरà¥à¤¡ रिकà¥à¤¤ नहीं हो सकता" +password_error_auth_old = "पहले उपयोग किया गया पासवरà¥à¤¡ अमानà¥à¤¯ है" +password_error_invalid = "नया पासवरà¥à¤¡ अमानà¥à¤¯ है (उदाहरण में अमानà¥à¤¯ वरà¥à¤£ हैं)" +password_error_not_unique = "पासवरà¥à¤¡ नहीं बदला गया था " +password_maximum_length = "अधिकतम पासवरà¥à¤¡ लंबाई है %%maxlength%% अकà¥à¤·à¤°" +password_minimum_length = "नà¥à¤¯à¥‚नतम पासवरà¥à¤¡ लंबाई है %%minlength%% अकà¥à¤·à¤°" +password_only_alphanumeric = "संखà¥à¤¯à¤¾ और अकà¥à¤·à¤° केवल à¤-जेड" +password_only_numeric = "केवल संखà¥à¤¯à¤¾" +Passwords do not match = "पासवरà¥à¤¡ मेल नहीं खाते" +past_days = "पिछला %%range%% दिन" +PDF Full Text = "पीडीà¤à¤« पूरà¥à¤£ पाठ" +peer_reviewed = "सहकरà¥à¤®à¥€ समीकà¥à¤·à¤¾ की" +peer_reviewed_limit = "सहकरà¥à¤®à¥€-समीकà¥à¤·à¤¿à¤¤ पतà¥à¤°à¤¿à¤•à¤¾à¤“ं से लेखों की सीमा" +permission_denied = "आपने à¤à¤• पृषà¥à¤ या कारà¥à¤°à¤µà¤¾à¤ˆ का अनà¥à¤°à¥‹à¤§ किया है, लेकिन आपके पास आवशà¥à¤¯à¤• अनà¥à¤®à¤¤à¤¿ नहीं है" +permission_denied_title = "अनà¥à¤®à¤¤à¤¿ नहीं मिली" +Phone Number = "फ़ोन नंबर" +Photo = "तसà¥à¤µà¥€à¤°" +Physical Description = "à¤à¥Œà¤¤à¤¿à¤• वरà¥à¤£à¤¨" +Physical Object = "à¤à¥Œà¤¤à¤¿à¤• वसà¥à¤¤à¥ " +pick_up_location = "उठाने की जगह" +Place a Hold = "होलà¥à¤¡ करें" +Place of birth = "जनà¥à¤® सà¥à¤¥à¤¾à¤¨" +Place of death = "मृतà¥à¤¯à¥ की जगह" +Playing Time = "खेलने का समय" +Please check back soon = "कृपया कà¥à¤› समय बाद दोबारा देखें " +Please contact the Library Reference Department for assistance = "सहायता के लिठकृपया पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ संदरà¥à¤ विà¤à¤¾à¤— से संपरà¥à¤• करें" +Please enable JavaScript. = "जावासà¥à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ कृपया सकà¥à¤·à¤® करें " +Please upgrade your browser. = "कृपया अपना बà¥à¤°à¤¾à¤‰à¤œà¤¼à¤° अपगà¥à¤°à¥‡à¤¡ करें" +Preferences = "पसंद" +Preferred Library = "पसंदीदा पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯" +Prev = "पिछला" +Preview = "पूरà¥à¤µà¤¾à¤µà¤²à¥‹à¤•à¤¨" +Preview from = "से पूरà¥à¤µà¤¾à¤µà¤²à¥‹à¤•à¤¨ करें" +Previous Search Result = "पिछला खोज परिणाम" +Previous Title = "पिछला शीरà¥à¤·à¤•" +Print = "पà¥à¤°à¤¿à¤‚ट" +print_selected = "पà¥à¤°à¤¿à¤‚ट चà¥à¤¨à¤¾ गया" +Private = "निजी" +Production Credits = "उतà¥à¤ªà¤¾à¤¦à¤¨ साख" +Profile = "रूपरेखा" +profile_update = "आपका पà¥à¤°à¥‹à¤«à¤¼à¤¾à¤‡à¤² अनà¥à¤°à¥‹à¤§ के अनà¥à¤¸à¤¾à¤° अपडेट किया गया था" +pronounced = "उचà¥à¤šà¤¾à¤°à¤£" +Provider = "पà¥à¤°à¤¦à¤¾à¤¨ करनेवाला" +Public = "जनता" +Publication = "पà¥à¤°à¤•à¤¾à¤¶à¤¨" +Publication Date = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ तिथि" +Publication Frequency = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ की आवृतà¥à¤¤à¤¿ " +Publication Information = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ सूचना" +Publication Type = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ पà¥à¤°à¤•à¤¾à¤°" +Publication Year = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ वरà¥à¤·" +Publication_Place = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ का सà¥à¤¥à¤¾à¤¨" +Published = "पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤" +Published in = "में पà¥à¤°à¤•à¤¾à¤¶à¤¿à¤¤" +Publisher = "पà¥à¤°à¤•à¤¾à¤¶à¤•" +Publisher Information = "पà¥à¤°à¤•à¤¾à¤¶à¤• की जानकारी" +Publisher Permissions = "पà¥à¤°à¤•à¤¾à¤¶à¤• अनà¥à¤®à¤¤à¤¿à¤¯à¤¾à¤" +QR Code = "कà¥à¤¯à¥‚आर कोड" +qrcode_hide = "कà¥à¤¯à¥‚आर कोड छिपाà¤à¤‚" +qrcode_show = "कà¥à¤¯à¥‚आर कोड दिखाà¤à¤‚" +query time = "सवाल का समय" +random_recommendation_title = "अपने परिणामों से यादृचà¥à¤›à¤¿à¤• आइटम " +Range = "सीमा" +Range slider = " रेंज सà¥à¤²à¤¾à¤‡à¤¡à¤°" +Read the full review online... = "पूरी समीकà¥à¤·à¤¾ ऑनलाइन पढ़ें ..." +Recall This = "इसे याद करें " +recaptcha_not_passed = "कॅपà¥à¤šà¤¾ पास नहीं हà¥à¤†" +recently_returned_channel_title = "हाल ही में लौटा" +recommend_links_text = "आप à¤à¥€ पà¥à¤°à¤¯à¤¤à¥à¤¨ कर सकते हैं:" +Record Citations = "रिकॉरà¥à¤¡ उदà¥à¤§à¤°à¤£" +Record Count = "गणना रिकॉरà¥à¤¡ करें" +Record Type = "रिकॉरà¥à¤¡ का पà¥à¤°à¤•à¤¾à¤°" +Recover Account = "खाते की वसूली" +recovery_by_email = "ईमेल दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤ करें" +recovery_by_username = "उपयोगकरà¥à¤¤à¤¾ नाम से पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤ करें" +recovery_disabled = "पासवरà¥à¤¡ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ में सकà¥à¤·à¤® नहीं है" +recovery_email_notification = "आपके खाते के लिठपासवरà¥à¤¡ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤ करने के लिठअनà¥à¤°à¥‹à¤§ किया गया था %%library%%." +recovery_email_sent = "इस खाते से पंजीकृत ईमेल पते पर पासवरà¥à¤¡ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ निरà¥à¤¦à¥‡à¤¶ à¤à¥‡à¤œà¥‡ गठहैं।" +recovery_email_subject = "खाता पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ देखें " +recovery_email_url_pretext = "आप इस यूआरà¤à¤² पर अपना पासवरà¥à¤¡ रीसेट कर सकते हैं: %%url%%" +recovery_expired_hash = "इस पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ लिंक की समय सीमा समापà¥à¤¤ हो गई है" +recovery_invalid_hash = "पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ लिंक नहीं पहचाना गया " +recovery_new_disabled = "आपको इस समय अपना पासवरà¥à¤¡ बदलने की अनà¥à¤®à¤¤à¤¿ नहीं है " +recovery_title = "पासवरà¥à¤¡ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿" +recovery_too_soon = "बहà¥à¤¤ अधिक पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ किठगठहैं, कृपया बाद में पà¥à¤¨à¤ƒ पà¥à¤°à¤¯à¤¾à¤¸ करें" +recovery_user_not_found = "हमें आपका खाता नहीं मिला" +rectangle_center_message = "यह हाइलाइटेड आयत का केंदà¥à¤° बिंदॠहै" +Reference Material = "संदरà¥à¤ सामगà¥à¤°à¥€" +Refine Results = "परिणाम को परिषà¥à¤•à¥ƒà¤¤ करें" +Region = "कà¥à¤·à¥‡à¤¤à¥à¤° " +relais_available = "यह आइटम / वसà¥à¤¤à¥ अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण के माधà¥à¤¯à¤® से उपलबà¥à¤§ है। कà¥à¤¯à¤¾ आप इसे निवेदन करना चाहेंगे ? " +relais_checking = "उपलबà¥à¤§à¤¤à¤¾ की जाà¤à¤š..." +relais_error_html = "इस अनà¥à¤°à¥‹à¤§ के साथ à¤à¤• समसà¥à¤¯à¤¾ थी। कृपया इस मद का उपयोग करने का अनà¥à¤°à¥‹à¤§ करें <a href="%%url%%" target='new'>इंटर लाइबà¥à¤°à¥‡à¤°à¥€ लोन की वेबसाइट</a>." +relais_request = "अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण का अनà¥à¤°à¥‹à¤§" +relais_requesting = "बिनती करना... " +relais_search = "खोज अंतरपà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯à¥€ ऋण" +relais_success_label = "पà¥à¤·à¥à¤Ÿà¥€à¤•à¤°à¤£" +relais_success_message = "अनà¥à¤°à¥‹à¤§ आईडी #%%id%% बनाया गया था। आपको à¤à¤• पà¥à¤·à¥à¤Ÿà¤¿à¤•à¤°à¤£ ई - मेल पà¥à¤°à¤¾à¤ªà¥à¤¤ होगा।" +Related Author = "संबंधित लेखक" +Related Items = "संबंधित चीजें " +Related Subjects = "संबंधित विषय" +Remove Filters = "फ़िलà¥à¤Ÿà¤° निकालें" +Remove from Book Bag = "बà¥à¤• बैग से निकालें" +renew_all = "सà¤à¥€ आइटम नवीनीकृत करें " +renew_determine_fail = "यदि आपका आइटम नवीनीकृत किया जा सकता है तो हम यह निरà¥à¤§à¤¾à¤°à¤¿à¤¤ करने में असमरà¥à¤¥ थे। कृपया करà¥à¤®à¤šà¤¾à¤°à¤¿à¤¯à¥‹à¤‚ के à¤à¤• सदसà¥à¤¯ से संपरà¥à¤• करें" +renew_empty_selection = "कोई आइटम नहीं चà¥à¤¨à¤¾ गया था" +renew_error = "हम आपके आइटम / वसà¥à¤¤à¥ को नवीनीकृत करने में असमरà¥à¤¥ थे - कृपया करà¥à¤®à¤šà¤¾à¤°à¤¿à¤¯à¥‹à¤‚ के à¤à¤• सदसà¥à¤¯ से संपरà¥à¤• करें" +renew_fail = "इस आइटम को नवीनीकृत नहीं किया जा सका " +renew_item = "नवीनीकृत वसà¥à¤¤à¥" +renew_item_due = "अगले 24 घंटों के à¤à¥€à¤¤à¤° आइटम देय" +renew_item_due_tooltip = "जलà¥à¤¦ ही आइटम देय" +renew_item_limit = "यह आइटम देय अपनी नवीनीकरण सीमा तक पहà¥à¤à¤š गया है" +renew_item_no = "इस आइटम को नवीनीकृत नहीं किया जा सकता है " +renew_item_overdue = "मद अतिदेय" +renew_item_overdue_tooltip = "मद अतिदेय" +renew_item_requested = "यह मद किसी अनà¥à¤¯ उपयोगकरà¥à¤¤à¤¾ दà¥à¤µà¤¾à¤°à¤¾ अनà¥à¤°à¥‹à¤§ किया गया है " +renew_select_box = "नवीनीकृत मद" +renew_selected = "चयनित मद नवीनीकृत करें" +renew_success = "नवीनीकरण सफल" +Renewed = "नवीकृत" +Request full text = "पूरà¥à¤£ पाठका अनà¥à¤°à¥‹à¤§ करें" +request_in_transit = "सà¥à¤¥à¤¾à¤¨à¤¾à¤¨à¥à¤¤à¤°à¤£ में संगà¥à¤°à¤¹ सà¥à¤¥à¤¾à¤¨ पर" +request_place_text = "à¤à¤• अनà¥à¤°à¥‹à¤§ रखें " +request_submit_text = "अनà¥à¤°à¥‹à¤§ पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ करें " +Requests = "अनà¥à¤°à¥‹à¤§" +Reserves = "à¤à¤‚डार" +Reserves Search = "खोज का आरकà¥à¤·à¤£ करता है" +Reserves Search Results = "खोज परिणामों को सà¥à¤°à¤•à¥à¤·à¤¿à¤¤ रखता है" +reset_filters_button = "फ़िलà¥à¤Ÿà¤° रीसेट करें" +result_checkbox_label = "परिणाम संखà¥à¤¯à¤¾ का चयन करें %%number%%." +result_count = "%%count%% परिणाम" +Results = "परिणाम" +results = "परिणाम" +Results for = "का परिणाम" +Results per page = "परिणाम पà¥à¤°à¤¤à¤¿ पृषà¥à¤ " +Resumption Token = "फिर से शà¥à¤°à¥‚ टोकन" +Return Date = "वापसी की तिथि" +Review by = "दà¥à¤µà¤¾à¤°à¤¾ समीकà¥à¤·à¤¾" +Reviews = "समीकà¥à¤·à¤¾" +Save = "सहेजें" +Save Comment = "टिपà¥à¤ªà¤£à¥€ सहेजें" +save_search = "खोज संगà¥à¤°à¤¹à¤¿à¤¤ करें" +save_search_remove = "सहेजे गठखोज निकालें" +Saved in = "में बचाया" +schedule_daily = "दैनिक" +schedule_explanation = "खोज के नठपरिणामों के बारे में अलरà¥à¤Ÿ ईमेल से पà¥à¤°à¤¾à¤ªà¥à¤¤ करें।" +schedule_none = "कोई नहीं" +schedule_weekly = "सापà¥à¤¤à¤¾à¤¹à¤¿à¤•" +Scheduled Alert Results = "अनà¥à¤¸à¥‚चित अलरà¥à¤Ÿ परिणाम" +scholarly_limit = "विदà¥à¤µà¤¾à¤¨à¥‹à¤‚ की पतà¥à¤°à¤¿à¤•à¤¾à¤“ं से लेखों की सीमा" +Scroll to Load More = "अधिक लोड करने के लिठसà¥à¤•à¥à¤°à¥‰à¤² करें " +Search = "खोज" +Search For = "निमà¥à¤¨ को खोजें" +Search For Items on Reserve = "आरकà¥à¤·à¤£ पर आइटम के लिठखोजें " +Search History = "इतिहास में खोज करें" +Search Home = "खोज घर" +Search Mode = "खोज पà¥à¤°à¤•à¤¾à¤°" +Search Options = "खोज विकलà¥à¤ª" +Search Results = "खोज परिणाम" +search results of = "के खोज परिणाम" +Search Tips = "खोज यà¥à¤•à¥à¤¤à¤¿à¤¯à¤¾à¤ " +Search Tools = "खोज साधन " +Search Type = "खोज की विधि" +Search within collection = "संगà¥à¤°à¤¹ के à¤à¥€à¤¤à¤° खोजें" +search_AND = "सà¤à¥€ शरà¥à¤¤à¥‡à¤‚" +search_groups = "खोज समूह" +search_match = "मेल खाना" +search_NOT = "कोई शरà¥à¤¤à¥‡à¤‚ नहीं " +search_OR = "कोई à¤à¥€ शरà¥à¤¤à¥‡à¤‚" +search_save_success = "खोज सफलतापूरà¥à¤µà¤• सहेजी गई" +search_terms = "खोज शबà¥à¤¦ " +search_unsave_success = "सहेजे गठखोज को सफलतापूरà¥à¤µà¤• निकाल दिया गया" +seconds_abbrev = "सेकंड" +see all = "सà¤à¥€ देखें" +See also = "यह सà¤à¥€ देखें" +Select this record = "इस रिकॉरà¥à¤¡ का चयन करें" +Select your carrier = "अपने कैरियर का चयन करें" +select_page = "पेज का चयन करें" +select_pickup_location = "पिकअप सà¥à¤¥à¤¾à¤¨ चà¥à¤¨à¥‡à¤‚" +select_request_group = "अनà¥à¤°à¥‹à¤§ समूह का चयन करें" +Selected = "चà¥à¤¨ लिया" +Send = "à¤à¥‡à¤œà¤¨à¤¾" +Send us your feedback! = "हमें अपनी पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ à¤à¥‡à¤œà¥‡à¤‚ !" +send_an_email_copy = "इस पते पर à¤à¤• पà¥à¤°à¤¤à¤¿ à¤à¥‡à¤œà¥‡à¤‚" +send_email_copy_to_me = "मà¥à¤à¥‡ à¤à¤• पà¥à¤°à¤¤à¤¿ à¤à¥‡à¤œà¥‡à¤‚" +Sensor Image = "जà¥à¤žà¤¾à¤¨à¥‡à¤‚दà¥à¤°à¥€ छवि" +Serial = " सामयिक पतà¥à¤°à¤¿à¤•à¤¾" +Series = "शà¥à¤°à¥ƒà¤‚खला" +Set = "समूह" +show_filters_html = "फिलà¥à¤Ÿà¤° दिखाà¤à¤‚ (%%count%%)" +showing_items_html = "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ <strong>%%start%% - %%end%%</strong> विषय " +showing_items_of_html = "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ <strong>%%start%% - %%end%%</strong> <strong>%%total%%</strong> विषय" +showing_results_for_html = "खोज के <strong>%%start%% - %%end%%</strong> परिणाम '<strong>%%lookfor%%</strong>' पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ " +showing_results_html = "खोज के <strong>%%start%% - %%end%%</strong> पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ परिणाम" +showing_results_of_for_html = "Showing <strong>%%start%% - %%end%%</strong> results of <strong>%%total%%</strong> for search '<strong>%%lookfor%%</strong>'" +showing_results_of_html = "पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ <strong>%%start%% - %%end%%</strong> परिणाम <strong>%%total%%</strong>" +sidebar_close = "साइडबार को संकà¥à¤·à¤¿à¤ªà¥à¤¤ करें" +sidebar_expand = "साइडबार का विसà¥à¤¤à¤¾à¤° करें" +Similar Items = "समान संसाधन " +Skip to content = "इसे छोड़कर सामगà¥à¤°à¥€ पर बढ़ने के लिà¤" +skip_confirm = "कà¥à¤¯à¤¾ आप वाकई इस चरण को छोड़ना चाहते हैं??" +skip_fix_metadata = "इस समय मेटाडेटा ठीक न करें।" +skip_step = "इस चरण को छोड़ दें " +Slide = "सà¥à¤²à¤¾à¤‡à¤¡" +sms_failure = "तà¥à¤°à¥à¤Ÿà¤¿! संदेश नहीं à¤à¥‡à¤œ सका" +sms_phone_number = "10-अंकों वाला फोन नंबर" +sms_sending = "संदेश à¤à¥‡à¤œà¤¨à¤¾..." +sms_success = "मैसेज à¤à¥‡à¤œà¤¾ गया" +Software = "सॉफà¥à¤Ÿà¤µà¥‡à¤¯à¤° " +Sorry, but the help you requested is unavailable in your language. = "कà¥à¤·à¤®à¤¾ करें, लेकिन आपके दà¥à¤µà¤¾à¤°à¤¾ अनà¥à¤°à¥‹à¤§à¤¿à¤¤ सहायता आपकी à¤à¤¾à¤·à¤¾ में अनà¥à¤ªà¤²à¤¬à¥à¤§ है" +Sort = "शà¥à¤°à¥‡à¤£à¥€à¤¬à¤¦à¥à¤§ करें" +sort_alphabetic = "वरà¥à¤£à¤®à¤¾à¤²à¤¾ " +sort_author = "लेखक" +sort_author_author = "वरà¥à¤£à¤®à¤¾à¤²à¤¾" +sort_author_relevance = "लोकपà¥à¤°à¤¿à¤¯à¤¤à¤¾ " +sort_callnumber = "बोधानक" +sort_checkout_date_asc = "निरà¥à¤—म तिथि (पहले सबसे पà¥à¤°à¤¾à¤¨à¥€)" +sort_checkout_date_desc = "निरà¥à¤—म तिथि (सबसे पहले नवीनतम)" +sort_count = "परिणाम की गणना" +sort_due_date_asc = "नियत तिथि (पहले सबसे पà¥à¤°à¤¾à¤¨à¥€)" +sort_due_date_desc = "नियत तिथि (सबसे पहले नवीनतम)" +sort_relevance = "पà¥à¤°à¤¾à¤¸à¤‚गिकता" +sort_return_date_asc = "वापसी की तारीख (सबसे पà¥à¤°à¤¾à¤¨à¥€ पहली)" +sort_return_date_desc = "वापसी की तारीख (पहले नई)" +sort_title = "शीरà¥à¤·à¤•" +sort_year = "तिथि अवरोही में" +sort_year asc = "तिथि आरोही में" +Source = "सà¥à¤°à¥‹à¤¤" +Source Title = "सà¥à¤°à¥‹à¤¤ शीरà¥à¤·à¤•" +spell_expand_alt = "खोज का विसà¥à¤¤à¤¾à¤° करें" +spell_suggest = "विकलà¥à¤ª खोजें " +Staff View = "सà¥à¤Ÿà¤¾à¤« के लिठ" +Start a new Advanced Search = "à¤à¤• नई उनà¥à¤¨à¤¤ खोज शà¥à¤°à¥‚ करें" +Start a new Basic Search = "à¤à¤• नया मूलà¤à¥‚त खोज शà¥à¤°à¥‚ करें" +Start Page = "पृषà¥à¤ पà¥à¤°à¤¾à¤°à¤‚ठकरें" +starting from = "से शà¥à¤°à¥‚" +Status = "सà¥à¤¥à¤¿à¤¤à¤¿" +status_transit = "मारà¥à¤—सà¥à¤¥" +status_unknown_message = "लाइव सà¥à¤¥à¤¿à¤¤à¤¿ उपलबà¥à¤§ नहीं है" +Storage Retrieval Requests = "संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§" +storage_retrieval_request_available = "संगà¥à¤°à¤¹ के लिठउपलबà¥à¤§ " +storage_retrieval_request_cancel = "संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ करें" +storage_retrieval_request_cancel_all = "सà¤à¥€ संगà¥à¤°à¤¹à¤£ नियंतà¥à¤°à¤£ कà¥à¤·à¥‡à¤¤à¥à¤° रदà¥à¤¦ करें को à¤à¥‡à¤œà¥‡à¤‚" +storage_retrieval_request_cancel_fail = "आपका अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ नहीं किया गया था अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +storage_retrieval_request_cancel_selected = "चयनित संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ रदà¥à¤¦ करें " +storage_retrieval_request_cancel_success = "आपका अनà¥à¤°à¥‹à¤§ सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया था" +storage_retrieval_request_cancel_success_items = "%%count%% अनà¥à¤°à¥‹à¤§ (ओं) को सफलतापूरà¥à¤µà¤• रदà¥à¤¦ कर दिया गया" +storage_retrieval_request_canceled = "रदà¥à¤¦" +storage_retrieval_request_check_text = "संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ की जाà¤à¤š करें" +storage_retrieval_request_comments = "टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +storage_retrieval_request_date_invalid = "कृपया à¤à¤• मानà¥à¤¯ तिथि पà¥à¤°à¤µà¤¿à¤·à¥à¤Ÿ करें" +storage_retrieval_request_date_past = "कृपया à¤à¤µà¤¿à¤·à¥à¤¯ में à¤à¤• तिथि दरà¥à¤œ करें" +storage_retrieval_request_empty_selection = "कोई संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ नहीं चà¥à¤¨à¥‡ गठथे " +storage_retrieval_request_error_blocked = "आपके पास इस संसाधन पर संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ रखने के लिठपरà¥à¤¯à¤¾à¤ªà¥à¤¤ विशेषाधिकार नहीं हैं " +storage_retrieval_request_error_fail = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें" +storage_retrieval_request_invalid_pickup = "à¤à¤• अमानà¥à¤¯ पिकअप सà¥à¤¥à¤¾à¤¨ दरà¥à¤œ किया गया था। कृपया पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें" +storage_retrieval_request_issue = "तिथि" +storage_retrieval_request_place_fail_missing = "आपका अनà¥à¤°à¥‹à¤§ विफल रहा। कà¥à¤› डेटा गायब था। अधिक सहायता के लिठकृपया संचलन डेसà¥à¤• से संपरà¥à¤• करें " +storage_retrieval_request_place_success = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा" +storage_retrieval_request_place_success_html = "आपका अनà¥à¤°à¥‹à¤§ सफल रहा। <a href="%%url%%">संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§</a>." +storage_retrieval_request_place_text = "à¤à¤• संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ रखें " +storage_retrieval_request_processed = "कारà¥à¤°à¤µà¤¾à¤ˆ कृत" +storage_retrieval_request_profile_html = "संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ जानकारी के लिà¤, कृपया अपनी सà¥à¤¥à¤¾à¤ªà¤¨à¤¾ करें <a href="%%url%%">लाइबà¥à¤°à¥‡à¤°à¥€ कैटलॉग पà¥à¤°à¥‹à¤«à¤¾à¤‡à¤²</a>." +storage_retrieval_request_reference = "संदरà¥à¤" +storage_retrieval_request_selected_item = "चयनित आइटम" +storage_retrieval_request_submit_text = "सà¥à¤¥à¤¾à¤¨ का अनà¥à¤°à¥‹à¤§" +storage_retrieval_request_volume = "खंड " +storage_retrieval_request_year = "वरà¥à¤·" +Subcollection = "उपसंगà¥à¤°à¤¹" +Subject = "विषय" +Subject Area = "विषय कà¥à¤·à¥‡à¤¤à¥à¤° " +Subject Geographic = "विषय à¤à¥Œà¤—ोलिक" +Subject Recommendations = "विषयक संसà¥à¤¤à¥à¤¤à¤¿à¤¯à¤¾à¤" +Subject Terms = "विषय शरà¥à¤¤à¥‡à¤‚" +Subject(s) = "विषय (यों)" +Subjects = "विषय" +Submit = "पà¥à¤°à¤¸à¥à¤¤à¥à¤¤" +Submitting = "पà¥à¤°à¤¸à¥à¤¤à¥à¤¤ करना " +Suggested Topics = " पà¥à¤°à¤¸à¥à¤¤à¤¾à¤µà¤¿à¤¤ विषय :" +Summary = "सारांश" +Summon Results = "नतीजा" +summon_database_recommendations = "आपको यहां अतिरिकà¥à¤¤ संसाधन मिल सकते हैं:" +Supplements = "की आपूरà¥à¤¤à¤¿ करता है" +Supplied by Amazon = "अमेज़न दà¥à¤µà¤¾à¤°à¤¾ आपूरà¥à¤¤à¤¿ की गई " +switch_view = "देखने के लिठसà¥à¤µà¤¿à¤š करें %%view%%" +switchquery_fuzzy = "फ़रà¥à¤œà¤¼à¥€ खोज करने से समान वरà¥à¤¤à¤¨à¥€ वाले शबà¥à¤¦ पà¥à¤¨à¤ƒ पà¥à¤°à¤¾à¤ªà¥à¤¤ हो सकते हैं " +switchquery_intro = "आप अपनी खोज सवाल को समायोजित करके अधिक परिणाम पà¥à¤°à¤¾à¤ªà¥à¤¤ करने में सकà¥à¤·à¤® हो सकते हैं।" +switchquery_lowercasebools = "यदि आप बूलियन ऑपरेटरों का उपयोग करने की कोशिश कर रहे हैं, तो उनà¥à¤¹à¥‡à¤‚ सà¤à¥€ बड़े अकà¥à¤·à¤° होना चाहिà¤" +switchquery_truncatechar = "अपने परिणामों को वà¥à¤¯à¤¾à¤ªà¤• बनाने के लिठअपनी खोज सवाल को छोटा करें" +switchquery_unwantedbools = "शबà¥à¤¦ और, या नहीं खोज को à¤à¥à¤°à¤®à¤¿à¤¤ कर सकते हैं; उदà¥à¤§à¤°à¤£ जोड़ने का पà¥à¤°à¤¯à¤¾à¤¸ करें" +switchquery_unwantedquotes = "उदà¥à¤§à¤°à¤£ हटाने से वà¥à¤¯à¤¾à¤ªà¤• खोज की अनà¥à¤®à¤¤à¤¿ मिल सकती है " +switchquery_wildcard = "वाइलà¥à¤¡à¤•à¤¾à¤°à¥à¤¡ पà¥à¤°à¤¤à¥€à¤• जोड़ने से शबà¥à¤¦ पà¥à¤¨à¤ƒ पà¥à¤°à¤¾à¤ªà¥à¤¤ हो सकता है" +System Unavailable = "सिसà¥à¤Ÿà¤® अनà¥à¤ªà¤²à¤¬à¥à¤§ है " +Table of Contents = "विषय - सूची" +Table of Contents unavailable = "सामगà¥à¤°à¥€ की तालिका अनà¥à¤ªà¤²à¤¬à¥à¤§ है" +Tag = "टैग" +Tag Management = "टैग पà¥à¤°à¤¬à¤‚धन" +tag_delete_filter = "आप निमà¥à¤¨à¤²à¤¿à¤–ित फ़िलà¥à¤Ÿà¤° का उपयोग कर रहे हैं - उपयोगकरà¥à¤¤à¤¾ नाम: %उपयोगकरà¥à¤¤à¤¾ नाम%, टैग: %टैग%,संसाधन: %संसाधन%" +tag_delete_warning = "चेतावनी! आप डिलीट करने वाले हैं %गणना% संसाधन टैग" +tag_filter_empty = "इस फ़िलà¥à¤Ÿà¤° के लिठकोई टैग उपलबà¥à¤§ नहीं हैं " +Tags = "टैग " +tags_deleted = "%गणना% टैग हटाया गया" +test_fail = "अनà¥à¤¤à¥à¤¤à¥€à¤°à¥à¤£ होना" +test_fix = "जमा देना" +test_ok = "ठीक" +Text this = "इसका टेकà¥à¤¸à¥à¤Ÿ मैसेज à¤à¥‡à¤œà¥‡ " +Thank you for your feedback. = "आपकी पà¥à¤°à¤¤à¤¿à¤•à¥à¤°à¤¿à¤¯à¤¾ के लिठआपका धनà¥à¤¯à¤µà¤¾à¤¦" +That email address is already used = "वह ईमेल पता पहले ही उपयोग किया जा चà¥à¤•à¤¾ है" +That username is already taken = "यह उपयोगकरà¥à¤¤à¤¾ नाम पहले ही लिया जा चà¥à¤•à¤¾ है " +The record you selected is not part of any of your lists. = "आपके दà¥à¤µà¤¾à¤°à¤¾ चयनित रिकॉरà¥à¤¡ आपकी किसी à¤à¥€ सूची का हिसà¥à¤¸à¤¾ नहीं है" +The record you selected is not part of the selected list. = "आपके दà¥à¤µà¤¾à¤°à¤¾ चयनित रिकॉरà¥à¤¡ चयनित सूची का हिसà¥à¤¸à¤¾ नहीं है।" +The system is currently unavailable due to system maintenance = "सिसà¥à¤Ÿà¤® रखरखाव के कारण वरà¥à¤¤à¤®à¤¾à¤¨ में सिसà¥à¤Ÿà¤® अनà¥à¤ªà¤²à¤¬à¥à¤§ है " +Theme = "विषय" +Thesis = "थीसिस" +This email was sent from = "यह ईमेल से à¤à¥‡à¤œà¤¾ गया था" +This field is required = "यह फ़ीलà¥à¤¡ आवशà¥à¤¯à¤• है" +This item is already part of the following list/lists = "यह आइटम पहले से ही निमà¥à¤¨à¤²à¤¿à¤–ित सूची / सूचियों का हिसà¥à¤¸à¤¾ है" +This result not is displayed to guests = "यह परिणाम मेहमानों को पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ नहीं किया जाता है" +Title = "शीरà¥à¤·à¤•" +Title not available = "शीरà¥à¤·à¤• उपलबà¥à¤§ नहीं है" +Title View = "शीरà¥à¤·à¤• दृशà¥à¤¯" +title_hold_place = "à¤à¤• शीरà¥à¤·à¤• सà¥à¤¤à¤° अनà¥à¤°à¥‹à¤§ रखें" +To = "से" +Too Many Email Recipients = "बहà¥à¤¤ सारे ईमेल पà¥à¤°à¤¾à¤ªà¥à¤¤à¤•à¤°à¥à¤¤à¤¾ " +too_many_favorites = "यह सूची à¤à¤• साथ सà¤à¥€ को पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिठबहà¥à¤¤ बड़ी है। अपने पसंदीदा को अधिक सूचियों में पà¥à¤¨: वà¥à¤¯à¤µà¤¸à¥à¤¥à¤¿à¤¤ करने या टैग का उपयोग करने की कोशिश करें" +too_many_new_items = "à¤à¤•à¤² सूची में पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिठबहà¥à¤¤ सी नई वसà¥à¤¤à¥à¤à¤ हैं। अपनी खोज को सीमित करने का पà¥à¤°à¤¯à¤¾à¤¸ करें" +too_many_reserves = "à¤à¤•à¤² सूची में पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिठबहà¥à¤¤ सारे कोरà¥à¤¸ रिजरà¥à¤µ हैं। अपनी खोज को सीमित करने का पà¥à¤°à¤¯à¤¾à¤¸ करें" +top_facet_label = "%%label%% खोज निहित" +Topic = "विषय" +Topics = "विषयों" +Total Balance Due = "कà¥à¤² शेष देय" +total_comments = "कà¥à¤² टिपà¥à¤ªà¤£à¤¿à¤¯à¤¾à¤" +total_lists = "कà¥à¤² सूचियाà¤" +total_resources = "कà¥à¤² संसाधन" +total_saved_items = "कà¥à¤² बचत आइटम" +total_tags = "कà¥à¤² टैग" +total_users = "कà¥à¤² उपयोगकरà¥à¤¤à¤¾" +Transliterated Title = "अनूदित शीरà¥à¤·à¤•" +tree_search_limit_reached_html = "आपकी खोज ने पेड़ में पà¥à¤°à¤¦à¤°à¥à¤¶à¤¿à¤¤ करने के लिठबहà¥à¤¤ सारे परिणाम लौटाà¤à¥¤ केवल पहले दिखा रहा है <b>%%limit%%</b>आइटम नहीं है। पूरी खोज के लिठकà¥à¤²à¤¿à¤• करें <a id="fullSearchLink" href="%%url%%" target="_blank">here.</a>" +trending_items_channel_title = "टà¥à¤°à¥‡à¤‚डिंग आइटम" +unique_tags = "अदà¥à¤µà¤¿à¤¤à¥€à¤¯ टैग" +University Library = "विशà¥à¤µà¤µà¤¿à¤¦à¥à¤¯à¤¾à¤²à¤¯ का पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯" +Unknown = "अजà¥à¤žà¤¾à¤¤" +unrecognized_facet_label = "अनà¥à¤¯" +unsubscribe_confirmation = "कà¥à¤¯à¤¾ आप ईमेल सदसà¥à¤¯à¤¤à¤¾ रदà¥à¤¦ करना चाहते हैं?" +unsubscribe_description = "à¤à¤µà¤¿à¤·à¥à¤¯ में यह संदेश पà¥à¤°à¤¾à¤ªà¥à¤¤ नहीं करना चाहते हैं? निमà¥à¤¨à¤²à¤¿à¤–ित लिंक का उपयोग करके सदसà¥à¤¯à¤¤à¤¾ रदà¥à¤¦ करें" +unsubscribe_successful = "सदसà¥à¤¯à¤¤à¤¾ रदà¥à¤¦ कर दी गई" +Upgrade VuFind = "वीयू खोज का नवीनीकरण करें" +upgrade_description = "यदि आप पिछले वीयू खोज संसà¥à¤•à¤°à¤£ को अपगà¥à¤°à¥‡à¤¡ कर रहे हैं, तो आप इस टूल से अपनी पà¥à¤°à¤¾à¤¨à¥€ सेटिंग लोड कर सकते हैं" +URL = "यूआरà¤à¤²" +Use for = "के लिठउपयोग" +Use instead = "इसके बजाय उपयोग करें" +User Account = "उपà¤à¥‹à¤•à¥à¤¤à¤¾ खाता" +Username = "उपयोगकरà¥à¤¤à¤¾ नाम " +Username cannot be blank = "उपयोगकरà¥à¤¤à¤¾ नाम खाली नहीं हो सकता है" +Username is already in use in another library card = "उपयोगकरà¥à¤¤à¤¾ नाम पहले से ही किसी अनà¥à¤¯ पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ कारà¥à¤¡ में उपयोग में है" +verification_done = "आपका ईमेल पता सफलतापूरà¥à¤µà¤• सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ हो गया है" +verification_email_change_sent = "ईमेल - सतà¥à¤¯à¤¾à¤ªà¤¨ निरà¥à¤¦à¥‡à¤¶ नठईमेल पते पर à¤à¥‡à¤œà¥‡ गठहैं। परिवरà¥à¤¤à¤¨ पà¥à¤°à¤à¤¾à¤µà¥€ होने से पहले आपको पते का सतà¥à¤¯à¤¾à¤ªà¤¨ करना होगा।" +verification_email_notification = "आपके खाते के लिठअपना ईमेल पता सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ करने के लिठà¤à¤• अनà¥à¤°à¥‹à¤§ किया गया था %%library%%." +verification_email_sent = "ईमेल पता सतà¥à¤¯à¤¾à¤ªà¤¨ निरà¥à¤¦à¥‡à¤¶ इस खाते के साथ पंजीकृत ईमेल पते पर à¤à¥‡à¤œà¥‡ गठहैं।" +verification_email_subject = "वीयू खोज ईमेल सतà¥à¤¯à¤¾à¤ªà¤¨" +verification_email_url_pretext = "आप इस यूआरà¤à¤² पर अपना ईमेल पता सतà¥à¤¯à¤¾à¤ªà¤¿à¤¤ कर सकते हैं: %%url%%" +verification_too_soon = "आपके ईमेल को सतà¥à¤¯à¤¾à¤ªà¤¨ की आवशà¥à¤¯à¤•à¤¤à¤¾ है। हाल ही में आपके पंजीकृत ईमेल पते पर à¤à¤• ईमेल à¤à¥‡à¤œà¤¾ गया था। यदि आपको यह पà¥à¤°à¤¾à¤ªà¥à¤¤ नहीं हà¥à¤†, तो कृपया कà¥à¤› मिनट पà¥à¤°à¤¤à¥€à¤•à¥à¤·à¤¾ करें और पà¥à¤¨: पà¥à¤°à¤¯à¤¾à¤¸ करें।" +verification_user_not_found = "हमें आपका खाता नहीं मिला" +VHS = "वीà¤à¤šà¤à¤¸" +Video = "वीडियो" +Video Clips = "वीडियो कà¥à¤²à¤¿à¤ª" +Videos = "वीडियो" +View Book Bag = "पà¥à¤¸à¥à¤¤à¤• बैग देखें" +View Complete Issue = "पूरा मà¥à¤¦à¥à¤¦à¤¾ देखें" +View Full Collection = "पूरा संगà¥à¤°à¤¹ देखें" +View Full Record = "पूरा अà¤à¤¿à¤²à¥‡à¤– देखें " +View in EDS = "ईडीà¤à¤¸ में देखें" +View online: Full view Book Preview from the Hathi Trust = "ऑनलाइन देखें: हाथी टà¥à¤°à¤¸à¥à¤Ÿ से पूरी पà¥à¤¸à¥à¤¤à¤• बà¥à¤• पूरà¥à¤µà¤¾à¤µà¤²à¥‹à¤•à¤¨" +View Record = "अà¤à¤¿à¤²à¥‡à¤– देखें" +View Records = "अà¤à¤¿à¤²à¥‡à¤– देखें" +View this record in EBSCOhost = "इस रिकॉरà¥à¤¡ को ईबीà¤à¤¸à¤¸à¥€à¤“à¤à¤š में देखें" +view_already_selected = "%%current%% पहले से चयनित देखें" +visual_facet_parent = "से" +Volume = "खंड" +Volume Holdings = "वॉलà¥à¤¯à¥‚म होलà¥à¤¡à¤¿à¤‚गà¥à¤¸" +VuFind Configuration = "वीयू खोज विनà¥à¤¯à¤¾à¤¸" +vufind_upgrade_fail = "हम इस समय वीयू खोज को अपगà¥à¤°à¥‡à¤¡ नहीं कर सकते हैं " +Warning: These citations may not always be 100% accurate = "चेतावनी: ये उदà¥à¤§à¤°à¤£ हमेशा 100% सटीक नहीं हो सकते हैं" +wcterms_broader = "वà¥à¤¯à¤¾à¤ªà¤• विषय" +wcterms_exact = "संबंधित विषय" +wcterms_narrower = "संकीरà¥à¤£ विषय" +Web = "वेब" +What am I looking at = "मेरी नज़र किस पर है ? " +widen_prefix = "अपनी खोज को विसà¥à¤¤à¥ƒà¤¤ करने का पà¥à¤°à¤¯à¤¾à¤¸ करें" +wiki_link = "विकिपीडिया दà¥à¤µà¤¾à¤°à¤¾ पà¥à¤°à¤¦à¤¾à¤¨ किया गया" +with filters = "फ़िलà¥à¤Ÿà¤° के साथ" +with_selected = "चà¥à¤¨à¤¿à¤¨à¥à¤¦à¤¾ के साथ" +Year of Publication = "पà¥à¤°à¤•à¤¾à¤¶à¤¨ का वरà¥à¤· " +Yesterday = "बिता कल" +You do not have any fines = "आपके पास कोई जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾ नहीं है" +You do not have any holds or recalls placed = "आपके पास कोई होलà¥à¤¡ या रिकॉल नहीं है" +You do not have any interlibrary loan requests placed = "आपके पास कोई à¤à¥€ अंतःकà¥à¤°à¤¿à¤¯à¤¾à¤¤à¥à¤®à¤• ऋण अनà¥à¤°à¥‹à¤§ नहीं है" +You do not have any items checked out = "आपके पास कोई आइटम चेक आउट नहीं है" +You do not have any library cards = "आपके पास कोई पà¥à¤¸à¥à¤¤à¤•à¤¾à¤²à¤¯ कारà¥à¤¡ नहीं है " +You do not have any saved resources = "आपके पास कोई बचा हà¥à¤† संसाधन नहीं है। à¤à¤• खोज करें और आइटमों को सहेजने के लिठपसंदीदा में जोड़ें बटन का उपयोग करें " +You do not have any storage retrieval requests placed = "आपके पास कोई संगà¥à¤°à¤¹à¤£ पà¥à¤¨à¤°à¥à¤ªà¥à¤°à¤¾à¤ªà¥à¤¤à¤¿ अनà¥à¤°à¥‹à¤§ नहीं है" +You must be logged in first = "आपको पहले लॉग इन होना चाहिà¤" +Your Account = "आपका खाता" +Your book bag is empty = "आपका बà¥à¤• बैग खाली है" +Your Checked Out Items = "आपका चेक आउट आइटम" +Your Comment = "आपकी टिपà¥à¤ªà¤£à¥€" +Your Favorites = "बची हà¥à¤ˆ वसà¥à¤¤à¥à¤à¤ " +Your Fines = "आपका जà¥à¤°à¥à¤®à¤¾à¤¨à¤¾ " +Your Holds and Recalls = "आपका धारण और सà¥à¤®à¤°à¤£" +Your Lists = "आपकी सूची" +Your Profile = "आपकी रूपरेखा" +Your search terms = "आपकी खोज की शरà¥à¤¤à¥‡à¤‚" +Your Tags = "आपका टैग" +your_match_would_be_here = "आपका मिलान यहां होगा" +Zip = "ज़िप" +zoom = "ज़ूम" diff --git a/languages/hr.ini b/languages/hr.ini new file mode 100644 index 0000000000000000000000000000000000000000..c1088bf95df85bd3e5745fd23fbf3cd4ee42340d --- /dev/null +++ b/languages/hr.ini @@ -0,0 +1,1262 @@ +; - Translation courtesy of Milo Ivir <mail@milotype.de> +; For future reference: +;Croatian = Hrvatski +Abstract = "Sažetak" +Access = "Pristup" +Access URL = "URL pristupa" +access_denied = "Zabranjen pristup." +Accession Number = "Pristupni broj" +Account = "RaÄun" +account_block_options_missing = "Neke mogućnosti su uklonjene, jer tvoj raÄun sadrži ograniÄenje. Detalji: %%details%%" +account_has_alerts = "Tvoj raÄun sadrži upozorenja" +Add a Library Card = "Dodaj Älansku iskaznicu" +Add a Note = "Dodaj biljeÅ¡ku" +Add Tag = "Dodaj oznaku" +Add Tags = "Dodaj oznake" +Add to another list = "Dodaj u jedan drugi popis" +Add to Book Bag = "Dodaj u koÅ¡aricu" +Add to favorites = "Spremi u popis" +Add your comment = "Dodaj komentar" +add_comment_fail_blank = "Komentar ne smije biti prazan." +add_comment_success = "Komentar je dodan." +add_favorite_fail = "GreÅ¡ka: Zapis nije spremljen" +add_list_fail = "GreÅ¡ka: Popis nije stvoren" +add_other_libraries = "UkljuÄi graÄ‘u drugih knjižnica" +add_search = "Dodaj polje za pretraživanje" +add_search_group = "Dodaj grupu za pretraživanje" +add_tag_error = "GreÅ¡ka: Nije bilo moguće spremiti oznaku" +add_tag_note = "Razmaci će razdvojiti oznake. Koristi navodnike za pojmove koji sadrže viÅ¡e rijeÄi." +add_tag_success = "Oznake su spremljene" +add_to_favorites_html = "Dodaj <em>%%title%%</em> u favorite" +Address = "Adresa" +adv_search_all = "Sva polja" +adv_search_author = "Autor" +adv_search_callnumber = "Signatura" +adv_search_filters = "Primijenjeni filtri" +adv_search_isn = "ISBN/ISSN" +adv_search_journaltitle = "Naslov žurnala" +adv_search_label = "Traži" +adv_search_publisher = "IzdavaÄ" +adv_search_select_all = "odaberi sve" +adv_search_series = "Serije" +adv_search_subject = "Predmet" +adv_search_title = "Naslov" +adv_search_toc = "Sadržaj" +adv_search_year = "Godina izdanja" +Advanced = "Napredno" +Advanced Search = "Napredno pretraživanje" +advSearchError_noRights = "Žao nam je, ali nemaÅ¡ dozvolu za ureÄ‘ivanje pretraživanja. Možda je tvoja sesija preglednika istekla?" +advSearchError_notAdvanced = "Pretraživanje koje želiÅ¡ urediti nije napredno pretraživanje." +advSearchError_notFound = "Zatraženo pretraživanje nije naÄ‘eno." +ajax_load_interrupted = "UÄitavanje prekinuto" +ajaxview_label_information = "Informacije" +ajaxview_label_tools = "Alati" +alert_email_address = "Planirani rezultati upozorenja će se slati na e-adresu" +All = "Sve" +All Fields = "Sva polja" +All Pages Loaded = "Sve stranice uÄitane" +All Text = "Sav tekst" +alphabrowse_matches = "Rezultati" +alphabrowselink_html = "Pregledaj unose prema %%index%%, poÄevÅ¡i od <a href="%%url%%">%%from%%</a>." +An error has occurred = "DoÅ¡lo je do greÅ¡ke" +An error occurred during execution; please try again later. = "Tijekom izvrÅ¡avanja je doÅ¡lo do greÅ¡ke; pokuÅ¡aj kasnije ponovo." +AND = "I" +and = "i" +anonymous_tags = "Anonimne oznake" +APA Citation = "APA naÄin citiranja" +applied_filter = "Primijenjeni filtar" +Article = "ÄŒlanak" +Ask a Librarian = "Upitaj knjižniÄara" +Associated country = "Pripadajuća zemlja" +Audience = "Publika" +Audio = "Audio" +authentication_error_admin = "U ovom trenutku te ne možemo prijaviti. Kontaktiraj administratora sustava za pomoć." +authentication_error_blank = "Informacije za prijavu ne smiju biti prazne." +authentication_error_creation_blocked = "Nije ti dozvoljeno stvoriti raÄun." +authentication_error_denied = "Vjerodajnice se ne poklapaju! Pristup zabranjen." +authentication_error_email_not_verified_html = "Tvoja e-adresa joÅ¡ nije potvrÄ‘ena. Provjeri sanduÄić s neželjenom poÅ¡tom. Ako treba, možemo ti <a href="%%url%%">ponovo poslati potvrdu e-poÅ¡tom</a>." +authentication_error_in_progress = "Zahtjev za autentifikacijom se već obraÄ‘uje. PokuÅ¡aj kasnije ponovo, ako moraÅ¡ poÄeti ispoÄetka." +authentication_error_invalid = "Neispravna prijava – pokuÅ¡aj ponovo." +authentication_error_loggedout = "Ti si se odjavio/la." +authentication_error_technical = "U ovom trenutku te ne možemo prijaviti. PokuÅ¡aj kasnije ponovo." +Author = "Autor" +Author Browse = "Pregledaj autore" +Author Notes = "BiljeÅ¡ke autora" +Author Results for = "NaÄ‘eni autori za" +Author Search Results = "Pretraživanjem naÄ‘eni autori" +Authority File = "Normativna datoteka" +Authors = "Autori" +Authors Related to Your Search = "Autori povezani s tvojim pretraživanjem" +Auto configuration is currently disabled = "Automatsko konfiguriranje je trenutaÄno onemogućeno" +auto_configure_description = "Ako se radi o novoj instalaciji, možda možeÅ¡ ispraviti greÅ¡ku pomoću VuFind alata za automatsko konfiguriranje." +auto_configure_disabled = "Automatsko konfiguriranje je onemogućeno." +auto_configure_title = "Automatsko konfiguriranje" +Availability = "Dostupnost" +Available = "Dostupno" +Available Functionality = "Dostupna funkcionalnost" +Awards = "Nagrade" +Back to Record = "Natrag na zapis" +Back to Search Results = "Natrag na rezultate pretraživanja" +Backtrace = "Praćenje unatrag" +Bag = "KoÅ¡arica" +Balance = "Saldo" +Barcode = "Barkod" +Be the first to leave a comment = "Budi prvi tko komentira" +Be the first to tag this record = "Budi prvi tko oznaÄuje ovaj zapis" +Bibliographic Details = "Bibliografski detalji" +Bibliography = "Bibliografija" +Blu-ray Disc = "Blu-ray disk" +Book = "Knjiga" +Book Bag = "KoÅ¡arica" +Book Chapter = "Poglavlje knjige" +Book Cover = "Korice knjige" +bookbag_confirm_empty = "Zaista želiÅ¡ isprazniti svoju koÅ¡aricu?" +bookbag_delete = "IzbriÅ¡i odabrana djela iz koÅ¡arice" +bookbag_delete_selected = "IzbriÅ¡i odabir" +bookbag_email = "PoÅ¡alji odabrana djela iz koÅ¡arice e-porukom" +bookbag_email_selected = "PoÅ¡alji odabir e-porukom" +bookbag_export = "Izvezi odabrana djela iz koÅ¡arice" +bookbag_export_selected = "Izvezi odabir" +bookbag_full = "Puna" +bookbag_full_msg = "Tvoja koÅ¡arica je puna" +bookbag_is_empty = "Tvoja koÅ¡arica je prazna" +bookbag_print_selected = "IspiÅ¡i odabir" +bookbag_save = "Spremi odabrana djela iz koÅ¡arice" +bookbag_save_selected = "Spremi odabir" +Bookmark = "KnjiÅ¡ka oznaka" +Books = "Knjige" +Borrowing Location = "Lokacija za posudbu" +Braille = "Brajica" +Breadcrumbs = "Navijgacija" +Brief View = "Kratki pregled" +Browse = "Pregledaj" +Browse Alphabetically = "Pregledaj abecednim redom" +Browse for Authors = "Pregledaj autore" +Browse Home = "Pregledaj prvu stranu" +Browse the Catalog = "Pregledaj katalog" +Browse the Collection = "Pregledaj zbirku" +Browse the Collection Alphabetically = "Pregledaj zbirku abecednim redom" +browse_author = "Autor" +browse_dewey = "Signatura (Dewey)" +browse_format = "Format" +browse_lcc = "Signatura (LC)" +browse_publishDate = "Godina izdanja" +browse_title = "Naslov" +browse_topic = "Tema" +bulk_email_success = "Tvoja djela su poslana e-porukom" +bulk_email_title = "Djela iz kataloga knjižnice" +bulk_error_missing = "Nedostajali su neki podaci. Tvoj upit nije uspio." +bulk_export_not_supported = "Odabrani zapisi ne podržavaju grupni izvoz." +bulk_fail = "Nažalost je doÅ¡lo do greÅ¡ke. PokuÅ¡aj ponovo." +bulk_noitems_advice = "Nije odabrano niti jedno djelo. OznaÄi kvadratić pored djela i pokuÅ¡aj ponovo." +bulk_save_error = "Nedostajali su neki podaci. Tvoja djela nisu spremljena." +bulk_save_success = "Tvoja djela su uspjeÅ¡no spremljena" +By = "Prema" +by = "od" +By Alphabetical = "Abecednim redom" +By Author = "Prema autoru" +By Call Number = "Prema signaturi" +By Course = "Prema predmetu" +By Department = "Prema odjelu" +By Era = "Prema razdoblju" +By Genre = "Prema žanru" +By Instructor = "Prema instruktoru" +By Popularity = "Prema popularnosti" +By Recent = "Prema nedavnim" +By Region = "Prema regiji" +By Title = "Prema naslovu" +By Topic = "Prema temi" +Call Number = "Signatura" +callnumber_abbrev = "Sign." +Cannot find record = "Nije moguće naći zapis" +Cannot find similar records = "Nije moguće naći sliÄne zapise" +cannot set = "Nije moguće postaviti" +Cassette = "Kaseta" +cat_establish_account = "Za postavljanje tvog profila upiÅ¡i sljedeće informacije:" +cat_password_abbrev = "Lozinka za katalog" +cat_username_abbrev = "KorisniÄko ime za katalog" +Catalog Login = "Prijava u katalog" +Catalog Results = "Rezultati iz kataloga" +catalog_login_desc = "UpiÅ¡i svoje podatke za prijavu." +CD = "CD" +Change Email Address = "Promijeni e-adresu" +Change Password = "Promijeni lozinku" +change_email_disabled = "U ovom trenutku ti nije dozvoljeno promijeniti e-adresu" +change_email_verification_reminder = "Slanjem ovog obrasca, poslat će se e-poruka na novu adresu; morat ćeÅ¡ kliknuti na poveznicu u e-poruci kako bi promjena stupila na snagu." +change_notification_email_message = "Upravo je postavljen zahtjev za promjenom e-adrese na %%library%%. Ako zahtjev nije tvoj, prijavi se na %%url%% i potvrdi integritet tvog raÄuna. Ako imaÅ¡ kakvih pitanja ili nedoumica, obrati se podrÅ¡ci na %%email%%." +change_notification_email_subject = "Napomena o promjeni e-poÅ¡te" +channel_add_more = "Dodaj joÅ¡ ovakvih kanala" +channel_browse = "Pregledaj daljnje zapise" +channel_expand = "Istraži povezane kanale" +channel_explore = "Istraži kanale" +channel_search = "Prikaži djela kao rezultate pretraživanja" +channel_searchbox_label = "Traži daljnje kanale:" +Check Hold = "Provjeri posudbu" +Check Recall = "Provjeri mogućnost rezervacije" +check_profile = "Provjeri informacije o korisniku." +Checked Out = "PosuÄ‘eno" +Checked Out Items = "PosuÄ‘ena djela" +Checkedout = "PosuÄ‘eno" +Checkout Date = "Datum posudbe" +Chicago Citation = "ÄŒikaÅ¡ki stil citiranja" +child_record_count = "%%count%% zapisa" +child_records = "Sadržaji/dijelovi" +Choose a Category to Begin Browsing = "Odaberi kategoriju i poÄni pregledati" +Choose a Column to Begin Browsing = "Odaberi stupac i poÄni pregledati" +Choose a List = "Odaberi popis" +choose_login_method = "Odaberi naÄin prijave:" +citation_issue_abbrev = "br." +citation_multipage_abbrev = "pp." +citation_singlepage_abbrev = "str." +citation_volume_abbrev = "Vol." +Cite this = "Citiraj ovo" +City = "Grad" +Clear = "Isprazni" +clear_tag_filter = "Isprazni filtar" +close = "zatvori" +Code = "KÈd" +Collection = "Zbirka" +Collection Browse = "Pregledaj zbirku" +Collection Items = "Djela zbirke" +collection_disambiguation = "NaÄ‘ene su viÅ¡estruko poklapajuće zbirke" +collection_empty = "Nema djela za prikaz." +collection_view_record = "Prikaži zapis" +Collections = "Zbirke" +comment_anonymous_user = "Anonimno" +comment_error_load = "GreÅ¡ka: Nije moguće ponovo iscrtati popis komentara" +comment_error_save = "GreÅ¡ka: Nije moguće spremiti komentar" +Comments = "Komentari" +Company/Entity = "Kompanija/Jedinica" +Conference Proceeding = "IzvjeÅ¡taj sastanka" +Configuration = "Konfiguracija" +confirm_delete = "Zaista ovo želiÅ¡ izbrisati?" +confirm_delete_brief = "Izbrisati djelo?" +confirm_delete_library_card_brief = "Izbrisati Älansku iskaznicu?" +confirm_delete_library_card_text = "Zaista želiÅ¡ izbrisati ovu Älansku iskaznicu?" +confirm_delete_list_brief = "Izbrisati popis?" +confirm_delete_list_text = "Zaista želiÅ¡ izbrisati ovaj popis?" +confirm_delete_tags_brief = "IzbriÅ¡i oznake" +confirm_dialog_no = "Ne" +confirm_dialog_yes = "Da" +confirm_hold_cancel_all_text = "ŽeliÅ¡ li prekinuti tvoje trenutaÄne narudžbe?" +confirm_hold_cancel_selected_text = "ŽeliÅ¡ li prekinuti tvoje odabrane narudžbe?" +confirm_ill_request_cancel_all_text = "ŽeliÅ¡ li prekinuti tvoje trenutaÄne zahtjeve za meÄ‘uknjižniÄnom posudbom?" +confirm_ill_request_cancel_selected_text = "ŽeliÅ¡ li prekinuti tvoje odabrane zahtjeve za meÄ‘uknjižniÄnom posudbom?" +confirm_new_password = "Potvrdi novu lozinku" +confirm_storage_retrieval_request_cancel_all_text = "ŽeliÅ¡ li prekinuti tvoje trenutaÄne zahtjeve za pretraživanjem spremiÅ¡ta?" +confirm_storage_retrieval_request_cancel_selected_text = "ŽeliÅ¡ li prekinuti tvoje odabrane zahtjeve za pretraživanjem spremiÅ¡ta?" +Contents = "Sadržaji" +Contributing Source = "Izvor doprinosa" +Contributors = "Doprinositelji" +Coordinates = "Koordinate" +Copies = "Primjerci" +Copy = "Primjerak" +Copyright = "Autorsko pravo" +Corporate Author = "Autor kompanije" +Corporate Authors = "Autori kompanije" +Country = "Zemlja" +Course = "TeÄaj" +Course Reserves = "Rezervacije za teÄajeve" +course_reserves_empty_list = "Nema odgovarajućih rezervacija za teÄajeve." +Cover Image = "Slika omota" +Create a List = "Stvori popis" +Create New Account = "Stvori novi raÄun" +Create New Password = "Stvori novu lozinku" +Created = "Stvoreno" +Database = "Baza podataka" +Date = "Datum" +Date of birth = "Datum roÄ‘enja" +Date of death = "Datum smrti" +date_day_placeholder = "D" +date_from = "Od" +date_month_placeholder = "M" +date_to = "Do" +date_year_placeholder = "G" +Debug Information = "Informacije o ispravljanju greÅ¡aka" +del_search = "Ukloni grupu pretraživanja" +Delete = "IzbriÅ¡i" +delete_account_confirm = "Zaista želiÅ¡ izbrisati svoj raÄun?" +delete_account_description_html = "Spremljena pretraživanja i popisi favorita će biti izbrisani. Ako želiÅ¡, kasnije možeÅ¡ uspostaviti novi raÄun." +delete_account_failure = "Neuspjelo brisanje raÄuna." +delete_account_success_message = "Tvoj raÄun je izbrisan. Odjava …" +delete_account_title = "IzbriÅ¡i raÄun" +delete_all = "IzbriÅ¡i sve" +delete_comment_failure = "Nije bilo moguće izbrisati komentar." +delete_comment_success = "Komentar je izrisan." +delete_list = "IzbriÅ¡i popis" +delete_page = "IzbriÅ¡i stranicu" +delete_selected = "IzbriÅ¡i odabir" +delete_selected_favorites = "IzbriÅ¡i odabrane favorite" +delete_tag = "IzbriÅ¡i oznaku" +delete_tags = "IzbriÅ¡i oznake" +delete_tags_by = "IzbriÅ¡i oznake prema" +Department = "Odsjek" +Description = "Opis" +Desired Username = "Željeno korisniÄko ime" +Detailed View = "Prikaz detalja" +Details = "Prikaz za djelatnike knjižnice" +Displaying the top = "Prikaz prvih" +Document Inspector = "Inspektor dokumenta" +Document Type = "Vrsta dokumenta" +DOI = "Digitalni identifikator objekta" +doi_detected_html = "ÄŒini se da tvoje pretraživanje sadrži digitalni identifikator objekta. Klikni ovdje za provjeru dostupnosti resursa: <a href="%%url%%">%%doi%%</a>" +Draw Search Box = "Crtaj polje za pretraživanje" +draw_searchbox_end = "Otpusti miÅ¡a kako bi se crtanje zavrÅ¡ilo." +draw_searchbox_start = "Klikni i povuci za odabir podruÄja." +Due = "Rok posudbe" +Due Date = "Datum roka posudbe" +DVD = "DVD" +eBook = "e-knjiga" +Edit = "Uredi" +edit = "uredi" +Edit Library Card = "Uredi Älansku karticu" +Edit this Advanced Search = "Uredi ovo napredno pretraživanje" +edit_list = "Uredi popis" +edit_list_fail = "Nažalost, ti ne smijeÅ¡ urediti ovaj popis" +edit_list_success = "Popis je uspjeÅ¡no aktualiziran." +Edition = "Izdanje" +eds_expander_fulltext = "Traži i u cijelom tekstu Älanka" +eds_expander_relatedsubjects = "Primijeni jednake predmete" +eds_expander_thesaurus = "Primijeni povezane rijeÄi" +eds_limiter_FC = "Samo katalog" +eds_limiter_FC1 = "Samo spremiÅ¡te institucije" +eds_limiter_FM6 = "Dostupna audio snimka" +eds_limiter_FR = "Reference dostupne" +eds_limiter_FT = "Cijeli tekst" +eds_limiter_FT1 = "Dostupno u zbirci knjižnice" +eds_limiter_RV = "Recenzirano od vrÅ¡njaka" +eds_mode_all = "NaÄ‘i sve pojmove u pretraživanju" +eds_mode_any = "NaÄ‘i bilo koji pojam u pretraživanju" +eds_mode_bool = "LogiÄki/Fraza" +eds_mode_smart = "SmartText pretraživanje" +eds_modes_and_expanders = "Modusi za pretraživanje i proÅ¡irenja" +Electronic = "ElektroniÄki" +Email = "E-poÅ¡ta" +Email Address = "E-adresa" +Email address is invalid = "E-adresa je neispravna" +Email Record = "PoÅ¡alji zapis e-poÅ¡tom" +Email this = "PoÅ¡alji ovo e-poÅ¡tom" +Email this Search = "PoÅ¡alji ovo pretraživanje e-poÅ¡tom" +email_change_pending_html = "ImaÅ¡ joÅ¡ nedovrÅ¡enu promjenu e-adrese u %%pending%%. Klikni poveznicu u potvrdnoj e-poruci koja je poslana na ovu adresu, kako bi se promjena dovrÅ¡ila. Ako treba, možemo <a href="%%url%%">ponovo poslati potvrdnu e-poruku</a>." +email_failure = "GreÅ¡ka – Nije moguće poslati poruku" +email_link = "Poveznica" +email_login_desc = "Koristi sljedeću poveznicu za prijavu. Ako osobno nisi pokrenuo/la prijavu, slobodno zanemari ovu poruku. Poveznica vrijedi samo ograniÄeno vrijeme i samo u pregledniku, u kojem si upisao/la e-adresu." +email_login_link = "Poveznica za prijavu: <%%url%%>" +email_login_link_sent = "Na tvoju e-adresu smo poslali poveznicu za prijavu. Možda će potrajati nekoliko trenutaka dok stigne. Ako je ne vidiÅ¡ u poÅ¡tanskom pretincu, provjeri i neželjenu poÅ¡tu." +email_login_requested = "Zatražena je prijava s tvojom e-adresom na %%title%%." +email_login_subject = "Prijavi se na %%title%%" +email_maximum_recipients_note = "Dozvoljeno je najviÅ¡e %%max%% primatelja." +email_multiple_recipients_note = "MožeÅ¡ odrediti viÅ¡estruke primatelje, razdijeljenih zarezom." +email_selected = "PoÅ¡alji odabrano putem e-poÅ¡te" +email_selected_favorites = "PoÅ¡alji odabrane favorite putem e-poÅ¡te" +email_sending = "Slanje poruke …" +email_subject = "Predmet" +email_success = "Poruka poslana" +Empty = "Prazno" +Empty Book Bag = "Prazna koÅ¡arica" +empty_search_disallowed = "Prazan upit nije dozvoljen s trenutaÄnim ciljem pretraživanja" +Enable Auto Config = "Aktiviraj automatsko konfiguriranje" +End Page = "Zadnja stranica" +Era = "Era" +error_inconsistent_parameters = "Oprosti, doÅ¡lo je do greÅ¡ke. Ustanovljena je nedosljednost u parametrima." +error_page_parameter_list_heading = "Parametri za zahtjev" +Exception = "Iznimke" +Excerpt = "Izvatci" +exclude_facet = "IskljuÄi poklapajuće rezultate" +exclude_newspapers = "IskljuÄi Älanke iz novina" +Expires = "IstjeÄe" +Export = "Izvezi" +Export Favorites = "Izvezi favorite" +Export Items = "Izvezi djela" +Export Record = "Izvezi zapis" +Export to = "Izvezi u " +export_choose_format = "Odaberi izvozni format." +export_download = "Preuzmi datoteku" +export_exporting = "Stvaranje izvozne datoteke" +export_fail = "Tvoja djela nisu izvezena" +export_invalid_format = "Ovaj zapis ne podržava odabrani izvozni format." +export_missing = "Neki su podaci nedostajali. Tvoja djela nisu izvezena." +export_no_formats = "Ovaj zapis ne podržava izvoz." +export_save = "Spremi datoteku" +export_selected = "Izvezi odabrano" +export_selected_favorites = "Izvezi odabrane favorite" +export_send = "PoÅ¡alji na %%service%%" +export_success = "Izvoz spreman" +export_unsupported_format = "Nepodržani izvozni format" +external_auth_heading = "Pristup licenziranom materijalu" +external_auth_login_message = "Prijavi se za pristup licenziranom materijalu" +external_auth_unauthorized = "NemaÅ¡ autorizaciju za pristup licenziranom materijalu" +external_auth_unauthorized_desc = "Tvoja metoda prijave ne omogućava pristup licenziranom materijalu. Odjavi se i ponovo se prijavi na drugi naÄin." +facet_list_empty = "Nema podataka" +facet_list_for = "Popis za %%field%%" +FAQs = "ÄŒesto postavljena pitanja" +fav_delete = "IzbriÅ¡i odabrane favorite" +fav_delete_deleting = "Tvoji se favoriti briÅ¡u." +fav_delete_fail = "Oprosti, doÅ¡lo je do greÅ¡ke. Tvoji favoriti nisu izbrisani." +fav_delete_missing = "Neki podaci su nedostajali. Tvoji favoriti nisu izbrisani." +fav_delete_success = "Tvoji favoriti su izbrisani." +fav_delete_warn = "Izbrisat ćeÅ¡ ove favorite sa svih popisa. Ako želiÅ¡ izbrisati favorite samo s odreÄ‘enog popisa, odaberi popis prije nego Å¡to kliknete gumb za brisanje." +fav_email_fail = "Oprosti, doÅ¡lo je do greÅ¡ke. Tvoji favoriti nisu poslani e-poÅ¡tom." +fav_email_missing = "Neki podaci su nedostajali. Tvoji favoriti nisu poslani e-poÅ¡tom." +fav_email_success = "Tvoji favoriti su poslani e-poÅ¡tom, kao Å¡to je bilo traženo." +fav_export = "Izvezi favorite" +fav_list_delete = "Popis je izbrisan." +fav_list_delete_cancel = "Ovaj popis nije izbrisan." +fav_list_delete_fail = "Oprosti, doÅ¡lo je do greÅ¡ke. Tvoj popis nije izbrisan." +Favorites = "Spremljena djela" +Fee = "Pristojba" +Feedback = "Povratna informacija" +feedback_email = "PoÅ¡alji e-poÅ¡tom" +feedback_name = "Ime" +Field of activity = "PodruÄje djelatnosti" +File Description = "Opis datoteke" +Filter = "Filtar" +Filter Collection = "Filtriraj zbirku" +filter_tags = "Filtriraj oznake" +filter_toggle_entries = "%%count%% filtra" +filter_wildcard = "Bilo koja" +Find = "NaÄ‘i" +Find More = "NaÄ‘i joÅ¡" +Find New Items = "NaÄ‘i nove jedinice" +Finding Aid = "Pomoć za pronalaženje" +Fine = "Zakasnina" +Fine Date = "Datum zakasnine" +fine_limit_patron = "Dosegao/la si granicu zakasnina i ne možeÅ¡ produljiti posudbu" +Fines = "Zakasnine" +First = "Prvo" +First Name = "Ime" +First Search Result = "Prvi rezultat pretrage" +fix_metadata = "Da, ispravi metapodatke; Äekat ću" +for search = "za pretraživanje" +Forgot Password = "Zaboravljena lozinka" +Form Submitted! = "Obrazac je poslan!" +Format = "Format" +From = "Od" +Full description = "Cijeli opis" +Full text is not displayed to guests = "Cijeli tekst se ne prikazuje gostima." +fulltext_limit = "OgraniÄi na Älanke, Äiji su cijeli tekstovi dostupni" +Gender = "Spol" +Genre = "Žanr" +Geographic Search = "Geografsko pretraživanje" +Geographic Terms = "Geografski pojmovi" +Geography = "Geografija" +Get full text = "Preuzmi cijeli tekst" +Get more information = "Preuzmi daljnje informacije" +Get RSS Feed = "Preuzmi RSS sažetak" +Globe = "Globus" +Go = "Idi" +Go to Standard View = "Idi na standardni prikaz" +go_to_list = "Idi na popis" +google_map_cluster = "Klaster" +google_map_cluster_points = "ToÄke klastera" +Government Document = "Vladin dokument" +Grid = "Mreža" +Group = "Grupa" +group_AND = "SVE grupe" +group_OR = "BILO KOJA grupa" +Has Illustrations = "Sadrži ilustracije" +Help = "Pomoć" +Help with Advanced Search = "Pomoć za napredno pretraživanje" +Help with Search Operators = "Pomoć za operatore pretraživanja" +help_page_missing = "Tražena stranica za pomoć ne postoji." +hierarchy_hide_tree = "Sakrij cijelu hijerarhiju" +hierarchy_show_tree = "Prikaži cijelu hijerarhiju" +hierarchy_tree = "Kontekst" +hierarchy_tree_error = "Oprosti, nismo mogli uÄitati hijerarhijsko stablo" +hierarchy_view_context = "Prikaz konteksta" +History = "Povijest" +history_delete = "IzbriÅ¡i" +history_delete_link = "IzbriÅ¡i" +history_empty_search = "Bilo Å¡to (prazno pretraživanje)" +history_limits = "OgraniÄenja" +history_no_searches = "U tvojoj povijesti trenutaÄno nema pretraživanja." +history_purge = "PoniÅ¡ti nespremljena pretraživanja" +history_recent_searches = "Nedavna pretraživanja" +history_results = "Rezultati" +history_save = "Spremiti?" +history_save_link = "Spremi" +history_saved_searches = "Spremljena pretraživanja" +history_schedule = "Raspored upozorenja" +history_search = "Traži" +history_time = "Vrijeme" +hold_available = "Posudba je spremna za preuzimanje" +hold_available_until = "Posudba je spremna za preuzimanje do %%date%%" +hold_cancel = "Prekini narudžbu" +hold_cancel_all = "Prekini sve narudžbe" +hold_cancel_fail = "Tvoj zahtjev nije prekinut. Zatraži pomoć na informacijskom pultu" +hold_cancel_selected = "Prekini odabrane narudžbe" +hold_cancel_success = "Tvoja narudžba je uspjeÅ¡no prekinuta" +hold_cancel_success_items = "Broj uspjeÅ¡no prekinutih narudžbi: %%count%%" +hold_date_invalid = "UpiÅ¡i ispravni datum" +hold_date_past = "UpiÅ¡i datum u budućnosti" +hold_empty_selection = "Nijedna narudžba nije odabrana" +hold_error_blocked = "NemaÅ¡ dozvolu za postavljanjem narudžbe za ovu jedinicu" +hold_error_fail = "Tvoj zahtjev nije uspio. Zatraži pomoć na informacijskom pultu" +hold_invalid_pickup = "Upisana je neispravna lokacija za preuzimanje posudbe. PokuÅ¡aj ponovo" +hold_invalid_request_group = "Upisana je neispravna grupa zahtjeva za narudžbe. PokuÅ¡aj ponovo" +hold_items_available = "Nije moguće postaviti narudžbu, jer su djela dostupna." +hold_login = "Prijavi se ako želiÅ¡ primati informacije o narudžbama i rezervacijama" +hold_place = "Postavi zahtjev" +hold_place_fail_missing = "Tvoj zahtjev nije uspio. Nedostajali su neki podaci. Zatraži pomoć na informacijskom pultu" +hold_place_success_html = "Tvoj zahtjev je uspio. <a href="%%url%%">Tvoje narudžbe i rezervacije</a>." +hold_profile_html = "Za primanje informacija o narudžbama i rezervacijama, postavi vlastiti <a href="%%url%%">profil</a>." +hold_queue_position = "Mjesto u redu Äekanja" +hold_record_already_on_loan = "Ovaj se naslov već posudio/la" +hold_request_group = "Zahtjev od" +hold_requested_group = "Postavljen zahtjev od" +hold_required_by = "Nije viÅ¡e potrebno nakon" +hold_success = "Tvoj zahtjev je uspio" +Holdings = "Primjerci" +Holdings at Other Libraries = "Primjerci u drugim knjižnicama" +holdings_details_from = "Detalji primjeraka od %%location%%" +Holdings_notes = "BiljeÅ¡ke" +Holds = "Narudžbe" +Holds and Recalls = "Narudžbe i rezervacije" +Home = "Naslovnica" +home_browse = "Pregledaj prema" +HTML Full Text = "HTML cijeli tekst" +Identifier = "Identifikator" +ill_request_available = "Posudba je spremna za preuzimanje" +ill_request_cancel = "Prekini zahtjev meÄ‘uknjižniÄne posudbe" +ill_request_cancel_all = "Prekini sve zahtjeve meÄ‘uknjižniÄne posudbe" +ill_request_cancel_fail = "Tvoj zahtjev nije prekinut. Zatraži pomoć na informacijskom pultu" +ill_request_cancel_selected = "Prekini odabrane zahtjeve meÄ‘uknjižniÄne posudbe" +ill_request_cancel_success = "Tvoj zahtjev je uspjeÅ¡no prekinut" +ill_request_cancel_success_items = "Broj uspjeÅ¡no prekinutih zahtjeva: %%count%%" +ill_request_canceled = "Prekinuto" +ill_request_check_text = "Provjeri zahtjeve za meÄ‘uknjižniÄnom posudbom" +ill_request_comments = "Komentari" +ill_request_date_invalid = "UpiÅ¡i ispravan datum" +ill_request_date_past = "UpiÅ¡i datum u budućnosti" +ill_request_empty_selection = "Nijedan zahtjev za meÄ‘uknjižniÄnom posudbom nije odabran" +ill_request_error_blocked = "NemaÅ¡ dozvolu za postavljanje zahtjeva meÄ‘uknjižniÄne posudbe za ovo djelo" +ill_request_error_fail = "Tvoj zahtjev nije uspio. Zatraži pomoć na informacijskom pultu" +ill_request_error_technical = "Tvoj zahtjev nije uspio zbog greÅ¡ke u sustavu. Zatraži pomoć na informacijskom pultu" +ill_request_error_unknown_patron_source = "Glavna knjižnica nije identificirana u zahtjevu za meÄ‘uknjižniÄnu posudbu." +ill_request_invalid_pickup = "Upisana je neispravna lokacija za preuzimanje posudbe. PokuÅ¡aj ponovo" +ill_request_pick_up_library = "Knjižnica za preuzimanje posudbe" +ill_request_pick_up_location = "Lokacija za preuzimanje posudbe" +ill_request_place_fail_missing = "Tvoj zahtjev nije uspio. Nedostajali su neki podaci. Zatraži pomoć na informacijskom pultu" +ill_request_place_success = "Tvoj zahtjev je uspio" +ill_request_place_success_html = "Tvoj zahtjev je uspio. <a href="%%url%%">Zahtjevi za meÄ‘uknjižniÄnom posudbom</a>." +ill_request_place_text = "Postavi zahtjev za meÄ‘uknjižniÄnom posudbom" +ill_request_processed = "ObraÄ‘eno" +ill_request_profile_html = "Za dobivanje informacija o zahtjevima za meÄ‘uknjižniÄnom posudbom postavi vlastiti <a href="%%url%%">profil</a>." +ill_request_submit_text = "Postavi zahtjev" +Illustrated = "Ilustrirano" +ils_account_create_error = "Nije bilo moguće stvoriti tvoj raÄun na naÅ¡em sustavu knjižnice. Ako problem ne nestane, kontaktiraj knjižnicu." +ils_action_unavailable = "Tražena funkcija nije dostupna s aktivnom Älanskom iskaznicom." +ils_connection_failed = "Neuspjelo povezivanje sa sustavom za upravljanje knjižnicom. Podaci o tvom raÄunu se ne mogu prikazati. Ako problem ne nestane, kontaktiraj knjižnicu." +ils_offline_holdings_message = "Informacije o dostupnosti primjeraka i djela trenutaÄno nisu dostupne. IspriÄavamo se zbog prouzroÄenih neugodnosti. Slobodno nas kontaktiraj radi daljnje pomoći:" +ils_offline_home_message = "Pojedinosti o tvom raÄunu i podaci o tvojim trenutaÄnim posudbama neće biti dostupni za to vrijeme. IspriÄavamo se zbog prouzroÄenih neugodnosti. Slobodno nas kontaktiraj radi daljnje pomoći:" +ils_offline_login_message = "Pojedinosti o tvom raÄunu neće biti dostupni za to vrijeme. IspriÄavamo se zbog prouzroÄenih neugodnosti. Slobodno nas kontaktiraj radi daljnje pomoći:" +ils_offline_status = "NaÅ¡ sustav za upravljanje knjižnicom se trenutaÄno održava." +ils_offline_title = "Sustav se trenutaÄno održava" +ils_transaction_history_disabled = "Povijest posudbi nije omogućena za aktivnu Älansku iskaznicu." +Import Record = "Uvezi zapis" +in = "u" +In This Collection = "U ovu zbirku" +in_collection_label = "U zbirku:" +include_synonyms = "ProÅ¡iri rezultate pomoću sinonima" +Index Terms = "Pojmovi kataloga" +Indexes = "Katalozi" +information = "Informacije" +Institution = "Institucija" +Institutional Login = "Institucionalna prijava" +institutional_login_desc = "UpiÅ¡i svoje opće korisniÄko ime i lozinku." +Instructor = "Instruktor" +Interlibrary Loan Requests = "Zahtjevi za meÄ‘uknjižniÄnim posudbama" +Internet = "Internet" +Invalid Patron Login = "Neispravna prijava na glavnu knjižnicu" +Invalid phone number. = "Neispravan broj telefona." +Invalid Recipient Email Address = "Neispravna e-adresa primatelja" +Invalid Sender Email Address = "Neispravna e-adresa poÅ¡iljatelja" +ISBN = "ISBN" +ISBN/ISSN = "ISBN/ISSN" +ISSN = "ISSN" +Issue = "Izdanje" +Item Description = "Opis djela" +Item Notes = "BiljeÅ¡ke" +Item removed from favorites = "Djelo je uklonjeno iz favorita" +Item removed from list = "Djelo je uklonjeno iz popisa" +Items = "Djela" +items = "djela" +items_added_to_bookbag = "%%count%% djela su dodana u tvoju koÅ¡aricu" +items_already_in_bookbag = "%%count%% djela su već dodana u tvoju koÅ¡aricu ili se ne mogu dodati" +Journal = "Žurnal" +Journal Articles = "ÄŒlanci žurnala" +Journal Info = "Informacije o žurnalu" +Journal Title = "Naslov žurnala" +Journals = "Žurnali" +Jump to = "SkoÄi na" +just_cataloged = "Samo katalogizirano" +Keyword = "KljuÄna rijeÄ" +Keyword Filter = "Filtar kljuÄnih rijeÄi" +Kit = "Oprema" +Language = "Jezik" +large = "Veliko" +Last = "Zadnje" +Last Modified = "Zadnja promjena" +Last Name = "Prezime" +Last Search Result = "Zadnji rezultat pretrage" +less = "manje" +libphonenumber_invalid = "Neispravan broj telefona" +libphonenumber_invalidcountry = "Neispravan predbroj" +libphonenumber_invalidregion = "Neispravni kÈd regije:" +libphonenumber_notanumber = "ÄŒini se da znakovni niz nije telefonski broj" +libphonenumber_toolong = "Zadani znakovni niz je predugaÄak za telefonski broj" +libphonenumber_tooshort = "Zadani znakovni niz je prekratak za telefonski broj" +libphonenumber_tooshortidd = "Telefonski broj prekratak nakon internacionalnog predbroja" +Library = "Knjižnica" +Library Card = "ÄŒlanska iskaznica" +Library Card Deleted = "ÄŒlanska iskaznica je izbrisana" +Library Card Name = "Ime Älanske iskaznice" +Library Cards = "ÄŒlanske iskaznice" +Library Cards Disabled = "ÄŒlanska iskaznica je onemogućena" +Library Catalog Password = "Lozinka za katalog knjižnice" +Library Catalog Profile = "Profil za katalog knjižnice" +Library Catalog Record = "Zapis u katalogu knjižnice" +Library Catalog Search = "Pretraživanje u katalogu knjižnice" +Library Catalog Search Result = "Rezultati pretraživanja u katalogu knjižnice" +Library Catalog Username = "KorisniÄko ime za katalog knjižnice" +Library Web Search = "Web pretraživanje knjižnice" +library_card_edit_password_placeholder = "Nova lozinka" +lightbox_error = "GreÅ¡ka: Nije moguće uÄitati skoÄni okvir" +Limit To = "OgraniÄi na" +Link to full results = "Poveznica na cjelokupne rezultate" +List = "Popis" +List Tags = "Izradi popis oznaka" +list_access_denied = "NemaÅ¡ dozvolu za prikaz ovog popisa." +list_edit_name_required = "Ime popisa je obavezno." +load_tag_error = "GreÅ¡ka: Nije moguće uÄitati oznake" +Loading = "UÄitavanje" +Loan History = "Povijest posudbi" +loan_history_empty = "NemaÅ¡ posudbe u tvojoj povijesti posudbi." +Local Login = "Lokalna prijava" +local_login_desc = "UpiÅ¡i korisniÄko ime i lozinku za ovu stranicu." +Located = "Lokalizirano" +Location = "Lokacija" +Log Out = "Odjavi se" +Login = "Prijava" +Login for full access = "Prijavi se za potpuni pristup." +login_disabled = "U ovom trenutku prijava nije dostupna." +login_target = "Knjižnica" +Logout = "Odjava" +Main Author = "Glavni autor" +Main Authors = "Glavni autori" +Major Categories = "Glavne kategorije" +Manage Tags = "Upravljaj oznakama" +Manuscript = "Manuskript" +Map = "Karta" +Map View = "Prikaz karte" +map_results_label = "Pri ovoj lokaciji:" +Maps = "Karte" +Media Format = "Format medija" +medium = "Medij" +MeSH Terms = "MeSH pojmovi" +Message = "Poruka" +Message From Sender = "Poruka poÅ¡iljatelja" +Metadata Prefix = "Prefiks metapodataka" +Microfilm = "Mikrofilm" +MLA Citation = "MLA naÄin citiranja" +Mobile Number = "Broj mobitela" +mobile_link = "ÄŒini se da si na mobilnom ureÄ‘aju; prebaciti na prikaz za mobitele?" +Monograph Title = "Naslov monografije" +more = "viÅ¡e" +More catalog results = "Daljnji rezultata kataloga" +More options = "Daljnje opcije" +More Summon results = "Daljnji Summon rezultati" +More Topics = "Daljnje teme" +more_authors_abbrev = "i dr." +more_info_toggle = "Prikaži/sakrij daljnje informacije." +more_topics = "JoÅ¡ %%count%% tema" +Most Recent Received Issues = "Najnovija dostavljena izdanja" +Multiple Call Numbers = "ViÅ¡estruke signature" +Multiple Locations = "ViÅ¡estruke lokacije" +Musical Score = "MuziÄki pogodak" +My Favorites = "Favoriti" +My Fines = "Zakasnine" +My Holds = "Narudžbe" +My Profile = "Profil" +Narrow Search = "Uže pretraživanje" +navigate_back = "Natrag" +nearby_items = "Naslovi u blizini "%%title%%"" +Need Help? = "TrebaÅ¡ pomoć?" +New Item Feed = "Sažetak novog djela" +New Item Search = "Pretraživanje novih djela" +New Item Search Results = "Rezultati pretraživanja novih djela" +New Items = "Nova djela" +New results found for search = "NaÄ‘eni su novi rezultati za pretragu" +New Title = "Novi naslov" +new_email_success = "Tvoja e-adresa je uspjeÅ¡no promijenjena" +new_password = "Nova lozinka" +new_password_success = "Tvoja lozinka je uspjeÅ¡no promijenjena" +new_results_heading = "Najnovijih rezultata: %%count%%" +new_user_welcome_subject = "Tvoj novi raÄun za %%library%%" +new_user_welcome_text = "Dobro doÅ¡ao, dobro doÅ¡la u %%library%%. Otvoren je jedan novi raÄun za %%firstname%% %%lastname%%. Tvoje korisniÄko ime je %%username%%. Postavi lozinku na ovoj stranici: %%url%%" +Newspaper = "Novine" +Next = "Dalje" +Next Search Result = "Sljedeći rezultat pretrage" +No citations are available for this record = "Nema citiranja za ovaj zapis" +No Cover Image = "Bez slike omota" +No dependency problems found = "Nema problema po pitanju ovisnosti" +No excerpts were found for this record. = "IsjeÄci za ovaj zapis nisu naÄ‘eni." +No library account = "Nema raÄuna za knjižnicu" +No new item information is currently available. = "TrenutaÄno nema informacija o novom djelu." +No Preference = "Bez preferencije" +No reviews were found for this record = "Nema recenzija za ovaj zapis" +No Tags = "Bez oznaka" +no_description = "Opis nije dostupan." +no_email_address = "Nedostaje e-adresa." +no_items_selected = "Nije odabrano niti jedno djelo" +nohit_active_filters = "Jedna ili viÅ¡e faseta filtra je primijenjena na pretraživanje. Ako ukloniÅ¡ filtre, vjerojatno ćeÅ¡ dobiti viÅ¡e rezultata." +nohit_change_tab = "PretražujeÅ¡ karticu "%%activeTab%%". Možda ćeÅ¡ u ostalim karticama takoÄ‘er neÅ¡to naći:" +nohit_filters = "TrenutaÄno primijenjen filtar na ovo pretraživanje:" +nohit_heading = "Bez rezultata!" +nohit_lookfor_html = "Tvoje pretraživanje – <strong>%%lookfor%%</strong> – se ne poklapa s niti jednim resursom." +nohit_no_filters = "Na ovo pretraživanje nije primijenjen niti jedan filtar.." +nohit_parse_error = "ÄŒini se da u tvom upitu neÅ¡to ne valja. Provjeri sintaksu. Ako ne pokuÅ¡avaÅ¡ koristiti napredne funkcije, postavljanjem upita pod navodnike može pomoći." +nohit_query_without_filters = "Ukloni sve filtre iz ovog pretraživanja." +nohit_spelling = "Možda moraÅ¡ isprobati jedan drugi naÄin pisanja" +nohit_suggest = "Preispitaj svoj upit, skrati ga, ukloni koju rijeÄ ili provjeri pravopis." +NOT = "NIJE" +Not Illustrated = "Nije ilustrirano" +Not On Reserve = "Nije rezervirano" +not_applicable = "--" +Note = "BiljeÅ¡ka" +note_760 = "Glavna serija" +note_762 = "Podserija" +note_765 = "Prijevod" +note_767 = "Prijevod" +note_770 = "Ima dodatak" +note_772 = "Dodatak za" +note_773 = "Sadržano u" +note_774 = "Sastavna jedinica" +note_775 = "Daljnje izdanje dostupno" +note_776 = "Dodatni oblik" +note_777 = "Izdano s" +note_780_0 = "Nastavlja se" +note_780_1 = "DjelomiÄno se nastavlja" +note_780_2 = "Zamijenjuje" +note_780_3 = "DjelomiÄno zamijenjuje" +note_780_4 = "IzraÄ‘eno iz" +note_780_5 = "Apsorbiran" +note_780_6 = "DjelomiÄno apsorbiran" +note_780_7 = "Izdvojeno iz" +note_785_0 = "Nastavlja se u" +note_785_1 = "DjelomiÄno se nastavlja u" +note_785_2 = "Zamijenjuje se s" +note_785_3 = "DjelomiÄno se zamijenjuje s" +note_785_4 = "Apsorbiran u" +note_785_5 = "DjelomiÄno apsorbiran u" +note_785_6 = "Podijeljeno u" +note_785_7 = "Sjedinjeno s / SaÄinjava" +note_785_8 = "Vraćeno na" +note_787 = "Ostale veze" +Notes = "BiljeÅ¡ke" +Number = "Broj" +number_decimal_point = "," +number_thousands_separator = "." +OAI Server = "OAI poslužitelj" +Occupation = "Zanimanje" +od_account_noaccess = "Ova Älanska iskaznica nema pristup na sadržaj u OverDriveu" +od_account_problem = "Postoji problem s tvojim raÄunom. %%message%%" +od_audiobook-mp3 = "MP3 zvuÄne knjige" +od_audiobook-overdrive = "SluÅ¡aj zvuÄne knjige na OverDriveu" +od_avail_avail = "Dostupno:" +od_avail_holds = "Narudžbe:" +od_avail_total = "Ukupno primjeraka:" +od_but_cancel_hold = "Prekini ovu narudžbu" +od_but_checkout = "Posudba putem OverDrivea" +od_but_checkout_s = "Posudba" +od_but_gettitle = "Preuzmi ovaj sadržaj" +od_but_gettitle_s = "Preuzmi" +od_but_hold = "Postavi narudžbu putem OverDrivea" +od_but_hold_s = "Postavi narudžbu" +od_but_return = "Vrati ovaj naslov" +od_cancel_hold = "Prekini narudžbu putem OverDrivea" +od_checkout = "OverDrive posudbe" +od_code_connection_failed = "Veza na OverDrive nije uspjela. Ako problem ne nestane, kontaktiraj knjižnicu." +od_code_contentnotavail = "Ovaj sadržaj nije dostupan u tvom podruÄju." +od_code_login_for_avail = "Prijavi se za prikaz dostupnosti" +od_code_resource_not_found = "Naslov nije naÄ‘en" +od_content = "OverDrive sadržaj" +od_dl_formats = "Podržani formati za preuzimanja" +od_docheckout_failure = "Nije bilo moguće posuditi ovaj naslov." +od_docheckout_success = "Ovaj naslov je posuÄ‘en tebi. Rok posudbe je %%expireDate%%" +od_early_return = "Prijevremena vraćanja na OverDriveu" +od_ebook-epub-adobe = "Adobe EPUB e-knjiga" +od_ebook-epub-open = "Open EPUB e-knjiga" +od_ebook-kindle = "Kindle knjiga" +od_ebook-mediado = "MediaDo Reader e-knjiga" +od_ebook-overdrive = "OverDrive Read e-knjiga" +od_ebook-pdf-adobe = "Adobe PDF e-knjiga" +od_ebook-pdf-open = "Open PDF e-knjiga" +od_expires_on = "Ovaj naslov istjeÄe %%due_date%%." +od_get_title = "OverDrive preuzimanje" +od_gettitle_failure = "Nije bilo moguće preuzeti ovaj naslov." +od_help_linktext = "OverDrive pomoć" +od_history = "OverDrive povijest" +od_hold = "OverDrive narudžba" +od_hold_cancel_failure = "Zahtjev za prekidom narudžbe nije uspio. " +od_hold_cancel_success = "Narudžba je uspjeÅ¡no prekinuta." +od_hold_email = "E-adresa za upozorenja o narudžbama: %%holdEmailAddress%%." +od_hold_now_avail = "Ova se narudžba može posuditi. Rok posudbe je %%expireDate%%." +od_hold_place_failure = "Zahtjev za narudžbom nije uspio." +od_hold_place_success = "Ovaj je naslov postavljen kao narudžba. Tvoja narudžba je na %%holdListPosition%%. mjestu." +od_hold_placed_on = "Narudžba je postavljena %%holdPlacedDate%%." +od_hold_queue = "Na %%holdPosition%%. mjestu od %%numberOfHolds%% u redoslijedu narudžbi." +od_holds = "OverDrive narudžbe" +od_info_unavail = "Ova informacija trenutaÄno nije dostupna." +od_is_checkedout = "Ovaj naslov si posudio/la. Rok posudbe je %%due_date%%." +od_is_on_hold = "Ovaj naslov si naruÄio/la." +od_loans = "OverDrive posudbe" +od_mycontent_help = "Za informacije o naslovima i za pomoć pri preuzimanju, pogledaj <a href="%%url%%">OverDrive pomoć</a>." +od_none_found = "Nema naslova." +od_return_failure = "Nije bilo moguće vratiti naslov." +od_return_success = "Ovaj je naslov vraćen." +od_video-streaming = "internetski prijenos video datoteke" +of_num_results = "#%%position%% od %%total%% rezultata" +old_password = "Stara lozinka" +On Reserve = "Rezervirano" +On Reserve - Ask at Circulation Desk = "Rezervirano – upitaj pri informacijskom pultu" +on_reserve = "Rezervacije – upitaj pri informacijskom pultu" +on_topic = "%%count%% djela na ovu temu" +Online Access = "Online pristup" +online_resources = "Cijeli tekst" +open_access_limit = "OgraniÄi na Open Access sadržaj" +operator_contains = "sadrži" +operator_exact = "je (toÄno)" +OR = "ILI" +or create a new list = "ili stvori novi popis" +original = "Original" +Other associated place = "Daljnje povezano mjesto" +Other Authors = "Daljnji autori" +Other Editions = "Daljnja izdanja" +Other Libraries = "Daljnje knjižnice" +Other Sources = "Daljnji izvori" +Page not found. = "Stranica nije pronaÄ‘ena." +page_first = "Idi na prvu stranicu" +page_last = "Idi na zadnju stranicu" +page_next = "Idi na sljedeću stranicu" +page_num = "%%page%%. stranica" +page_prev = "Idi na prethodnu stranicu" +pagination_label = "Paginacija" +Password = "Lozinka" +Password Again = "Ponovi lozinku" +Password cannot be blank = "Lozinka ne smije biti prazna" +password_error_auth_old = "Prethodno koriÅ¡tena lozinka je neispravna" +password_error_invalid = "Nova lozinka je neispravna (npr. sadrži neispravne znakove)" +password_error_not_unique = "Lozinka nije promijenjena" +password_maximum_length = "Maksimalni broj znakova za lozinku: %%maxlength%%" +password_minimum_length = "Minimalni broj znakova za lozinku: %%minlength%%" +password_only_alphanumeric = "Samo brojke i slova A-Z" +password_only_numeric = "Samo brojke" +Passwords do not match = "Lozinke se ne poklapaju" +past_days = "Zadnjih %%range%% dana" +PDF Full Text = "PDF cijeli tekst" +peer_reviewed = "Recenzirano od vrÅ¡njaka" +peer_reviewed_limit = "OgraniÄi na Älanke, recenziranih od vrÅ¡njaka" +permission_denied = "Zatražio/la si jednu stranicu ili radnju, ali za to nemaÅ¡ potrebnu dozvolu." +permission_denied_title = "Nije dozvoljeno" +Phone Number = "Broj telefona" +Photo = "Fotografija" +Physical Description = "Opis" +Physical Object = "Stvar" +pick_up_location = "Lokacija za preuzimanje posudbe" +Place a Hold = "Postavi narudžbu" +Place of birth = "Mjesto roÄ‘enja" +Place of death = "Mjesto smrti" +Playing Time = "Trajanje" +Please check back soon = "Navrati opet" +Please contact the Library Reference Department for assistance = "Za pomoć se obrati knjižnici" +Please enable JavaScript. = "Omogući JavaScript." +Please upgrade your browser. = "Nadogradi svoj preglenik." +Preferences = "Postavke" +Preferred Library = "Preferirana knjižnica" +Prev = "Prethodno" +Preview = "Pregled" +Preview from = "Pregled iz" +Previous Search Result = "Prethodni rezultat pretrage" +Previous Title = "Prethodni naslov" +Print = "IspiÅ¡i" +print_selected = "IspiÅ¡i odabrano" +Private = "Privatno" +Production Credits = "Informacije o proizvodnji" +Profile = "Profil" +profile_update = "Tvoj profil je promijenjen kao Å¡to je zatraženo" +pronounced = "izgovoreno" +Provider = "Pružatelj usluge" +Public = "Javno" +Publication = "Izdanje" +Publication Date = "Datum izdanja" +Publication Frequency = "UÄestalost izdanja" +Publication Information = "Informacije o izdanju" +Publication Type = "Vrsta izdanja" +Publication Year = "Godina izdanja" +Publication_Place = "Mjesto izdanja" +Published = "Izdano" +Published in = "Izdano u" +Publisher = "IzdavaÄ" +Publisher Information = "Informacije o izdavaÄu" +Publisher Permissions = "Dozvole izdavaÄa" +QR Code = "QR kÈd" +qrcode_hide = "Sakrij QR kÈd" +qrcode_show = "Prikaži QR kÈd" +query time = "vrijeme pretraživanja" +random_recommendation_title = "SluÄajna djela iz tvojih rezultata" +Range = "Opseg" +Range slider = "KlizaÄ za opseg" +Read the full review online... = "ProÄitaj cijelu recenziju online …" +Recall This = "Rezerviraj ovo" +recaptcha_not_passed = "CAPTCHA provjera nije proÅ¡la" +recently_returned_channel_title = "Nedavno vraćeno" +recommend_links_text = "MožeÅ¡ pokuÅ¡ati i:" +Record Citations = "Citiranja zapisa" +Record Count = "Broj zapisa" +Record Type = "Vrsta zapisa" +Recover Account = "Obnovi raÄun" +recovery_by_email = "Obnovi pomoću e-adrese" +recovery_by_username = "Obnovi pomoću korisniÄkog imena" +recovery_disabled = "Obnavljanje lozinke je onemogućeno" +recovery_email_notification = "Upravo je stvoren zahtjev za obnavljanjem lozinke za tvoj raÄun pri knjižnici %%library%%." +recovery_email_sent = "Upute za obnavljanje lozinke su poslane na e-adresu koja je registrirane za ovaj raÄun." +recovery_email_subject = "Obnavljanje VuFind raÄuna" +recovery_email_url_pretext = "Lozinku možeÅ¡ ponovo postaviti na ovom URL-u: %%url%%" +recovery_expired_hash = "Ova poveznica za obnavljanje je istekla" +recovery_invalid_hash = "Poveznica za obnavljanje nije prepoznata" +recovery_new_disabled = "U ovom trenutku ne smijeÅ¡ promijeniti lozinku" +recovery_title = "Obnavljanje lozinke" +recovery_too_soon = "Postavljeno je previÅ¡e zahtjeva za obnavljanje, pokuÅ¡aj kasnije ponovo" +recovery_user_not_found = "Nismo naÅ¡li tvoj raÄun" +rectangle_center_message = "Ovo je centralna toÄka za oznaÄeni pravokutnik" +Reference Material = "PriruÄnik" +Refine Results = "Detaljiziraj rezultate" +Region = "Regija" +relais_available = "Ovo je djelo dostupno putem meÄ‘uknjižniÄne posudbe. ŽeliÅ¡ li ga zatražiti na taj naÄin?" +relais_checking = "Provjera dostupnosti …" +relais_error_html = "DoÅ¡lo je do greÅ¡ke s ovim zahtjevom. Zatraži ovo djelo putem <a href="%%url%%" target='new'>web stranice za meÄ‘uknjižniÄnu posudbu</a>." +relais_request = "Zahtjev za meÄ‘uknjižniÄnu posudbu" +relais_requesting = "Zahtjev u tijeku …" +relais_search = "Traži meÄ‘uknjižniÄnu posudbu" +relais_success_label = "Potvrda:" +relais_success_message = "Zahtjev ID br. %%id%% je stvoren. Dobit ćeÅ¡ potvrdnu e-poruku." +Related Author = "Povezani autori" +Related Items = "Povezana djela" +Related Subjects = "Povezani predmeti" +Remove Filters = "Ukloni filtre" +Remove from Book Bag = "Ukloni iz koÅ¡arice" +renew_all = "Produži rok za sve posudbe" +renew_determine_fail = "Nismo mogli ustanoviti je li se rok tvoje posudbe može produžiti. Kontaktiraj djelatnike knjižnice." +renew_empty_selection = "Nijedno djelo nije odabrano" +renew_error = "Nismo uspjeli produžiti rok tvoje posudbe – kontaktiraj djelatnike knjižnice" +renew_fail = "Nije bilo moguće produžiti rok posudbe" +renew_item = "Produži rok posudbe" +renew_item_due = "Rok posudbe dospijeva za 24 sata" +renew_item_due_tooltip = "Posudbe koje uskoro dospijevaju" +renew_item_limit = "Rok posudbe se viÅ¡e ne može produžiti" +renew_item_no = "Rok ove posudbe se ne može produžiti" +renew_item_overdue = "Rok posudbe je dospio" +renew_item_overdue_tooltip = "Posudba je prekoraÄila rok" +renew_item_requested = "Jedan drugi korisnik je zatražio ovo djelo" +renew_select_box = "Produži rok posudbe" +renew_selected = "Produži rok posudbe odabranih djela" +renew_success = "Rok posudbe je uspjeÅ¡no produžen" +Renewed = "Produženo" +Request full text = "Zatraži cijeli tekst" +request_in_transit = "Na putu prema lokaciji za posuÄ‘ivanje" +request_place_text = "Postavi zahtjev" +request_submit_text = "PoÅ¡alji zahtjev" +Requests = "Zahtjevi" +Reserves = "Rezervacije" +Reserves Search = "Pretraživanje rezervacija" +Reserves Search Results = "Rezultati pretraživanja rezervacija" +reset_filters_button = "Resetiraj filtre" +result_checkbox_label = "Odaberi broj rezultata %%number%%" +result_count = "%%count%% rezultata" +Results = "Rezultati" +results = "rezultati" +Results for = "Rezultati za" +Results per page = "Broj rezultata po stranici" +Resumption Token = "Token za nastavak" +Return Date = "Datum vraćanja" +Review by = "Recenzija" +Reviews = "Recenzije" +Save = "Spremi" +Save Comment = "Spremi komentar" +save_search = "Spremi pretraživanje" +save_search_remove = "Ukloni spremljeno pretraživanje" +Saved in = "Spremljeno u" +schedule_daily = "Dnevno" +schedule_explanation = "Primaj upozorenja e-poÅ¡tom o novim rezultatima za pretragu." +schedule_none = "Bez" +schedule_weekly = "Tjedno" +Scheduled Alert Results = "Planirani rezultati upozorenja" +scholarly_limit = "OgraniÄi na Älanke iz znanstvenih Äasopisa" +Scroll to Load More = "Kliži, kako bi se uÄitalo viÅ¡e" +Search = "Traži" +Search For = "Traži" +Search For Items on Reserve = "Pretraži rezervacije" +Search History = "Povijest pretraživanja" +Search Home = "Pretraži prvu stranicu" +Search Mode = "Modus za pretraživanje" +Search Options = "Opcije za pretraživanje" +Search Results = "Rezultati pretraživanja" +search results of = "rezultati pretraživanja od" +Search Tips = "Savjeti za pretraživanje" +Search Tools = "Alati za pretraživanje" +Search Type = "Vrsta pretraživanja" +Search within collection = "Traži u zbirci" +search_AND = "SVE pojmove" +search_groups = "Pretraži grupe" +search_match = "Poklapajuće" +search_NOT = "NIJEDAN pojam" +search_OR = "BILO KOJI pojam" +search_save_success = "Pretraživanje uspjeÅ¡no spremljeno." +search_terms = "Pojmovi za pretraživanje" +search_unsave_success = "Spremljeno pretraživanje uspjeÅ¡no uklonjeno." +seconds_abbrev = "s" +see all = "pogledaj sve" +See also = "TakoÄ‘er pogledaj" +Select this record = "Odaberi ovaj zapis" +Select your carrier = "Odaberi prenositelja" +select_page = "Odaberi stranicu" +select_pickup_location = "Odaberi lokaciju za preuzimanje posudbe" +select_request_group = "Odaberi grupu zahtjeva" +Selected = "Odabrano" +Send = "PoÅ¡alji" +Send us your feedback! = "PoÅ¡alji nam povratne informacije!" +send_an_email_copy = "PoÅ¡alji kopiju na ovu adresu" +send_email_copy_to_me = "PoÅ¡alji kopiju meni" +Sensor Image = "Slika senzora" +Serial = "Serijal" +Series = "Serija" +Set = "Skupina" +show_filters_html = "Prikaži filtre (%%count%%)" +showing_items_html = "Prikaz djela <strong>%%start%% – %%end%%</strong>" +showing_items_of_html = "Prikaz djela <strong>%%start%% – %%end%%</strong> od <strong>%%total%%</strong>" +showing_results_for_html = "Prikaz rezultata <strong>%%start%% – %%end%%</strong> za pretraživanje '<strong>%%lookfor%%</strong>'" +showing_results_html = "Prikaz rezultata <strong>%%start%% – %%end%%</strong>" +showing_results_of_for_html = "Prikaz rezultata <strong>%%start%% – %%end%%</strong> od <strong>%%total%%</strong> za pretraživanje '<strong>%%lookfor%%</strong>'" +showing_results_of_html = "Prikaz rezultata <strong>%%start%% – %%end%%</strong> od <strong>%%total%%</strong>" +sidebar_close = "Sklopi boÄnu traku" +sidebar_expand = "Rasklopi boÄnu traku" +Similar Items = "Similar Items" +Skip to content = "PreskoÄi na sadržaj" +skip_confirm = "Zaista želiÅ¡ preskoÄiti ovaj korak?" +skip_fix_metadata = "Ne ispravljaj metapodatke u ovom trenutku." +skip_step = "PreskoÄi ovaj korak" +Slide = "Slajd" +sms_failure = "GreÅ¡ka! Nije moguće poslati poruku." +sms_phone_number = "Telefonski broj za SMS" +sms_sending = "Slanje poruke …" +sms_success = "Poruka poslana." +Software = "Softver" +Sorry, but the help you requested is unavailable in your language. = "Na žalost, pomoć koju tražiÅ¡ ne postoji na tvom jeziku." +Sort = "Razvrstaj" +sort_alphabetic = "Abecednim redom" +sort_author = "Autor" +sort_author_author = "Abecednim redom" +sort_author_relevance = "Popularnost" +sort_callnumber = "Signatura" +sort_checkout_date_asc = "Datum posudbe (prvo najstarije)" +sort_checkout_date_desc = "Datum posudbe (prvo najnovije)" +sort_count = "Broj rezultata" +sort_due_date_asc = "Datum roka posudbe (prvo najstarije)" +sort_due_date_desc = "Datum roka posudbe (prvo najnovije)" +sort_relevance = "ZnaÄajnost" +sort_return_date_asc = "Datum vraćanja posudbe (prvo najstarije)" +sort_return_date_desc = "Datum vraćanja posudbe (prvo najnovije)" +sort_title = "Naslov" +sort_year = "Datum uzlazno" +sort_year asc = "Datum silazno" +Source = "Izvor" +Source Title = "Naslov izvora" +spell_expand_alt = "ProÅ¡iri pretraživanje" +spell_suggest = "Alternative za pretraživanje" +Staff View = "Prikaz za djelatnike knjižnice" +Start a new Advanced Search = "ZapoÄni novo napredne pretraživanje" +Start a new Basic Search = "ZapoÄni novo osnovno pretraživanje" +Start Page = "PoÄetna stranica" +starting from = "poÄni od" +Status = "Status" +status_transit = "Na putu" +status_unknown_message = "Prikaz statusa u stvarnom vremenu nije dostupan" +Storage Retrieval Requests = "Zahtjev za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_available = "Posudba je spremna za preuzimanje" +storage_retrieval_request_cancel = "Prekini zahtjev za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_cancel_all = "Prekini sve zahtjeve za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_cancel_fail = "Tvoj je zahtjev prekinut. Zatraži pomoć na informacijskom pultu" +storage_retrieval_request_cancel_selected = "Prekini odabrane zahtjeve za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_cancel_success = "Tvoj je zahtjev uspjeÅ¡no prekinut" +storage_retrieval_request_cancel_success_items = "Broj uspjeÅ¡no prekinutih zahtjeva: %%count%%" +storage_retrieval_request_canceled = "Prekinuto" +storage_retrieval_request_check_text = "Provjeri zahtjev za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_comments = "Komentari" +storage_retrieval_request_date_invalid = "UpiÅ¡i ispravni datum" +storage_retrieval_request_date_past = "UpiÅ¡i datum u budućnosti" +storage_retrieval_request_empty_selection = "Nijedan zahtjev za pronalaženjem u spremiÅ¡tu nije odabran" +storage_retrieval_request_error_blocked = "NemaÅ¡ dovoljne dozvole za postavljanje zahtjeva za pronalaženjem djela u spremiÅ¡tu" +storage_retrieval_request_error_fail = "Tvoj zahtjev nije uspio. Zatraži pomoć na informacijskom pultu" +storage_retrieval_request_invalid_pickup = "Upisana je neispravna lokacija za preuzimanje posudbe. PokuÅ¡aj ponovo" +storage_retrieval_request_issue = "Datum" +storage_retrieval_request_place_fail_missing = "Tvoj zahtjev nije uspio. Nedostajali su neki podaci. Zatraži pomoć na informacijskom pultu" +storage_retrieval_request_place_success = "Tvoj je zahtjev uspio" +storage_retrieval_request_place_success_html = "Tvoj je zahtjev uspio. <a href="%%url%%">Zahtjev za pronalaženjem u spremiÅ¡tu</a>." +storage_retrieval_request_place_text = "Postavi zahtjev za pronalaženjem u spremiÅ¡tu" +storage_retrieval_request_processed = "ObraÄ‘eno" +storage_retrieval_request_profile_html = "Za primanje informacija o zahtjevu za pronalaženjem u spremiÅ¡tu, postavi <a href="%%url%%">profil</a>." +storage_retrieval_request_reference = "Referenca" +storage_retrieval_request_selected_item = "Odabrano djelo" +storage_retrieval_request_submit_text = "Postavi zahtjev" +storage_retrieval_request_volume = "Svezak" +storage_retrieval_request_year = "Godina" +Subcollection = "Podzbirka" +Subject = "Tema" +Subject Area = "Tematsko podruÄje" +Subject Geographic = "Geografske teme" +Subject Recommendations = "Tematske preporuke" +Subject Terms = "Tematski pojmovi" +Subject(s) = "Tema/Teme" +Subjects = "Teme" +Submit = "PoÅ¡alji" +Submitting = "Slanje u tijeku" +Suggested Topics = "Predložene teme" +Summary = "Sažetak" +Summon Results = "Rezultati Summon pretraživanja" +summon_database_recommendations = "Daljnje resurse možeÅ¡ naći ovdje:" +Supplements = "Nadopune" +Supplied by Amazon = "IsporuÄuje Amazon" +switch_view = "Prebaci na prikaz %%view%%" +switchquery_fuzzy = "Upotrebom nejednoznaÄnog pretraživanja se mogu naći sliÄno pisani pojmovi" +switchquery_intro = "Možda ćeÅ¡ dobiti viÅ¡e rezultata, ako podesiÅ¡ upit pretraživanja." +switchquery_lowercasebools = "Ako koristiÅ¡ Booleove operatore, oni se moraju pisati VELIKIM SLOVIMA" +switchquery_truncatechar = "Skrati upit pretraživanja za dobivanje viÅ¡e rezultata" +switchquery_unwantedbools = "RijeÄi I, ILI i NIJE mogu zbuniti pretraživanje; pokuÅ¡aj koristiti navodnike" +switchquery_unwantedquotes = "Uklanjanje navodnika može dozvoliti Å¡ire pretraživanje" +switchquery_wildcard = "Upotrebom zamjenskih znakova možeÅ¡ pronaći varijante pojmova" +System Unavailable = "Sustav nedostupan" +Table of Contents = "Sadržaj" +Table of Contents unavailable = "Sadržaj nedostupan" +Tag = "Oznaka" +Tag Management = "Upravljanje oznakama" +tag_delete_filter = "KoristiÅ¡ sljedeći filtar – KorisniÄko ime: %username%, oznaka: %tag%, resursa: %resource%" +tag_delete_warning = "Upozorenje! Izbrisat ćeÅ¡ %count% oznaka resursa" +tag_filter_empty = "Za ovaj filtar ne postoje oznake" +Tags = "Oznake" +tags_deleted = "Broj izbrisanih oznaka: %count%" +test_fail = "Neuspjelo" +test_fix = "Popravi" +test_ok = "U redu" +Text this = "PoÅ¡alji SMS" +Thank you for your feedback. = "Zahvaljujemo se na povratnoj informaciji." +That email address is already used = "Već se koristi ta e-adresa" +That username is already taken = "Već se koristi to korisniÄko ime" +The record you selected is not part of any of your lists. = "Odabrani zapis nije dio niti jednog tvojeg popisa." +The record you selected is not part of the selected list. = "Odabrani zapis nije dio odabranog popisa." +The system is currently unavailable due to system maintenance = "Sustav trenutaÄno nije dostupan zbog održavanja sustava" +Theme = "Izgled" +Thesis = "Disertacija" +This email was sent from = "Ova e-poruka je poslana od" +This field is required = "Ovo polje je obavezno" +This item is already part of the following list/lists = "Ovo djelo se već nalazi u sljedećim popisima" +This result not is displayed to guests = "Ovaj se rezultat ne prikazuje gostima." +Title = "Naslov" +Title not available = "Naslov nije dostupan" +Title View = "Prikaz naslova" +title_hold_place = "Postavi zahtjev na razini naslova" +To = "Za" +Too Many Email Recipients = "PreviÅ¡e e-adresa" +too_many_favorites = "Ovaj popis je prevelik za zajedniÄki prikaz. PokuÅ¡aj presložiti favorite u viÅ¡estruke popise ili ograniÄi upotrebu oznaka." +too_many_new_items = "PreviÅ¡e je novih djela za prikaz u jednom popisu. PokuÅ¡aj ograniÄiti pretraživanje." +too_many_reserves = "PreviÅ¡e je rezervacija za prikaz u jednom popisu. PokuÅ¡aj ograniÄiti pretraživanje" +top_facet_label = "%%label%% unutar tvog pretraživanja." +Topic = "Tema" +Topics = "Teme" +Total Balance Due = "Ukupno stanje na raÄunu" +total_comments = "Ukupno komentara" +total_lists = "Ukupno popisa" +total_resources = "Ukupno resursa" +total_saved_items = "Ukupno spremljenih djela" +total_tags = "Ukupno oznaka" +total_users = "Ukupno korisnika" +Transliterated Title = "Transliterirani naslov" +tree_search_limit_reached_html = "Pretraživanjem je dobiven prevelik broj rezultata za prikaz u stablu. Prikazuje se samo prvih <b>%%limit%%</b> djela. Za cjelovito pretraživanje klikni <a id="fullSearchLink" href="%%url%%" target="_blank">ovdje.</a>" +trending_items_channel_title = "NajÄitanija djela" +unique_tags = "Jedinstvene oznake" +University Library = "SveuÄiliÅ¡na knjižnica" +Unknown = "Nepoznato" +unrecognized_facet_label = "Ostale" +unsubscribe_confirmation = "ŽeliÅ¡ odjaviti pretplatu na e-poÅ¡tu?" +unsubscribe_description = "Ubuduće ne želiÅ¡ primati ovu poruku? Odjavi pretplatu pomoću sljedeće poveznice" +unsubscribe_successful = "Pretplata odjavljena" +Upgrade VuFind = "Nadogradi VuFind" +upgrade_description = "Ako nadograÄ‘ujeÅ¡ stariju VuFind verziju, s ovim alatom možeÅ¡ uÄitati svoje stare postavke." +URL = "URL" +Use for = "Koristi za" +Use instead = "Koristi umjesto" +User Account = "KorisniÄki raÄun" +Username = "KorisniÄko ime" +Username cannot be blank = "KorisniÄko ime ne smije biti prazno" +Username is already in use in another library card = "KorisniÄko ime se već koristi za jednu drugu Älansku iskaznicu" +verification_done = "Tvoja e-adresa je uspjeÅ¡no provjerena." +verification_email_change_sent = "Upute za potvrdu e-adrese poslane su na novu e-adresu. MoraÅ¡ potvrditi adresu kako bi promjena stupila na snagu." +verification_email_notification = "Upravo je postavljen zahtjev za potvrdu e-adrese za tvoj raÄun pri %%library%%." +verification_email_sent = "Upute za potvrdu e-adrese poslane su na e-adresu koja je registrirana za ovaj raÄun." +verification_email_subject = "VuFind provjera e-adresa" +verification_email_url_pretext = "Svoju e-adresu možeÅ¡ potvrditi na URL stranici: %%url%%" +verification_too_soon = "MoraÅ¡ potvrditi svoju e-adresu. E-poruka je poslana na e-adresu koja je registrirana za ovaj raÄun. Ako poruka joÅ¡ nije stigla, priÄekaj malo i pokuÅ¡aj ponovo." +verification_user_not_found = "Nismo naÅ¡li tvoj raÄun" +VHS = "VHS" +Video = "Video" +Video Clips = "Video isjeÄci" +Videos = "Videa" +View Book Bag = "Prikaži koÅ¡aricu" +View Complete Issue = "Prikaži cijelo izdanje" +View Full Collection = "Prikaži cijelu zbirku" +View Full Record = "Prikaži cijeli zapis" +View in EDS = "Prikaži u EDS-u" +View online: Full view Book Preview from the Hathi Trust = "Prikaži online: Cijeli prikaz pregleda knjige putem Hathi Trust" +View Record = "Prikaži zapis" +View Records = "Prikaži zapise" +View this record in EBSCOhost = "Prikaži ovaj zapis u EBSCOhost" +view_already_selected = "%%current%% prikaz već odabran" +visual_facet_parent = "Od" +Volume = "Svezak" +Volume Holdings = "Primjerci sveske" +VuFind Configuration = "VuFind konfiguracija" +vufind_upgrade_fail = "U ovom trenutku ne možemo nadograditi VuFind" +Warning: These citations may not always be 100% accurate = "Upozorenje: Ovi citati možda nisu uvijek 100% toÄni" +wcterms_broader = "Å ire teme" +wcterms_exact = "Povezane teme" +wcterms_narrower = "Uže teme" +Web = "Web" +What am I looking at = "Å to gledam?" +widen_prefix = "PokuÅ¡aj proÅ¡iriti pretraživanje na" +wiki_link = "Omogućuje Wikipedia" +with filters = "s filtrima" +with_selected = "s odabirom" +Year of Publication = "Godina izdanja" +Yesterday = "JuÄer" +You do not have any fines = "NemaÅ¡ zakasnina" +You do not have any holds or recalls placed = "NemaÅ¡ postavljenih narudžbi ili rezervacija" +You do not have any interlibrary loan requests placed = "NemaÅ¡ postavljenih zahtjeva za meÄ‘uknjižniÄnom posudbom" +You do not have any items checked out = "NemaÅ¡ niÅ¡ta posuÄ‘enog" +You do not have any library cards = "NemaÅ¡ Älanskih iskaznica" +You do not have any saved resources = "NemaÅ¡ spremljenih resursa. Pokreni pretraživanje i za spremanje djela koristi gumb "Dodaj u favorite"." +You do not have any storage retrieval requests placed = "NemaÅ¡ postavljenih zahtjeva za pronalaženjem u spremiÅ¡tu" +You must be logged in first = "Najprije se prijavi" +Your Account = "Tvoj raÄun" +Your book bag is empty = "Tvoja koÅ¡arica je prazna" +Your Checked Out Items = "Tvoji posuÄ‘eni naslovi" +Your Comment = "Tvoj komentar" +Your Favorites = "Spremljeni omiljeni naslovi" +Your Fines = "Tvoje zakasnine" +Your Holds and Recalls = "Tvoje narudžbe ili rezervacije" +Your Lists = "Tvoj popis" +Your Profile = "Tvoj profil" +Your search terms = "Tvoji pojmovi za pretraživanje" +Your Tags = "Tvoje etikete" +your_match_would_be_here = "Tvoje poklapanje bi se nalazilo ovdje." +Zip = "PoÅ¡tanski broj" +zoom = "Zumiranje" diff --git a/languages/it.ini b/languages/it.ini index 8b96bd23310375fb14d06eb8e457409c16eb02b5..e6fd318c1f47fbbfad4ded6847021d50b9e64d74 100644 --- a/languages/it.ini +++ b/languages/it.ini @@ -7,6 +7,7 @@ access_denied = "Accesso negato." Accession Number = "Accession Number" Account = "Account" account_block_options_missing = "Alcune opzioni sono state tolte per via di un blocco del tuo account. Dettagli: %%details%%" +account_has_alerts = "Il tuo account ha delle segnalazioni" Add a Library Card = "Aggiungi una tessera di biblioteca" Add a Note = "Aggiungi una nota" Add Tag = "Aggiungi Tag" @@ -46,8 +47,10 @@ Advanced Search = "Ricerca avanzata" advSearchError_noRights = "Mi spiace ma non hai il permesso di modificare la ricerca. Forse è terminata la tua sessione?" advSearchError_notAdvanced = "La ricerca che hai chiesto di modificare non è una ricerca avnzata." advSearchError_notFound = "La ricerca richiesta non è stata trovata." +ajax_load_interrupted = "Caricamento interrotto" ajaxview_label_information = "Informazioni" ajaxview_label_tools = "Strumenti" +alert_email_address = "I risultati della segnalazione pianificata sarà inviata all'indirizzo e-mail" All = "All" All Fields = "Tutti i Campi" All Pages Loaded = "Tutte le pagine caricate" @@ -57,6 +60,7 @@ alphabrowselink_html = "Naviga il catalogo per %%index%% partendo da <a href="%% An error has occurred = "Si è verificato un errore" An error occurred during execution; please try again later. = "Errore durante l'esecuzione; riprova più tardi." AND = "AND" +and = "e" anonymous_tags = "Tag anonimi" APA Citation = "Citazione APA" applied_filter = "Filtro applicato" @@ -69,6 +73,8 @@ authentication_error_admin = "Non puoi loggarti in questo momento. Per assistenz authentication_error_blank = "Le informazioni di login non possono essere vuote." authentication_error_creation_blocked = "Non sei autorizzato a creare un account." authentication_error_denied = "Le credenziali non coincidono! Accesso negato." +authentication_error_email_not_verified_html = "Il tuo indirizzo E-mail non è stato ancora verificato. Per favore controlla il tuo filtro dello spam per il messaggio di verifica. Se necessario, possiamo <a href="%%url%%">inviare di nuovo la E-mail di verifica</a>." +authentication_error_in_progress = "La richiesta di autenticazione è in elaborazione. Per favore riprova più tardi se devi ricominciare." authentication_error_invalid = "Login non valido -- riprova." authentication_error_loggedout = "Sei stato disconnesso." authentication_error_technical = "Non puoi loggarti in questo momento.Riprova più tardi." @@ -94,7 +100,6 @@ Backtrace = "Resoconto" Bag = "Carrello" Balance = "Bilancio" Barcode = "Codice a barre" -basic_search_keep_filters = "Mantieni i filtri attuali" Be the first to leave a comment = "Lascia un commento" Be the first to tag this record = "puoi essere il primo ad aggiungerne! " Bibliographic Details = "Dettagli Bibliografici" @@ -163,6 +168,7 @@ Call Number = "Collocazione" callnumber_abbrev = "Colloc: " Cannot find record = "Nessun record trovato" Cannot find similar records = "Nessun record simile trovato" +cannot set = "Impossibile impostare" Cassette = "Cassetta" cat_establish_account = "Per definire il profilo del tuo account, inserisici le seguenti informazioni:" cat_password_abbrev = "Password (catalogo)" @@ -171,7 +177,12 @@ Catalog Login = "Login del catalogo" Catalog Results = "Risultati del Catalogo" catalog_login_desc = "Inserisci le credenziali con cui accedi al catalogo della biblioteca." CD = "CD" +Change Email Address = "Modifica indirizzo E-mail" Change Password = "Cambia Password" +change_email_disabled = "In questa fase, non ti è permesso cambiare il tuo indirizzo E-mail" +change_email_verification_reminder = "L'invio di questo form manderà una E-mail al nuovo indirizzo; dovrai cliccare un link nella E-mail prima che il cambiamento diventi effettivo." +change_notification_email_message = "È stata appena effettuata una richiesta di cambiare il tuo indirizzo E-mail presso %%library%%. Se non hai effettuato tu questa richiesta, puoi effettuare il log in all'indirizzo %%url%% e confermare l'integrità del tuo account. Per favore contatta il supporto all'indirizzo E-mail %%email%% se hai domande o dubbi." +change_notification_email_subject = "Notifica della modifica della E-mail dell'Account" channel_add_more = "Aggiungi altre selezioni tipo questa" channel_browse = "Naviga più records" channel_expand = "Esplora selezioni correlate" @@ -180,6 +191,7 @@ channel_search = "Mostra copie come risultati di ricerca" channel_searchbox_label = "Certca su più sezioni:" Check Hold = "Controlla le prenotazioni" Check Recall = "Controlla le richieste" +check_profile = "Controlla le informazioni utente." Checked Out = "Non disponibile" Checked Out Items = "Documenti presi in prestito" Checkedout = "Preso in prestito" @@ -213,6 +225,7 @@ comment_error_load = "Errore: lista commenti non accessibile" comment_error_save = "Errore: commento non salvato" Comments = "Commenti" Company/Entity = "Società /Entità " +Conference Proceeding = "Atti del Convegno" Configuration = "Configurazione" confirm_delete = "Sei sicuro di voler procedere con l'eliminazione?" confirm_delete_brief = "Cancellare questo elemento?" @@ -233,6 +246,7 @@ confirm_storage_retrieval_request_cancel_selected_text = "Vuoi cancellare le ric Contents = "Contenuti" Contributing Source = "Fonte del contributo" Contributors = "Nomi associati" +Coordinates = "Coordinate" Copies = "Copie" Copy = "Copia" Copyright = "Copyright" @@ -292,6 +306,7 @@ Due Date = "Data di scadenza" DVD = "DVD" eBook = "eBook" Edit = "Modifica" +edit = "modifica" Edit Library Card = "Modifica la tessera della biblioteca" Edit this Advanced Search = "Modifica questa ricerca avanzata" edit_list = "Modifica la lista" @@ -320,8 +335,14 @@ Email address is invalid = "L'indirizzo email non è valido" Email Record = "Email (record)" Email this = "Invia email" Email this Search = "Invia questa ricerca per email" +email_change_pending_html = "Hai una modifica della E-mail pendente %%pending%%. Per favore fai click sul collegamento nella E-mail di verifica inviata a questo indirizzo per completare la modifica. Se necessario, possiamo <a href="%%url%%">inviare di nuovo la E-mail di verifica</a>." email_failure = "Errore - il messaggio non può essere inviato" email_link = "Link" +email_login_desc = "Per favore utilizza il seguente collegamento per effettuare l'accesso. Se non hai avviato la procedura di accesso, puoi semplicemente ignorare questo messaggio. Nota che il collegamento è valido solamente per un tempo limitato e solo con il dispositivo che hai utilizzato per inserire l'indirizzo E-mail." +email_login_link = "Collegamento per effettuare l'accesso: <%%url%%>" +email_login_link_sent = "Abbiamo mandato un collegamento per effettuare l'accesso al tuo indirizzo E-mail. L'operazione può richiedere alcuni minuti. Se non ricevi il collegamento a breve, per favore controlla anche il tuo filtro anti-spam." +email_login_requested = "L'accesso a %%title%% è stato richiesto con il tuo indirizzo E-mail." +email_login_subject = "Accedi a %%title%%" email_maximum_recipients_note = "Inserisci al massimo %%max%% indirizzi." email_multiple_recipients_note = "Separi gli indirizzi email da una virgola." email_selected = "Invia per email" @@ -331,6 +352,7 @@ email_subject = "Soggetto" email_success = "Messagio inviato" Empty = "Vuoto" Empty Book Bag = "Svuota il carrello" +empty_search_disallowed = "Una interrogazione vuota non è permessa con il target di ricerca attuale" Enable Auto Config = "Abilita l'auto-configurazione" End Page = "Fine della pagina" Era = "Epoca" @@ -363,6 +385,8 @@ external_auth_heading = "Accesso s materiale sottoposto a licenza" external_auth_login_message = "Login per accedere a materiale sottoposto a licenza" external_auth_unauthorized = "Non sei autorizzato ad accedere a materiale sotto licenza" external_auth_unauthorized_desc = "La tua modalità di login non permette di accedere a materiale sottoposto a licenza d'usio. Esci e fai login con un altro metodo." +facet_list_empty = "Nessun dato disponibile" +facet_list_for = "Lista di Facet per %%field%%" FAQs = "FAQ" fav_delete = "Elimina i preferiti selezionati" fav_delete_deleting = "I tuoi preferiti stanno per essere eliminati." @@ -385,13 +409,16 @@ feedback_name = "Nome" Field of activity = "Campo di attività " File Description = "Descrizione del file" Filter = "Filtra" +Filter Collection = "Collezione dei filtri" filter_tags = "Filtra i tag" +filter_toggle_entries = "%%count%% filtri" filter_wildcard = "Qualsiasi" Find = "Cerca" Find More = "Cerca" Find New Items = "Trova nuovi documenti" Finding Aid = "Finding Aid" Fine = "Multa" +Fine Date = "Data della sanzione" fine_limit_patron = "Hai raggiunto il limite dei prestiti consentiti e non puoi rinnovare il prestito" Fines = "Multe" First = "Primo" @@ -411,6 +438,7 @@ Geographic Search = "Ricerca gerografica" Geographic Terms = "Termini geografici" Geography = "Geografia" Get full text = "Testo" +Get more information = "Ottieni maggiori informazioni" Get RSS Feed = "Feed RSS" Globe = "Mappamondo" Go = "Vai" @@ -418,6 +446,7 @@ Go to Standard View = "Visulizzazione standard" go_to_list = "Vai alla lista" google_map_cluster = "Cluster" google_map_cluster_points = "Punti di raggruppamento" +Government Document = "Documento governativo" Grid = "Griglia" Group = "Gruppo" group_AND = "Tutti i gruppi" @@ -444,9 +473,11 @@ history_results = "Risultati" history_save = "Salva" history_save_link = "Salva" history_saved_searches = "Ricerche salvate" +history_schedule = "Pianifica la segnalazione" history_search = "Ricerca" history_time = "Tempo" hold_available = "Pronto per il ritiro" +hold_available_until = "Disponibile per il ritiro fino al %%date%%" hold_cancel = "Cancella prenotazione" hold_cancel_all = "Cancella tutte le prenotazioni" hold_cancel_fail = "La tua richiesta non è stata cancellata. Contatta il servizio di prestito per assistenza." @@ -510,6 +541,7 @@ ill_request_processed = "Elaborato" ill_request_profile_html = "Per informazioni sulle richieste di prestito interbibliotecario, vai al tuo <a href="%%url%%">profilo nel catalogo della biblioteca</a>." ill_request_submit_text = "Fai una richiesta" Illustrated = "Illustrato" +ils_account_create_error = "Il tuo account non può essere creato nel nostro sistema di gestione bibliotecaria. Se il problema persiste, contatta la tua biblioteca." ils_action_unavailable = "La funzione richiesta non è disponibile con la tessera di biblioteca ora attiva." ils_connection_failed = "Il sistema di gestione della nostra biblioteca in questo momento è in manutenzione." ils_offline_holdings_message = "Le informazioni sulla disponibilità delle copie non sono disponibili. Ci scusiamo per l'inconveniente. Per assistenza puoi contattarci:" @@ -587,6 +619,7 @@ Library Web Search = "Library Web Search" library_card_edit_password_placeholder = "Nuova Password" lightbox_error = "Errore: i Popup non funzionano" Limit To = "Limita a" +Link to full results = "Collegamento ai risultati completi" List = "Lista" List Tags = "Elenca i tag" list_access_denied = "Non hai il permesso di visualizzare questa lista." @@ -649,9 +682,12 @@ New Item Feed = "Feed dei nuovi documenti" New Item Search = "Trova nuovi documenti" New Item Search Results = "Nuovi documenti trovati" New Items = "Nuovi documenti" +New results found for search = "Nuovi risultati per la ricerca" New Title = "Nuovo titolo" +new_email_success = "Il tuo indirizzo E-mail è stato cambiato con successo" new_password = "Nuova Password" new_password_success = "La tua password è stata cambiata con successo" +new_results_heading = "%%count%% risultati più recenti" new_user_welcome_subject = "Il tuo nuovo account presso la %%library%%" new_user_welcome_text = "Benvenuto in %%library%%. Un nuovo account è stato attivato per %%firstname%% %%lastname%%. Il tuo username è %%username%%. Per favore definisci una password in questa pagina: %%url%%" Newspaper = "Quotidiano" @@ -666,6 +702,7 @@ No Preference = "Nessuna preferenza" No reviews were found for this record = "Nessuna recensione è stata trovata per questo record" No Tags = "Nessun Tag" no_description = "Descrizione non disponibile." +no_email_address = "Indirizzo E-mail mancante." no_items_selected = "Non hai selezionato alcun elemento" nohit_active_filters = "Hai applicato uno o più filtri a questa ricerca. Se li rimuovi, otterrai un maggior numero di risultati." nohit_change_tab = "Stai cercando in "%%activeTab%%". Potresti trovare ulteriori risultati cercando in:" @@ -717,6 +754,63 @@ number_decimal_point = "," number_thousands_separator = "." OAI Server = "OAI Server" Occupation = "Occupazione" +od_account_noaccess = "La carta della biblioteca non ha accesso ai contenuti in Overdrive" +od_account_problem = "C'è un problema con il tuo account. %%message%%" +od_audiobook-mp3 = "Audiolibro MP3" +od_audiobook-overdrive = "Audiolibro OverDrive Listen" +od_avail_avail = "Disponibile:" +od_avail_holds = "Posseduto:" +od_avail_total = "Copie totali:" +od_but_cancel_hold = "Cancella questo posseduto" +od_but_checkout = "Checkout tramite Overdrive" +od_but_checkout_s = "Checkout" +od_but_gettitle = "Scarica questo contenuto" +od_but_gettitle_s = "Download" +od_but_hold = "Inserisci posseduto attraverso Overdrive" +od_but_hold_s = "Inserisci posseduto" +od_but_return = "Restituisci questo titolo" +od_cancel_hold = "Cancella una copia Overdrive" +od_checkout = "Checkout Overdrive" +od_code_connection_failed = "Connessione a Overdrive fallita. Se il problema persiste, contattare la propria biblioteca." +od_code_contentnotavail = "Questo contenuto non è disponibile nella tua area." +od_code_login_for_avail = "Accedi per controllare la disponibilità " +od_code_resource_not_found = "Titolo non trovato" +od_content = "Contenuto Overdrive" +od_dl_formats = "Formati per il download supportati" +od_docheckout_failure = "Questo titolo non può essere rilasciato." +od_docheckout_success = "Questo titolo è stato rilasciato a te. Scade il %%expireDate%%" +od_early_return = "Restituzione anticipata Overdrive" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "Questo titolo scade il %%due_date%%." +od_get_title = "Download Overdrive" +od_gettitle_failure = "Questo titolo non può essere scaricato." +od_help_linktext = "Aiuto Overdrive" +od_history = "Cronologia Overdrive" +od_hold = "Posseduto Overdrive" +od_hold_cancel_failure = "La richiesta di cancellazione del posseduto è fallita. " +od_hold_cancel_success = "Il posseduto è stato cancellato con successo." +od_hold_email = "Indirizzo email per la notifica del posseduto: %%holdEmailAddress%%." +od_hold_now_avail = "Questo posseduto è disponibile per il rilascio. L'operazione di rilascio scade il %%expireDate%%." +od_hold_place_failure = "La riquesta di posseduto è fallita." +od_hold_place_success = "Questo titolo è stato inserito nel posseduto. La posizione del tuo posseduto è %%holdListPosition%%" +od_hold_placed_on = "Posseduto inserito il %%holdPlacedDate%%." +od_hold_queue = "Posizione %%holdPosition%% di %%numberOfHolds%% nella code dei posseduti." +od_holds = "Posseduto Overdrive" +od_info_unavail = "Questa informazione è attualmente non disponibile." +od_is_checkedout = "Hai questo titolo in rilascio. La restituzione è il %%due_date%%." +od_is_on_hold = "Hai questo titolo come posseduto." +od_loans = "Prestiti Overdrive" +od_mycontent_help = "Per informazioni e aiuto relativi allo scaricamento di questi titoli, vedi <a href="%%url%%">Aiuto Overdrive</a>." +od_none_found = "Nessun titolo trovato." +od_return_failure = "Questo titolo non può essere restituito." +od_return_success = "Questo titolo è stato restituito." +od_video-streaming = "streaming file video" of_num_results = "#%%position%% di %%total%% risultati" old_password = "Vecchia Password" On Reserve = "Riservato" @@ -740,6 +834,7 @@ Page not found. = "Pagina non trovata." Password = "Password" Password Again = "Ripeti password" Password cannot be blank = "Il campo password non può essere vuoto" +password_error_auth_old = "La password usata non è valida" password_error_invalid = "La nuova password non è valida (contiene caratteri non validi)" password_error_not_unique = "La password non è stata cambiata" password_maximum_length = "La lunghezza massima della password è di %%maxlength%% caratteri" @@ -804,6 +899,7 @@ Read the full review online... = "Leggi tutta la recensione online..." Recall This = "Prenota" recaptcha_not_passed = "CAPTCHA non corretto" recently_returned_channel_title = "Rientrato di recente" +recommend_links_text = "Puoi provare anche:" Record Citations = "Citazioni del record" Record Count = "Conteggio dei record" Record Type = "Tipo record" @@ -822,6 +918,7 @@ recovery_title = "Rigenera la password" recovery_too_soon = "Sono state fatte troppe richieste; riprova più tardi" recovery_user_not_found = "Non riusciamo a trovare il tuo account" rectangle_center_message = "Questo è il centro per il rettangolo evidenziato" +Reference Material = "Materiale di riferimento" Refine Results = "Raffina i risultati" Region = "Regione" relais_available = "Questa copia è disponibile attraverso il servizio ILL. Vuoi richiederla?" @@ -844,9 +941,11 @@ renew_error = "Non possiamo effettuare il rinnovo. Contatta la biblioteca per ch renew_fail = "Richiesta non rinnovabile" renew_item = "Rinnova" renew_item_due = "Da restituire entro le prossime 24 ore" +renew_item_due_tooltip = "Oggetti in scadenza" renew_item_limit = "Raggiunto il limite di rinnovi" renew_item_no = "Non rinnovabile" renew_item_overdue = "Scaduto" +renew_item_overdue_tooltip = "Oggetti scaduti" renew_item_requested = "Richiesto da un altro utente" renew_select_box = "Rinnova" renew_selected = "Rinnova i selezionati" @@ -860,6 +959,7 @@ Requests = "Richieste" Reserves = "Reserve" Reserves Search = "Reserves Search" Reserves Search Results = "Reserves Search Results" +reset_filters_button = "Reset dei filtri" result_checkbox_label = "Seleziona il risultato numero %%number%%" result_count = "%%count%% risultati" Results = "Resultati" @@ -875,6 +975,11 @@ Save Comment = "Salva commento" save_search = "Salva la ricerca" save_search_remove = "Rimuovi la ricerca salvata" Saved in = "Salvato in" +schedule_daily = "Giornaliera" +schedule_explanation = "Ricevi notifiche via E-mail in caso di nuovi risultati per questa ricerca." +schedule_none = "Nessuna" +schedule_weekly = "Settimanale" +Scheduled Alert Results = "Notifiche risultati pianificate" scholarly_limit = "Limita agli articoli di periodici accademici" Scroll to Load More = "Scorri per visualizzarne ancora" Search = "Ricerca" @@ -889,12 +994,14 @@ search results of = "ricerca risultati per" Search Tips = "Suggerimenti per la ricerca" Search Tools = "Strumenti per la ricerca" Search Type = "Tipo di ricerca" +Search within collection = "Cerca all'interno della collezione" search_AND = "TUTTI i termini" search_groups = "Gruppi di ricerca" search_match = "Contiene" search_NOT = "NESSUN termine" search_OR = "QUALSIASI termine" search_save_success = "Ricerca salvata con successo." +search_terms = "Cerca tra i termini" search_unsave_success = "Ricerca salvata rimossa con successo." seconds_abbrev = "s" see all = "vedi tutto" @@ -913,6 +1020,7 @@ Sensor Image = "Sensor Image" Serial = "Periodico" Series = "Serie" Set = "Set" +show_filters_html = "Mostra i filtri (%%count%%)" showing_items_html = "Mostra <strong>%%start%% - %%end%%</strong> Elementi" showing_items_of_html = "Mostra <strong>%%start%% - %%end%%</strong> di <strong>%%total%%</strong> elementi" showing_results_for_html = "Mostra <strong>%%start%% - %%end%%</strong> risultati ricerca '<strong>%%lookfor%%</strong>'" @@ -960,6 +1068,7 @@ Start a new Basic Search = "Inizia una nuova ricerca base" Start Page = "Pagina iniziale" starting from = "parti da" Status = "Status" +status_transit = "In transito" status_unknown_message = "Status in tempo reale non disponibile" Storage Retrieval Requests = "Richiesta di volumi da magazzino" storage_retrieval_request_available = "Pronto per il ritiro" @@ -1035,6 +1144,7 @@ The record you selected is not part of any of your lists. = "Il record seleziona The record you selected is not part of the selected list. = "Il record selezionato non fa parte della lista selezionata." The system is currently unavailable due to system maintenance = "Il sistema è attualmente fuori uso a causa di lavori di manutenzione" Theme = "Tema" +Thesis = "Tesi" This email was sent from = "Questa email è stata inviata da" This field is required = "Questo campo è obbligatorio" This item is already part of the following list/lists = "Questo documento fa già parte della seguente lista/liste" @@ -1065,6 +1175,9 @@ unique_tags = "Tag unici" University Library = "Biblioteca Universitaria" Unknown = "Sconosciuto" unrecognized_facet_label = "Altro" +unsubscribe_confirmation = "Vuoi cancellare la sottoscrizione per tua E-mail?" +unsubscribe_description = "Non vuoi ricevere più questo messaggio in futuro? Cancella la sottoscrizione utilizzando il seguente link" +unsubscribe_successful = "Sottoscrizione cancellata" Upgrade VuFind = "Aggiorna VuFind" upgrade_description = "Se stai aggiornando una versione di VuFind precedente, puoi caricare le tue vecchie impostazioni con questo tool." URL = "URL" @@ -1074,11 +1187,20 @@ User Account = "Account utente" Username = "Username" Username cannot be blank = "Il campo username non può essere lasciato vuoto" Username is already in use in another library card = "Il nome utente è già usato in un'altra tessera della biblioteca" +verification_done = "Il tuo indirizzo E-mail è stato verificato con successo." +verification_email_change_sent = "Le istruzioni per la verifica dell'indirizzo E-mail sono state inviate a un nuovo indirizzo E-mail. Devi convalidare l'indirizzo prima che questo cambiamento sia effettivo." +verification_email_notification = "È stata appena fatta una richiesta di verifica del tuo indirizzo E-mail per il tuo account presso %%library%%." +verification_email_sent = "Le istruzioni per la verifica del tuo indirizzo E-mail sono state mandate all'indirizzo E-mail registrato con questo account." +verification_email_subject = "Verifica E-mail di VuFind" +verification_email_url_pretext = "Puoi verificare il tuo indirizzo E-mail a questo URL: %%url%%" +verification_too_soon = "La tua E-mail richiede di essere convalidata. Una E-mail è stata inviata recentemente al tuo indirizzo E-mail registrato. Se non l'hai ricevuta, attendi alcuni minuti e riprova di nuova." +verification_user_not_found = "Non riusciamo a trovare il tuo account" VHS = "VHS" Video = "Video" Video Clips = "Video" Videos = "Video" View Book Bag = "Vedi il carrello" +View Complete Issue = "Visualizza la Issue completa" View Full Collection = "Vedi la collezione completa" View Full Record = "Vedi il record completo" View in EDS = "Vedi in EDS" diff --git a/languages/ja.ini b/languages/ja.ini index 04276f9db9f725e77e68242b60da46f12f71846d..f66415934a02ee7e3de3cc32ff18d92b824827cb 100644 --- a/languages/ja.ini +++ b/languages/ja.ini @@ -51,6 +51,7 @@ advSearchError_notFound = "指定ã—ãŸæ¤œç´¢ã¯ã‚ã‚Šã¾ã›ã‚“。" ajax_load_interrupted = "ãƒãƒ¼ãƒ‰ãŒä¸æ–ã•ã‚Œã¾ã—ãŸ" ajaxview_label_information = "æƒ…å ±" ajaxview_label_tools = "ツール" +alert_email_address = "定期通知メールãŒæŒ‡å®šã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«é€ä¿¡ã•ã‚Œã¾ã™ã€‚" All = "ã™ã¹ã¦" All Fields = "全フィールド" All Pages Loaded = "全ページãƒãƒ¼ãƒ‰ã•ã‚Œã¾ã—ãŸ" @@ -60,6 +61,7 @@ alphabrowselink_html = "%%index%%ã‚’<a href="%%url%%">%%from%%</a>ã‹ã‚‰ãƒ–ラ An error has occurred = "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ" An error occurred during execution; please try again later. = "処ç†ä¸ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚時間をãŠã„ã¦å†åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。" AND = "AND" +and = ", " anonymous_tags = "匿åã‚¿ã‚°" APA Citation = "APA引用形å¼" applied_filter = "é©ç”¨ã•ã‚ŒãŸãƒ•ã‚£ãƒ«ã‚¿ãƒ¼" @@ -72,6 +74,8 @@ authentication_error_admin = "ç¾åœ¨ã€ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“。システ authentication_error_blank = "ãƒã‚°ã‚¤ãƒ³æƒ…å ±ã‚’å…¥åŠ›ã—ã¦ãã ã•ã„。" authentication_error_creation_blocked = "アカウントを作æˆã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。" authentication_error_denied = "クレデンシャルãŒä¸€è‡´ã—ã¾ã›ã‚“。アクセスã§ãã¾ã›ã‚“。" +authentication_error_email_not_verified_html = "メールアドレスã®ç¢ºèªãŒã•ã‚Œã¦ã„ã¾ã›ã‚“。確èªãƒ¡ãƒ¼ãƒ«ãŒå±Šã„ã¦ã„ãªã„å ´åˆã¯ã€ä½¿ç”¨ã•ã‚Œã¦ã„るメーラーã®ã‚¹ãƒ‘ムフィルターをãƒã‚§ãƒƒã‚¯ã—ã¦ãã ã•ã„。必è¦ã§ã‚ã‚Œã°<a href="%%url%%">確èªãƒ¡ãƒ¼ãƒ«ã‚’å†é€</a>ã—ã¾ã™ã®ã§ã€ã”連絡ãã ã•ã„。" +authentication_error_in_progress = "èªè¨¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ã™ã§ã«å‡¦ç†ã‚’開始ã—ã¦ã„ã¾ã™ã€‚ã‚„ã‚Šç›´ã—ãŸã„å ´åˆã¯ã€å°‘ã—時間を置ã„ã¦ã‹ã‚‰å†è©¦è¡Œã—ã¦ãã ã•ã„。" authentication_error_invalid = "ä¸æ£ãªãƒã‚°ã‚¤ãƒ³ã§ã™ã€‚ã‚‚ã†ä¸€åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。" authentication_error_loggedout = "ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¦ã„ã¾ã™ã€‚" authentication_error_technical = "ç¾åœ¨ã€ãƒã‚°ã‚¤ãƒ³ã§ãã¾ã›ã‚“。少ã—経ã£ã¦ã‹ã‚‰å®Ÿè¡Œã—ã¦ãã ã•ã„。" @@ -97,7 +101,6 @@ Backtrace = "ãƒãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹" Bag = "カート" Balance = "残高" Barcode = "ãƒãƒ¼ã‚³ãƒ¼ãƒ‰" -basic_search_keep_filters = "ç¾åœ¨ã®çµžè¾¼ã¿ã‚’ä¿æŒã™ã‚‹" Be the first to leave a comment = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¸ã®åˆã‚ã¦ã®ã‚³ãƒ¡ãƒ³ãƒˆã‚’付ã‘ã¾ã›ã‚“ã‹" Be the first to tag this record = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã¸ã®åˆã‚ã¦ã®ã‚¿ã‚°ã‚’付ã‘ã¾ã›ã‚“ã‹" Bibliographic Details = "書誌詳細" @@ -167,6 +170,7 @@ Call Number = "請求記å·" callnumber_abbrev = "請求記å·" Cannot find record = "該当ã™ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" Cannot find similar records = "åŒæ§˜ãªãƒ¬ã‚³ãƒ¼ãƒ‰ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸ" +cannot set = "è¨å®šä¸å¯" Cassette = "カセット" cat_establish_account = "次ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆæƒ…å ±ã‚’å…¥åŠ›ã—ã¦ãã ã•ã„:" cat_password_abbrev = "パスワード" @@ -175,7 +179,12 @@ Catalog Login = "目録システムã®ãƒã‚°ã‚¤ãƒ³" Catalog Results = "目録検索çµæžœ" catalog_login_desc = "図書館目録システムã®ã‚¯ãƒ¬ãƒ‡ãƒ³ã‚·ãƒ£ãƒ«ã‚’入力ã—ã¦ãã ã•ã„。" CD = "CD" +Change Email Address = "メールアドレスを変更" Change Password = "パスワードã®å¤‰æ›´" +change_email_disabled = "ç¾åœ¨ã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã®å¤‰æ›´ã¯ã§ãã¾ã›ã‚“。" +change_email_verification_reminder = "ã“ã®ãƒ•ã‚©ãƒ¼ãƒ ã‚’é€ä¿¡ã™ã‚‹ã¨æ–°ã—ã„アドレスã«ãƒ¡ãƒ¼ãƒ«ãŒé€ä¿¡ã•ã‚Œã¾ã™ã€‚ãã®ãƒ¡ãƒ¼ãƒ«ã«ã‚るリンクをクリックã™ã‚‹ã¨å¤‰æ›´ãŒæœ‰åŠ¹ã«ãªã‚Šãªã‚Šã¾ã™ã€‚" +change_notification_email_message = "%%library%%ã«ç™»éŒ²ã•ã‚ŒãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã®å¤‰æ›´ä¾é ¼ãŒã‚ã‚Šã¾ã—ãŸã€‚心当ãŸã‚ŠãŒãªã„å ´åˆã¯ã€%%url%% ã«ãƒã‚°ã‚¤ãƒ³ã—ã¦å•é¡ŒãŒãªã„ã‹ç¢ºèªã—ã¦ãã ã•ã„。疑å•ã‚„質å•ãŒã‚ã‚Šã¾ã—ãŸã‚‰ã€%%email%% ã¾ã§ã”連絡ãã ã•ã„。" +change_notification_email_subject = "メールアドレス変更通知" channel_add_more = "ãƒãƒ£ãƒãƒ«ã‚’è¿½åŠ " channel_browse = "ã•ã‚‰ã«è¡¨ç¤º" channel_expand = "関連ãƒãƒ£ãƒãƒ«ã‚’検索" @@ -184,6 +193,7 @@ channel_search = "検索çµæžœã¨ã—ã¦è¡¨ç¤º" channel_searchbox_label = "ãƒãƒ£ãƒãƒ«ã‚’検索:" Check Hold = "予約確èª" Check Recall = "è¿”å´è«‹æ±‚確èª" +check_profile = "åˆ©ç”¨è€…æƒ…å ±ã®ãƒã‚§ãƒƒã‚¯" Checked Out = "貸出ä¸" Checked Out Items = "貸出ä¸è³‡æ–™" Checkedout = "貸出ä¸" @@ -217,6 +227,7 @@ comment_error_load = "エラー: コメントリストをå†è¡¨ç¤ºã§ãã¾ã› comment_error_save = "エラー: コメントをä¿å˜ã§ãã¾ã›ã‚“ã§ã—ãŸ" Comments = "コメント" Company/Entity = "会社/法人" +Conference Proceeding = "会è°éŒ²" Configuration = "è¨å®š" confirm_delete = "ã“れを本当ã«å‰Šé™¤ã—ã¦ã‚‚良ã„ã§ã™ã‹?" confirm_delete_brief = "アイテムを削除ã—ã¾ã™ã‹?" @@ -297,6 +308,7 @@ Due Date = "è¿”å´æ—¥" DVD = "DVD" eBook = "eBook" Edit = "編集" +edit = "編集" Edit Library Card = "図書館カードã®ç·¨é›†" Edit this Advanced Search = "詳細検索を編集" edit_list = "リストã®ç·¨é›†" @@ -325,8 +337,14 @@ Email address is invalid = "メールアドレスãŒä¸æ£ã§ã™" Email Record = "レコードをメール" Email this = "ã“ã®è³‡æ–™ã‚’メール" Email this Search = "検索çµæžœã‚’メール" +email_change_pending_html = "メールアドレスã®å¤‰æ›´ãŒä¿ç•™ã•ã‚Œã¦ã„ã¾ã™ï¼ˆ%%pending%%)。確èªãƒ¡ãƒ¼ãƒ«ã«æ›¸ã‹ã‚ŒãŸãƒªãƒ³ã‚¯ã‚’クリックã—ã¦å¤‰æ›´ã‚’完了ã—ã¦ãã ã•ã„。必è¦ã§ã‚ã‚Œã°<a href="%%url%%">確èªãƒ¡ãƒ¼ãƒ«ã‚’å†é€</a>ã„ãŸã—ã¾ã™ã€‚" email_failure = "エラー - メッセージをé€ä¿¡ã§ãã¾ã›ã‚“ã§ã—ãŸ" email_link = "リンク" +email_login_desc = "ãƒã‚°ã‚¤ãƒ³ã«ã¯æ¬¡ã®ãƒªãƒ³ã‚¯ã‚’ã”使用ãã ã•ã„。心当ãŸã‚ŠãŒãªã„å ´åˆã¯ãã®ã¾ã¾æ”¾ç½®ã—ã¦ã‚‚å•é¡Œã‚ã‚Šã¾ã›ã‚“。ãªãŠã€ã“ã®ãƒªãƒ³ã‚¯ã«ã¯æœ‰åŠ¹æœŸé™ãŒã‚ã‚Šã¾ã™ã€‚ã¾ãŸã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力ã—ãŸãƒ‡ãƒã‚¤ã‚¹ã§ã—ã‹ä½¿ç”¨ã§ããªã„ã“ã¨ã«ã”注æ„ãã ã•ã„。" +email_login_link = "ãƒã‚°ã‚¤ãƒ³ãƒªãƒ³ã‚¯: <%%url%%>" +email_login_link_sent = "指定ã•ã‚ŒãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ãƒã‚°ã‚¤ãƒ³ãƒªãƒ³ã‚¯ã‚’é€ä¿¡ã—ã¾ã—ãŸã€‚メールãŒå±Šãã¾ã§å°‘ã—時間ãŒã‹ã‹ã‚‹å ´åˆãŒã‚ã‚Šã¾ã™ã€‚ãªãŠã€ã—ã°ã‚‰ãå¾…ã£ã¦ã‚‚届ã‹ãªã„å ´åˆã¯ã€ã‚¹ãƒ‘ムフィルタをãƒã‚§ãƒƒã‚¯ã—ã¦ã¿ã¦ãã ã•ã„。" +email_login_requested = "メールアドレスã‹ã‚‰%%title%%ã¸ã®ãƒã‚°ã‚¤ãƒ³ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’å—ã‘付ã‘ã¾ã—ãŸã€‚" +email_login_subject = "%%title%%ã¸ãƒã‚°ã‚¤ãƒ³" email_maximum_recipients_note = "最大%%max%%人ã¾ã§å—信者を指定ã§ãã¾ã™ã€‚" email_multiple_recipients_note = "複数ã®å—信者をコンマ区切りã§æŒ‡å®šã§ãã¾ã™ã€‚" email_selected = "é¸æŠžã‚¢ã‚¤ãƒ†ãƒ をメールé€ä¿¡" @@ -336,6 +354,7 @@ email_subject = "主題" email_success = "メッセージをé€ä¿¡ã—ã¾ã—ãŸ" Empty = "空" Empty Book Bag = "図書ãƒãƒƒã‚¯ã‚’空ã«ã™ã‚‹" +empty_search_disallowed = "ã“ã®æ¤œç´¢å¯¾è±¡ã§ã¯ç©ºã®ã‚¯ã‚¨ãƒªã¯ç¦æ¢ã•ã‚Œã¦ã„ã¾ã™ã€‚" Enable Auto Config = "自動è¨å®šæ©Ÿèƒ½ã®æœ‰åŠ¹åŒ–" End Page = "終了ページ" Era = "時代区分" @@ -388,12 +407,13 @@ Favorites = "ãŠæ°—ã«å…¥ã‚Š" Fee = "料金" Feedback = "ã”æ„見" feedback_email = "メール" -feedback_login_required = "ã“ã®æ“作ã«ã¯ãƒã‚°ã‚¤ãƒ³ãŒå¿…è¦ã§ã™" feedback_name = "ãŠåå‰" Field of activity = "アクテイビティã®ãƒ•ã‚¤ãƒ¼ãƒ«ãƒ‰" File Description = "ファイル記述" Filter = "フィルター" +Filter Collection = "コレクションã®çµžã‚Šè¾¼ã¿" filter_tags = "ã‚¿ã‚°ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒªãƒ³ã‚°" +filter_toggle_entries = "%%count%% フィルター" filter_wildcard = "ä»»æ„" Find = "検索" Find More = "ãã®ä»–ã®æ¤œç´¢" @@ -429,6 +449,7 @@ Go to Standard View = "標準画é¢ã¸ç§»è¡Œ" go_to_list = "リストã¸é£›ã¶" google_map_cluster = "クラスタ" google_map_cluster_points = "クラスター・ãƒã‚¤ãƒ³ãƒˆ" +Government Document = "政府資料" Grid = "グリッド" Group = "グループ" group_AND = "è«–ç†ç©ï¼ˆAND)" @@ -455,6 +476,7 @@ history_results = "çµæžœ" history_save = "ä¿å˜?" history_save_link = "ä¿å˜" history_saved_searches = "ä¿å˜æ¸ˆã®æ¤œç´¢" +history_schedule = "通知スケジュール" history_search = "検索" history_time = "日時" hold_available = "予約å¯èƒ½" @@ -601,6 +623,7 @@ Library Web Search = "図書館ã®Web検索" library_card_edit_password_placeholder = "新パスワード" lightbox_error = "エラー: ãƒãƒƒãƒ—アップボックスをãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“" Limit To = "絞込ã¿" +Link to full results = "詳細çµæžœã¸ã®ãƒªãƒ³ã‚¯" List = "リスト" List Tags = "ã‚¿ã‚°ã®ä¸€è¦§" list_access_denied = "ã“ã®ãƒªã‚¹ãƒˆã®é–²è¦§ã‚’許ã•ã‚Œã¦ã„ã¾ã›ã‚“。" @@ -663,9 +686,12 @@ New Item Feed = "æ–°ç€è³‡æ–™ã®ãƒ•ã‚£ãƒ¼ãƒ‰" New Item Search = "æ–°ç€è³‡æ–™ã®æ¤œç´¢" New Item Search Results = "æ–°ç€è³‡æ–™ã®æ¤œç´¢çµæžœ" New Items = "æ–°ç€è³‡æ–™" +New results found for search = "æ–°ãŸãªæ¤œç´¢çµæžœãŒã‚ã‚Šã¾ã—ãŸ" New Title = "æ–°ç€è³‡æ–™" +new_email_success = "メールアドレスãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸ" new_password = "新パスワード" new_password_success = "パスワードãŒå¤‰æ›´ã•ã‚Œã¾ã—ãŸã€‚" +new_results_heading = "%%count%% 件ã®æœ€æ–°çµæžœ" new_user_welcome_subject = "%%library%%ã«ãŠã‘ã‚‹æ–°ã—ã„アカウント" new_user_welcome_text = "%%library%%ã«ã‚ˆã†ã“ã。%%lastname%%%%firstname%%ã•ã‚“ã®æ–°ã—ã„アカウントを作æˆã—ã¾ã—ãŸã€‚ユーザåã¯%%username%%ã§ã™ã€‚%%url%%ã§ãƒ‘スワードをè¨å®šã—ã¦ãã ã•ã„。" Newspaper = "æ–°èž" @@ -681,6 +707,7 @@ No Preference = "å•ã‚ãªã„" No reviews were found for this record = "ã“ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã«æ›¸è©•ã¯ã‚ã‚Šã¾ã›ã‚“。" No Tags = "ã‚¿ã‚°ãªã—" no_description = "記述ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。" +no_email_address = "メールアドレスãŒæŒ‡å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。" no_items_selected = "アイテムãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“" nohit_active_filters = "一ã¤ä»¥ä¸Šã®ãƒ•ã‚¡ãƒƒã‚»ãƒƒãƒˆãƒ»ãƒ•ã‚¤ãƒ«ã‚¿ãƒ¼ãŒã“ã®æ¤œç´¢ã«é©ç”¨ã•ã‚Œã¾ã™.ã‚‚ã—フィルターを削除ã™ã‚‹ã¨, より多ãã®çµæžœãŒå¾—られるã‹ã‚‚ã—ã‚Œã¾ã›ã‚“。" nohit_change_tab = "%%activeTab%%を検索ã—ã¦ã„ã¾ã™ã€‚ä»–ã®ã‚¿ãƒ–ã®ã²ã¨ã¤ã§ä½•ã‹è¦‹ã¤ã‹ã‚‹ã‹ã‚‚知れã¾ã›ã‚“。" @@ -732,6 +759,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "OAIサーãƒãƒ¼" Occupation = "è·æ¥" +od_account_noaccess = "ã“ã®å›³æ›¸ã‚«ãƒ¼ãƒ‰ã§ã¯Overdriveを利用ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“。" +od_account_problem = "アカウントã«æ¬¡ã®å•é¡ŒãŒã‚ã‚Šã¾ã—ãŸ: %%message%%" +od_audiobook-mp3 = "MP3 オーディオブック" +od_audiobook-overdrive = "OverDrive Listen オーディオブック" +od_avail_avail = "貸出å¯èƒ½æ•°:" +od_avail_holds = "予約数:" +od_avail_total = "所蔵ç·æ•°:" +od_but_cancel_hold = "ã“ã®äºˆç´„ã‚’å–り消ã™" +od_but_checkout = "Overdriveã‹ã‚‰è²¸ã—出ã™" +od_but_checkout_s = "貸出" +od_but_gettitle = "ã“ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„をダウンãƒãƒ¼ãƒ‰" +od_but_gettitle_s = "ダウンãƒãƒ¼ãƒ‰" +od_but_hold = "Overdriveã§äºˆç´„ã™ã‚‹" +od_but_hold_s = "予約" +od_but_return = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã‚’è¿”å´ã™ã‚‹" +od_cancel_hold = "Overdriveã§ã®äºˆç´„ã‚’å–り消ã™" +od_checkout = "Overdrive 貸出手続ã" +od_code_connection_failed = "Overdriveã¸ã®æŽ¥ç¶šã«å¤±æ•—ã—ã¾ã—ãŸã€‚å†ç¾ã™ã‚‹ã‚ˆã†ã§ã—ãŸã‚‰å›³æ›¸é¤¨ã«ã”相談ãã ã•ã„。" +od_code_contentnotavail = "ã“ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“。" +od_code_login_for_avail = "利用ã«ã¯ãƒã‚°ã‚¤ãƒ³ãŒå¿…è¦ã§ã™" +od_code_resource_not_found = "該当ã™ã‚‹ã‚¿ã‚¤ãƒˆãƒ«ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸ" +od_content = "Overdrive コンテンツ" +od_dl_formats = "利用å¯èƒ½ãªãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆ" +od_docheckout_failure = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯è²¸å‡ºã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +od_docheckout_success = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã‚’ã‚ãªãŸã«è²¸ã—出ã—ã¾ã—ãŸã€‚利用期é™ã¯ % %expireDate%% ã§ã™ã€‚" +od_early_return = "Overdrive 早期返å´" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã®åˆ©ç”¨æœŸé™ã¯ %%due_date%% ã§ã™ã€‚" +od_get_title = "Overdrive ダウンãƒãƒ¼ãƒ‰" +od_gettitle_failure = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +od_help_linktext = "Overdrive ヘルプ" +od_history = "Overdrive å±¥æ´" +od_hold = "Overdrive 予約" +od_hold_cancel_failure = "予約ã®å–り消ã—ãŒã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +od_hold_cancel_success = "予約をå–り消ã—ã¾ã—ãŸã€‚" +od_hold_email = "予約案内通知ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹: %%holdEmailAddress%%." +od_hold_now_avail = "予約ã•ã‚Œã¦ã„ãŸã‚¢ã‚¤ãƒ†ãƒ ãŒè²¸å‡ºå¯èƒ½ã«ãªã‚Šã¾ã—ãŸã€‚%%expireDate%% ã¾ã§ã«è²¸å‡ºæ‰‹ç¶šãã‚’ã—ã¦ãã ã•ã„。" +od_hold_place_failure = "予約ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +od_hold_place_success = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯äºˆç´„済ã¿ã§ã™ã€‚äºˆç´„é †ä½ã¯ %%holdListPosition%% ã§ã™ã€‚" +od_hold_placed_on = "%%holdPlacedDate%% ã«äºˆç´„ã•ã‚Œã¾ã—ãŸã€‚" +od_hold_queue = "予約者数 %%numberOfHolds% 人ã®ã†ã¡ã€%%holdPosition%% 番目ã§ã™ã€‚" +od_holds = "Overdrive 予約" +od_info_unavail = "ã“ã®æƒ…å ±ã¯ç¾åœ¨åˆ©ç”¨ã§ãã¾ã›ã‚“。" +od_is_checkedout = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ã‚ãªãŸã«è²¸å‡ºä¸ã§ã™ã€‚利用期é™ã¯ %%due_date%% ã§ã™ã€‚" +od_is_on_hold = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯ã™ã§ã«äºˆç´„済ã¿ã§ã™ã€‚" +od_loans = "Overdrive 貸出" +od_mycontent_help = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã®ãƒ€ã‚¦ãƒ³ãƒãƒ¼ãƒ‰ã«é–¢ã™ã‚‹æƒ…å ±ã¯<a href="%%url%%">Overdrive ヘルプ</a>ã‚’ã”覧ãã ã•ã„。" +od_none_found = "該当ã™ã‚‹ã‚¿ã‚¤ãƒˆãƒ«ã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" +od_return_failure = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯è¿”å´ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚" +od_return_success = "ã“ã®ã‚¿ã‚¤ãƒˆãƒ«ã¯è¿”å´ã•ã‚Œã¾ã—ãŸã€‚" +od_video-streaming = "ストリーミングビデオファイル" of_num_results = "#%%position%% / %%total%% çµæžœ" old_password = "旧パスワード" On Reserve = "予約ã®æœ‰ç„¡" @@ -761,6 +845,7 @@ pagination_label = "ページ" Password = "パスワード" Password Again = "ã‚‚ã†ä¸€åº¦ãƒ‘スワード" Password cannot be blank = "パスワードを入力ã—ã¦ãã ã•ã„" +password_error_auth_old = "入力ã•ã‚ŒãŸãƒ‘スワードã¯ä¸æ£ã§ã™ã€‚" password_error_invalid = "新パスワードã¯ä¸æ£ã§ã™ï¼ˆä¸æ£ãªæ–‡å—ãŒå«ã¾ã‚Œã¦ã„ã‚‹ãªã©ï¼‰" password_error_not_unique = "パスワードã¯å¤‰æ›´ã•ã‚Œã¾ã›ã‚“ã§ã—ãŸ" password_maximum_length = "パスワードã¯æœ€å¤§%%maxlength%%æ–‡å—ã¾ã§ã§ã™ã€‚" @@ -826,6 +911,7 @@ Read the full review online... = "書評全文をオンラインã§é–²è¦§..." Recall This = "è¿”å´è«‹æ±‚ã™ã‚‹" recaptcha_not_passed = "CAPTCHAãŒä¸€è‡´ã—ã¾ã›ã‚“" recently_returned_channel_title = "最新ã®è¿”å´" +recommend_links_text = "関連リンク:" Record Citations = "レコードã®å¼•ç”¨å½¢" Record Count = "レコード数" Record Type = "レコードã®ã‚¿ã‚¤ãƒ—" @@ -844,6 +930,7 @@ recovery_title = "パスワードã®å†è¨å®š" recovery_too_soon = "å†è¨å®šå›žæ•°ã®ä¸Šé™ã‚’超ãˆã¾ã—ãŸã€‚ã—ã°ã‚‰ã時間をãŠã„ã¦å†åº¦å®Ÿè¡Œã—ã¦ãã ã•ã„。" recovery_user_not_found = "指定ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å˜åœ¨ã—ã¾ã›ã‚“。" rectangle_center_message = "ã“ã‚Œã¯ãƒã‚¤ãƒ©ã‚¤ãƒˆã•ã‚ŒãŸå››è§’ã®ä¸å¿ƒç‚¹ã§ã™ã€‚" +Reference Material = "å‚考資料" Refine Results = "çµæžœã®çµžã‚Šè¾¼ã¿" Region = "地ç†åŒºåˆ†" relais_available = "ã“ã®ã‚¢ã‚¤ãƒ†ãƒ ã¯å›³æ›¸é¤¨é–“貸出を通ã˜ã¦åˆ©ç”¨å¯èƒ½ã§ã™ã€‚リクエストã—ã¾ã™ã‹?" @@ -884,6 +971,7 @@ Requests = "リクエスト" Reserves = "予約" Reserves Search = "予約資料検索" Reserves Search Results = "予約資料検索çµæžœ" +reset_filters_button = "フィルターã®ãƒªã‚»ãƒƒãƒˆ" result_checkbox_label = "çµæžœç•ªå·: %%number%%ã‚’é¸æŠž" result_count = "%%count%% çµæžœ" Results = "çµæžœ" @@ -899,6 +987,11 @@ Save Comment = "コメントã®ä¿å˜" save_search = "検索ã®ä¿å˜" save_search_remove = "ä¿å˜ã—ãŸæ¤œç´¢ã®å‰Šé™¤" Saved in = "ä¿å˜å…ˆ" +schedule_daily = "日次" +schedule_explanation = "æ–°ãŸãªæ¤œç´¢çµæžœã®é€šçŸ¥ãƒ¡ãƒ¼ãƒ«ã‚’å—ã‘å–る。" +schedule_none = "ãªã—" +schedule_weekly = "週次" +Scheduled Alert Results = "検索çµæžœå®šæœŸé€šçŸ¥" scholarly_limit = "å¦è¡“雑誌ã®è«–æ–‡ã«é™å®šã™ã‚‹" Scroll to Load More = "ã‚‚ã£ã¨è¦‹ã‚‹" Search = "検索" @@ -913,6 +1006,7 @@ search results of = "検索çµæžœ: " Search Tips = "検索方法" Search Tools = "検索ツール" Search Type = "検索種別" +Search within collection = "コレクション内ã§æ¤œç´¢" search_AND = "è«–ç†ç©ï¼ˆAND)" search_groups = "検索グループ" search_match = "è«–ç†æ¼”ç®—" @@ -938,6 +1032,7 @@ Sensor Image = "センサー画åƒ" Serial = "é€æ¬¡åˆŠè¡Œç‰©" Series = "シリーズ" Set = "セット" +show_filters_html = "フィルター表示 (%%count%%)" showing_items_html = "検索çµæžœ <strong>%%start%% - %%end%%</strong> アイテム" showing_items_of_html = "検索çµæžœ <strong>%%start%% - %%end%%</strong> / <strong>%%total%%</strong> アイテム" showing_results_for_html = "検索çµæžœ <strong>%%start%% - %%end%%</strong> çµæžœ 検索語 '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "基本検索をやり直ã™" Start Page = "開始ページ" starting from = "次ã®æ–‡å—ã‹ã‚‰ãƒ–ラウズを開始" Status = "状態" +status_transit = "é…é€ä¸" status_unknown_message = "ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹æƒ…å ±ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“" Storage Retrieval Requests = "所蔵検索リクエスト" storage_retrieval_request_available = "予約å¯èƒ½" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "é¸æŠžã—ãŸãƒ¬ã‚³ The record you selected is not part of the selected list. = "é¸æŠžã—ãŸãƒ¬ã‚³ãƒ¼ãƒ‰ã¯é¸æŠžãƒªã‚¹ãƒˆã«å«ã¾ã‚Œã¦ã„ã¾ã›ã‚“。" The system is currently unavailable due to system maintenance = "システムä¿å®ˆä½œæ¥ã®ãŸã‚ã€ç¾åœ¨ã‚·ã‚¹ãƒ†ãƒ ã¯åˆ©ç”¨ã§ãã¾ã›ã‚“" Theme = "テーマ" +Thesis = "å¦ä½è«–æ–‡" This email was sent from = "ã“ã®ãƒ¡ãƒ¼ãƒ«ã®é€ä¿¡å…ƒ" This field is required = "ã“ã®ãƒ•ã‚£ãƒ¼ãƒ«ãƒ‰ã¯å¿…é ˆã§ã™" This item is already part of the following list/lists = "ã“ã®è³‡æ–™ã¯æ—¢ã«ä»¥ä¸‹ã®ãƒªã‚¹ãƒˆã«å«ã¾ã‚Œã¦ã„ã¾ã™" @@ -1090,6 +1187,9 @@ unique_tags = "ユニークタグ" University Library = "大å¦å›³æ›¸é¤¨" Unknown = "ä¸æ˜Ž" unrecognized_facet_label = "ãã®ä»–" +unsubscribe_confirmation = "定期メールをã‚ャンセルã—ã¾ã™ã‹ã€‚" +unsubscribe_description = "今後ã€ã“ã®ãƒ¡ãƒ¼ãƒ«ã¯ä¸è¦ã§ã™ã‹ã€‚次ã®ãƒªãƒ³ã‚¯ã§ã‚ャンセルã§ãã¾ã™ã€‚" +unsubscribe_successful = "定期通知ã¯ã‚ャンセルã•ã‚Œã¾ã—ãŸ" Upgrade VuFind = "VuFindã®ã‚¢ãƒƒãƒ—グレード" upgrade_description = "VuFindã®ã‚¢ãƒƒãƒ—グレードを行ã†å ´åˆã€ã“ã®ãƒ„ールを使ã†ã¨ç¾è¡Œã®è¨å®šã‚’ãƒãƒ¼ãƒ‰ã§ãã¾ã™ã€‚" URL = "URL" @@ -1099,6 +1199,14 @@ User Account = "アカウント" Username = "ユーザå" Username cannot be blank = "ユーザåを入力ã—ã¦ãã ã•ã„" Username is already in use in another library card = "ã“ã®ãƒ¦ãƒ¼ã‚¶åã¯åˆ¥ã®å›³æ›¸ã‚«ãƒ¼ãƒ‰ã§ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™" +verification_done = "メールアドレスを確èªã—ã¾ã—ãŸã€‚" +verification_email_change_sent = "æ–°ã—ã„メールアドレスã«å¤‰æ›´ç¢ºèªã®æ–¹æ³•ã‚’書ã„ãŸãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡ã—ã¾ã—ãŸã€‚アドレスã®ç¢ºèªãŒãªã„ã¨å¤‰æ›´ãŒæœ‰åŠ¹ã«ãªã‚Šã¾ã›ã‚“ã®ã§ã”注æ„ãã ã•ã„。" +verification_email_notification = "%%library%% ã¸ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆç™»éŒ²ã«æŒ‡å®šã—ãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’確èªã™ã‚‹ãŸã‚ã®å‡¦ç†ã‚’開始ã—ã¾ã—ãŸã€‚" +verification_email_sent = "メールアドレスを確èªã™ã‚‹æ–¹æ³•ã‚’示ã—ãŸãƒ¡ãƒ¼ãƒ«ã‚’登録ã•ã‚ŒãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«é€ä¿¡ã—ã¾ã—ãŸã€‚" +verification_email_subject = "VuFind メールアドレス確èª" +verification_email_url_pretext = "次ã®URLã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’確èªã—ã¦ãã ã•ã„: %%url%%" +verification_too_soon = "メールアドレスã®ç¢ºèªãŒå¿…è¦ã§ã™ã€‚確èªæ–¹æ³•ã‚’示ã—ãŸãƒ¡ãƒ¼ãƒ«ã‚’登録ã•ã‚ŒãŸãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«é€ä»˜ã—ã¾ã—ãŸã€‚ã¾ã ã€å—ã‘å–ã£ã¦ã„ãªã„å ´åˆã¯ã€å°‘ã—時間をãŠã„ã¦ã€ã‚‚ã†ä¸€åº¦ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ãã ã•ã„。" +verification_user_not_found = "アカウントãŒç™»éŒ²ã•ã‚Œã¦ã„ã¾ã›ã‚“" VHS = "VHS" Video = "ビデオ" Video Clips = "ビデオクリップ" diff --git a/languages/native.ini b/languages/native.ini index 7c2f62e7fb51b567516ae98ba7a381e974dee132..5c4d03b17ec086f7cd0bf555106fc68995ee440a 100644 --- a/languages/native.ini +++ b/languages/native.ini @@ -5,7 +5,8 @@ Bengali = "বাংলা" Brazilian Portugese = "Português (Brasil)" Catalan = "Català " Chinese = "ä¸æ–‡ï¼ˆç¹é«”)" -Czech = "ÄeÅ¡tina" +Croatian = "Hrvatski" +Czech = "ÄŒeÅ¡tina" Danish = "Dansk" Dutch = "Nederlands" English = "English" @@ -16,6 +17,7 @@ Galician = "Galego" German = "Deutsch" Greek = "Ελληνικά" Hebrew = "עברית" +Hindi = "हिंदी" Irish = "Gaeilge" Italian = "Italiano" Japanese = "日本語" diff --git a/languages/nl.ini b/languages/nl.ini index 063ae974b5ff4c11d29ba84e5ec7151162fd15cf..9010cb563e4236c413ab569f068dc18a15534ab2 100644 --- a/languages/nl.ini +++ b/languages/nl.ini @@ -51,6 +51,7 @@ advSearchError_notFound = "De zoekopdracht die je vraagt, werd niet gevonden." ajax_load_interrupted = "Laden werd onderbroken" ajaxview_label_information = "Informatie" ajaxview_label_tools = "Tools" +alert_email_address = "Geplande resultaatmeldingen worden naar het e-mailadres verzonden" All = "Alle" All Fields = "Alle velden" All Pages Loaded = "Alle pagina's zijn ingeladen" @@ -60,6 +61,7 @@ alphabrowselink_html = "Doorblader de catalogus met %%index%% beginnend vanaf %% An error has occurred = "Er is een fout opgetreden" An error occurred during execution; please try again later. = "Er trad een fout op tijdens de uitvoering; probeer het later nog eens." AND = "AND" +and = "en" anonymous_tags = "Anonieme tags" APA Citation = "APA Citatie" applied_filter = "Toegepaste filter" @@ -72,6 +74,8 @@ authentication_error_admin = "We kunnen je nu niet inloggen. Contacteer jouw sys authentication_error_blank = "Login informatie moet worden ingevuld." authentication_error_creation_blocked = "Je hebt geen toestemming om een account aan te maken" authentication_error_denied = "De gegevens komen niet overeen! Toegang geweigerd." +authentication_error_email_not_verified_html = "Je e-mailadres is nog niet geverifieerd. Controleer je spam folder voor de verificatie mail. Indien nodig kunnen we <a href="%%url%%">de verificatie e-mail opnieuw versturen</a>." +authentication_error_in_progress = "Verificatieverzoek wordt al verwerkt. Probeer het later opnieuw als je opnieuw moet beginnen." authentication_error_invalid = "Ongeldige login -- Probeer het alsjeblieft opnieuw." authentication_error_loggedout = "Je bent afgemeld" authentication_error_technical = "We kunnen je nu niet inloggen. Probeer het later alsjeblieft nog eens." @@ -97,7 +101,6 @@ Backtrace = "Backtrace" Bag = "Tas" Balance = "Balans" Barcode = "Barcode" -basic_search_keep_filters = "Bewaar mijn huidige filters" Be the first to leave a comment = "Wees de eerste die reageert" Be the first to tag this record = "Wees de eerste die dit record labelt" Bibliographic Details = "Bibliografische gegevens" @@ -167,6 +170,7 @@ Call Number = "Plaatsingsnummer" callnumber_abbrev = "Plaatsingsnr." Cannot find record = "Record niet gevonden" Cannot find similar records = "Geen gelijkaardige records gevonden" +cannot set = "Kan niet worden ingesteld" Cassette = "Cassette" cat_establish_account = "Geef alsjeblieft de volgende gegevens in om jouw profiel aan te maken your account profile:" cat_password_abbrev = "Catalogus Wachtwoord" @@ -175,7 +179,12 @@ Catalog Login = "Catalogus aanmelding" Catalog Results = "Resultaten in de catalogus" catalog_login_desc = "Voer jouw bibliotheekcatalogusgegevens in" CD = "CD" +Change Email Address = "Verander je e-mailadres" Change Password = "Verander jouw wachtwoord" +change_email_disabled = "Je mag je e-mailadres momenteel niet aanpassen" +change_email_verification_reminder = "Als je dit formulier verzendt, wordt een e-mail verzonden naar het nieuwe adres; je moet op een link in de e-mail klikken voordat de wijziging van kracht wordt." +change_notification_email_message = "Er is zojuist een verzoek gedaan om je e-mailadres te wijzigen in %%library%%. Als je deze aanvraag niet gedaan hebt, log dan in op %%url%% en bevestig de integriteit van je profiel. Contacteer support via %%email%% als je vragen of opmerkingen hebt." +change_notification_email_subject = "Melding van wijziging van de e-mail van uw profiel" channel_add_more = "Voeg meer kanalen zoals deze toe" channel_browse = "Meer records" [If this just appears under the records shown, I would expect just to see "Meer" channel_expand = "Ontdek vergelijkbare kanalen" @@ -184,6 +193,7 @@ channel_search = "Toon items als zoekresultaten" channel_searchbox_label = "Zoek meer kanalen:" Check Hold = "Controleer de reservering" Check Recall = "Controleer de terugroeping" +check_profile = "Controleer gebruikersinformatie" Checked Out = "Uitgeleend" Checked Out Items = "Uitgeleende artikelen" Checkedout = "Uitgeleend" @@ -217,6 +227,7 @@ comment_error_load = "Foutmelding: De commentarenlijst kon niet worden vernieuwd comment_error_save = "Foutmelding: Commentaar werd niet opgeslagen" Comments = "Commentaar" Company/Entity = "Bedrijf/Entiteit" +Conference Proceeding = "Conferentie akten" Configuration = "Configuratie" confirm_delete = "Weet je zeker dat je dit wil verwijderen?" confirm_delete_brief = "Item Verwijderen?" @@ -297,6 +308,7 @@ Due Date = "Inleverdatum" DVD = "DVD" eBook = "E-boek" Edit = "Bewerken" +edit = "bewerk" Edit Library Card = "Bewerk bibliotheekkaart" Edit this Advanced Search = "Pas deze uitgebreide zoekopdracht aan" edit_list = "Lijst aanpassen" @@ -325,8 +337,14 @@ Email address is invalid = "Email adres is ongeldig" Email Record = "Email Record" Email this = "Versturen" Email this Search = "Zoekopdracht versturen" +email_change_pending_html = "Je hebt een e-mailadreswijziging tot %%pending%% hangende. Klik de link in de e-mail ter verificatie, verzonden naar dit e-mailadres om de aanpassing te vervolledigen. Indien nodig kunnen we de <a href = "%%url%%">e-mail ter verificatie opnieuw versturen</a>." email_failure = "Foutmelding - Bericht kan niet worden verzonden" email_link = "Link" +email_login_desc = "Gebruik onderstaande link om in te loggen. Als je deze login niet aanvroeg mag je dit bericht zonder meer negeren. Let op: deze link is maar geldig voor een beperkte tijd en enkel op het apparaat waarop je je e-mailadres invulde." +email_login_link = "Link naar de login: <%%url%%>" +email_login_link_sent = "We hebben een login link gestuurd naar je e-mailadres. Het kan even duren voor de link arriveert. Als je de link niet ontvangen hebt, controleer dan je spam folder." +email_login_requested = "Login werd aangevraagd met jouw e-mailadres op %%title%%." +email_login_subject = "Login voor %%title%%" email_maximum_recipients_note = "Maximaal %%max%% bestemmelingen toegestaan" email_multiple_recipients_note = "Bestemmelingen scheiden d.m.v. komma's" email_selected = "Email geselecteerd" @@ -336,6 +354,7 @@ email_subject = "Onderwerp" email_success = "Bericht is verzonden" Empty = "Leeg" Empty Book Bag = "Lege boekentas" +empty_search_disallowed = "Een lege zoekopdracht is niet toegestaan met het huidig zoekdoel" Enable Auto Config = "Automatische Configuratie Inschakelen" End Page = "Laatste pagina" Era = "Tijdperk" @@ -388,12 +407,13 @@ Favorites = "Favorieten" Fee = "Bijdrage" Feedback = "Feedback" feedback_email = "Email" -feedback_login_required = "Je moet eerst inloggen." feedback_name = "Naam" Field of activity = "Werkterrein" File Description = "Bestandsbeschrijving" Filter = "Filter" +Filter Collection = "Filter collection" filter_tags = "Filter tags" +filter_toggle_entries = "%%count%% filters" filter_wildcard = "Welke ook" Find = "Zoek" Find More = "Vind meer" @@ -429,6 +449,7 @@ Go to Standard View = "Ga naar standaardweergave" go_to_list = "Ga naar lijst" google_map_cluster = "Cluster" google_map_cluster_points = "Clusterpunten" +Government Document = "Overheidsdocument" Grid = "Tegels" Group = "Groep" group_AND = "ALLE groepen" @@ -455,6 +476,7 @@ history_results = "Resultaten" history_save = "Opslaan?" history_save_link = "Opslaan" history_saved_searches = "Jouw recent opgeslagen zoekopdrachten" +history_schedule = "Meldingsschema" history_search = "Zoekopdracht" history_time = "Tijdstip" hold_available = "Klaar om te worden opgehaald" @@ -601,6 +623,7 @@ Library Web Search = "Doorzoek de website van de bibliotheek" library_card_edit_password_placeholder = "Nieuw wachtwoord" lightbox_error = "Foutmelding: Pop-upvenster kan niet worden geladen" Limit To = "Beperken tot" +Link to full results = "Link naar volledige resultaten" List = "Lijst" List Tags = "Lijstlabels" list_access_denied = "Je hebt geen toestemming om deze lijst te zien." @@ -663,9 +686,12 @@ New Item Feed = "Abonnement op nieuwe items" New Item Search = "Zoeken naar nieuw item" New Item Search Results = "Resultaten voor nieuwe items" New Items = "Nieuwe items" +New results found for search = "Nieuwe resultaten gevonden voor je zoekopdracht" New Title = "Nieuwe titel" +new_email_success = "Je e-mailadres is aangepast" new_password = "Nieuw wachtwoord" new_password_success = "Succes! Jouw wachtwoord is aangepast." +new_results_heading = "%%count%% nieuwste resultaten" new_user_welcome_subject = "Uw nieuwe account in de %%library%%" new_user_welcome_text = "Welkom in de %%library%%. Er werd een nieuw account geopend voor %%firstname%% %%lastname%%. Je gebruikersnaam is %%username%%. Maak een paswoord aan op deze pagina: %%url%%" Newspaper = "Dagblad" @@ -681,6 +707,7 @@ No Preference = "Geen voorkeur" No reviews were found for this record = "Er werden geen besprekingen gevonden voor dit record" No Tags = "Geen labels" no_description = "Geen beschrijving beschikbaar." +no_email_address = "E-mailadres ontbreekt" no_items_selected = "er werden geen items geselecteerd" nohit_active_filters = "Eén of meerdere filters zijn toegepast op deze zoekopdracht. Als u filters verwijdert, kunt u mogelijk meer resultaten zien." nohit_change_tab = "U heeft gezocht in het tabblad '%%activeTab%% ". U kunt mogelijk iets vinden in één van de andere tabbladen:" @@ -732,6 +759,63 @@ number_decimal_point = "," number_thousands_separator = "." OAI Server = "OAI Server" Occupation = "Beroep" +od_account_noaccess = "Deze bibliotheek heeft geen toegang tot materiaal in Overdrive" +od_account_problem = "Er is een probleem met je account. %%message%%" +od_audiobook-mp3 = "MP3 audioboek" +od_audiobook-overdrive = "Overdrive Luister audioboek" +od_avail_avail = "Beschikbaar:" +od_avail_holds = "Reservaties:" +od_avail_total = "Totaal aantal kopieën:" +od_but_cancel_hold = "Annuleer deze reservatie" +od_but_checkout = "Uitleenbalie via Overdrive" +od_but_checkout_s = "Uitleenbalie" +od_but_gettitle = "Download dit materiaal" +od_but_gettitle_s = "Download" +od_but_hold = "Plaats een reservatie via Overdrive" +od_but_hold_s = "Plaats reservatie" +od_but_return = "Retourneer dit werk" +od_cancel_hold = "Annuleer Overdrive Reservatie" +od_checkout = "Overdrive Uitleenbalie" +od_code_connection_failed = "Connectie met Overdrive is mislukt. Als dit probleem blijft aanhouden, contacteer dan je bibliotheek." +od_code_contentnotavail = "Dit materiaal is niet beschikbaar in jouw regio." +od_code_login_for_avail = "Inloggen om beschikbaarheid te zien" +od_code_resource_not_found = "Werk niet gevonden" +od_content = "Overdrive materiaal" +od_dl_formats = "Ondersteunde downloadformaten" +od_docheckout_failure = "Dit werk kon niet worden uitgeleend." +od_docheckout_success = "Dit werk is aan u uitgeleend. Het verloopt op %%expireDate%%" +od_early_return = "Overdrive vroege inlevering" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Boek" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "OverDrive Read eBook" +od_ebook-pdf-adobe = "Adobe PDF eBook" +od_ebook-pdf-open = "Open PDF eBook" +od_expires_on = "Dit werk verloopt op %%due_date%%." +od_get_title = "Overdrive Download" +od_gettitle_failure = "Dit werk kon niet worden gedownload." +od_help_linktext = "Overdrive Help" +od_history = "Overdrive Geschiedenis" +od_hold = "Overdrive Reservatie" +od_hold_cancel_failure = "De annulering van de reservatie is mislukt." +od_hold_cancel_success = "De reservatie is met succes geannuleerd." +od_hold_email = "E-mailadres voor reservatie meldingen: %%holdEmailAddress%%." +od_hold_now_avail = "Deze reservering is beschikbaar voor uitleen tot %%expireDate%%." +od_hold_place_failure = "De reserveringsaanvraag is mislukt." +od_hold_place_success = "Deze titel is gereserveerd. Jouw plaats in de wachtrij is %%holdListPosition%%" +od_hold_placed_on = "Reservering geplaatst op %%holdPlacedDate%%." +od_hold_queue = "Positie %%holdPosition%% van %%numberOfHolds%% in de wachtrij" +od_holds = "Overdrive reserveringen" +od_info_unavail = "Deze informatie is momenteel niet beschikbaar." +od_is_checkedout = "Je hebt dit werk uitgeleend, het moet worden ingeleverd op %%due_date%%" +od_is_on_hold = "Je hebt dit werk gereserveerd." +od_loans = "Overdrive uitleningen" +od_mycontent_help = "Ga naar <a href="%%url%%">Overdrive Help</a> voor informatie over en hulp bij het downloaden van deze werken." +od_none_found = "Geen werken gevonden." +od_return_failure = "Dit werk kon niet worden ingeleverd." +od_return_success = "Dit werk is ingeleverd." +od_video-streaming = "streaming video file" of_num_results = "#%%position%% van %%total%% resultaten" old_password = "Oude wachtwoord" On Reserve = "Gereserveerd" @@ -761,6 +845,7 @@ pagination_label = "Paginering" Password = "Wachtwoord" Password Again = "Nog eens jouw wachtwoord" Password cannot be blank = "Wachtwoord kan niet blanco zijn" +password_error_auth_old = "Het eerder gebruikte paswoord is ongeldig" password_error_invalid = "Het wachtwoord is ongeldig (bv. bevat ongeldige karakters)" password_error_not_unique = "Het wachtwoord werd niet aangepast" password_maximum_length = "Het wachtwoord bevat maximaal %%maxlength%% karakters" @@ -826,6 +911,7 @@ Read the full review online... = "Lees volledige bespreking online..." Recall This = "Hou dit vast" recaptcha_not_passed = "Onjuiste CAPTCHA" recently_returned_channel_title = "Onlangs ingeleverd" +recommend_links_text = "Je kunt ook proberen:" Record Citations = "Record citatie" Record Count = "Aantal records" Record Type = "Document type" @@ -844,6 +930,7 @@ recovery_title = "Wachtwoord herstellen" recovery_too_soon = "Teveel herstelvragen werden verzonden, probeer later opnieuw" recovery_user_not_found = "We vinden jouw account niet" rectangle_center_message = "Dit is het middelpunt voor de rechthoek." +Reference Material = "Referentiemateriaal" Refine Results = "Verfijn jouw resultaten" Region = "Gebied" relais_available = "Dit item is beschikbaar via IBL (Interbibliothecair leenverkeer). Wil je het aanvragen?" @@ -884,6 +971,7 @@ Requests = "Aanvragen" Reserves = "Reserveringen" Reserves Search = "Zoekopdracht Reserveringen" Reserves Search Results = "Zoekresultaten voor Reserveringen" +reset_filters_button = "Wis alle filters" result_checkbox_label = "Selecteer resultaatnummer %%number%%" result_count = "%%count%% resultaten" Results = "Resultaten" @@ -899,6 +987,11 @@ Save Comment = "Sla commentaar op" save_search = "Sla zoekopdracht op" save_search_remove = "Verwijder opgeslagen zoekopdracht" Saved in = "Bewaard in" +schedule_daily = "Dagelijks" +schedule_explanation = "Ontvang meldingen over nieuwe resultaten voor je zoekopdracht" +schedule_none = "Geen" +schedule_weekly = "Wekelijks" +Scheduled Alert Results = "Geplande meldingen" scholarly_limit = "enkel artikelen uit wetenschappelijke tijdschriften" Scroll to Load More = "Scroll om meer gegevens te laden" Search = "Zoeken" @@ -913,6 +1006,7 @@ search results of = "zoekresultaten van" Search Tips = "Zoektips" Search Tools = "Zoekinstrumenten" Search Type = "Zoekwijze" +Search within collection = "Zoek binnen de collectie" search_AND = "ALLE Termen" search_groups = "zoekveldengroepen" search_match = "Overeenkomst" @@ -938,6 +1032,7 @@ Sensor Image = "Sensoriële afbeelding" Serial = "Serie" Series = "Reeks" Set = "Aanpassen" +show_filters_html = "Toon filters (%%count%%)" showing_items_html = "Toon <strong>%%start%% - %%end%%</strong> Items" showing_items_of_html = "Toon <strong>%%start%% - %%end%%</strong> van <strong>%%total%%</strong> Items" showing_results_for_html = "Toon <strong>%%start%% - %%end%%</strong> resultaten Voor zoekopdracht '<strong>%%lookfor%%</strong>'" @@ -985,6 +1080,7 @@ Start a new Basic Search = "Begin een nieuwe eenvoudige zoekopdracht" Start Page = "Startpagina" starting from = "beginnend bij" Status = "Status" +status_transit = "Wordt verwerkt" status_unknown_message = "Status is onbeschikbaar" Storage Retrieval Requests = "Documentaanvragen" storage_retrieval_request_available = "Klaar om te worden opgehaald" @@ -1060,6 +1156,7 @@ The record you selected is not part of any of your lists. = "Het door jou gesele The record you selected is not part of the selected list. = "Het door jou geselecteerde record maakt geen deel uit van de geselecteerde lijst." The system is currently unavailable due to system maintenance = "Door onderhoudswerken is het systeem momenteel niet beschikbaar" Theme = "Thema" +Thesis = "Thesis" This email was sent from = "Dit bericht werd verzonden via" This field is required = "Dit veld moet worden ingevuld" This item is already part of the following list/lists = "Dit item maakt al deel uit van de volgende lijst(en)" @@ -1090,6 +1187,9 @@ unique_tags = "Unieke tags" University Library = "Universiteitsbibliotheek" Unknown = "Onbekend" unrecognized_facet_label = "Andere" +unsubscribe_confirmation = "Wil je het e-mailabonnement opzeggen?" +unsubscribe_description = "Liever geen e-mails meer? Schrijf je uit via de volgende link" +unsubscribe_successful = "je bent uitgeschreven" Upgrade VuFind = "VuFind upgraden" upgrade_description = "Als je een voorgaande VuFind versie aan het upgraden bent, kan je jouw oude settings laden met dit hulpmiddel" URL = "URL" @@ -1099,6 +1199,14 @@ User Account = "Gebruikersaccount" Username = "Gebruikersnaam" Username cannot be blank = "Gebruikersnaam mag niet leeg zijn" Username is already in use in another library card = "Gebruikersnaam werd al gebruikt in een andere bibliotheekkaart" +verification_done = "Je e-mailadres werd met succes geverifieerd." +verification_email_change_sent = "Instructies voor het verifiëren van het e-mailadres zijn naar het nieuwe e-mailadres verzonden. Je moet het adres verifiëren voordat de wijziging van kracht wordt." +verification_email_notification = "Er werd een aanvraag verstuurd om je e-mailadres te verifieren voor je account bij %%library%%" +verification_email_sent = "Instructies ter verificatie van je e-mailadres werden verstuurd naar het e-mailadres gekoppeld aan dit account." +verification_email_subject = "VuFind e-mail verificatie" +verification_email_url_pretext = "Je kunt je e-mailadres op deze url verifieren: %%url%%" +verification_too_soon = "Je e-mailadres moet worden gevalideerd. Er werd een e-mail verzonden naar jouw geregistreerde e-mail adres. Als je deze niet ontvangen hebt, gelieve enkele minuten te wachten en het opnieuw te proberen." +verification_user_not_found = "We konden je account niet vinden" VHS = "VHS" Video = "Video" Video Clips = "Videoclips" diff --git a/languages/pl.ini b/languages/pl.ini index 306c809a4174b4e2ba5c08cb414bbbfb5d4beacd..45bdd747a6466d522eb6381008e18d2da4d636ef 100644 --- a/languages/pl.ini +++ b/languages/pl.ini @@ -112,6 +112,7 @@ advSearchError_notFound = "Nie znaleziono żądanego wyszukiwania." ajax_load_interrupted = "Åadowanie zostaÅ‚o przerwane" ajaxview_label_information = "Informacja" ajaxview_label_tools = "NarzÄ™dzia" +alert_email_address = "Wyniki alertów zostanÄ… wysÅ‚ane na adres e-maila" All = "wszystko" All Fields = "Wszystkie pola" All Pages Loaded = "Wszystkie strony sÄ… zaÅ‚adowane." @@ -121,6 +122,7 @@ alphabrowselink_html = "PrzeglÄ…daj zapisy wedÅ‚ug %%index%% zaczynajÄ…c od <a h An error has occurred = "WystÄ…piÅ‚ bÅ‚Ä…d." An error occurred during execution; please try again later. = "WystÄ…piÅ‚ bÅ‚Ä…d. Spróbuj później." AND = "I" +and = "i" anonymous_tags = "anonimowa etykieta" APA Citation = "Styl cytowania APA" applied_filter = "Zastosowane filtry" @@ -133,6 +135,8 @@ authentication_error_admin = "Obecnie nie możesz siÄ™ zalogować. Skontaktuj si authentication_error_blank = "Nazwa użytkownika lub hasÅ‚o nie mogÄ… być puste." authentication_error_creation_blocked = "Nie masz upoważnienia aby stworzyć konto." authentication_error_denied = "Dane sÄ… nieważne. DostÄ™p niedozwolony." +authentication_error_email_not_verified_html = "Twój adres e-maila nie zostaÅ‚ jeszcze zweryfikowany. Sprawdź filtr antyspamowy pod kÄ…tem wiadomoÅ›ci weryfikacyjnej. W razie potrzeby możemy ponownie <a href="%%url%%">wysÅ‚ać e-mail weryfikacyjny</a>." +authentication_error_in_progress = "Proces uwierzytelnienia jest już przetwarzany. Spróbuj ponownie później, jeÅ›li chcesz zacząć od nowa." authentication_error_invalid = "Dane sÄ… nieważne. Spróbuj jeszcze raz." authentication_error_loggedout = "WylogowaÅ‚eÅ› siÄ™" authentication_error_technical = "Obecnie nie możesz siÄ™ zalogować. Spróbuj później." @@ -158,7 +162,6 @@ Backtrace = "Åšlad wsteczny" Bag = "Schowek" Balance = "Saldo" Barcode = "Kod kreskowy" -basic_search_keep_filters = "Zatrzymaj filtry" Be the first to leave a comment = "Napisz pierwszy komentarz" Be the first to tag this record = "DoÅ‚Ä…cz pierwszÄ… etykiete" Bibliographic Details = "Opis bibliograficzny" @@ -228,6 +231,7 @@ Call Number = "Sygnatura" callnumber_abbrev = "KN #" Cannot find record = "Nie znaleziono rekordu" Cannot find similar records = "Nie znaleziono podobnych rekordów" +cannot set = "Nie można ustawić" Cassette = "Kaseta" cat_establish_account = "Aby zaÅ‚ożyć konto, podaj nastÄ™pujÄ…ce dane:" cat_password_abbrev = "HasÅ‚o" @@ -236,7 +240,12 @@ Catalog Login = "Login" Catalog Results = "Rezultaty" catalog_login_desc = "Podaj dane do logowania." CD = "CD" +Change Email Address = "ZmieÅ„ adres e-maila" Change Password = "ZmieÅ„ hasÅ‚o" +change_email_disabled = "W tej chwili nie możesz zmienić swojego adresu e-mail" +change_email_verification_reminder = "PrzesÅ‚anie tego formularza spowoduje wysÅ‚anie wiadomoÅ›ci e-mail na nowy adres; bÄ™dziesz musiaÅ‚ kliknąć link w e-mailu, zanim zmiana zostanie zrealizowana." +change_notification_email_message = "WysÅ‚ano proÅ›bÄ™ o zmianÄ™ adresu e-mail w %%library%%. JeÅ›li nie zaczÄ…Å‚eÅ› tego żądania, możesz zalogować siÄ™ na %%url%% i potwierdzić integralność swojego konta. JeÅ›li masz pytania lub wÄ…tpliwoÅ›ci, skontaktuj siÄ™ z pomocÄ… technicznÄ… pod adresem %%email%%." +change_notification_email_subject = "Powiadomienie o zmianie adresu e-mail konta." channel_add_more = "DoÅ‚Ä…cz wiÄ™cej podobnych kanałów" channel_browse = "PrzeglÄ…daj dalsze zapisy" channel_expand = "PrzeglÄ…daj podobne kanaÅ‚y" @@ -245,6 +254,7 @@ channel_search = "Pokaż egzemplarze jako rezultaty" channel_searchbox_label = "Szukaj dalsze kanaÅ‚y:" Check Hold = "Sprawdź możliwość zamówienia / zarezerwowania" Check Recall = "Sprawdź możliwość odwoÅ‚ania" +check_profile = "Sprawdź informacje czytelnika." Checked Out = "Wypożyczone" Checked Out Items = "Wypożyczone książki" Checkedout = "Wypożyczone" @@ -278,6 +288,7 @@ comment_error_load = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zaÅ‚adować komentarzy" comment_error_save = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zapisać komentarza" Comments = "Komentarze" Company/Entity = "Firma/jednostka" +Conference Proceeding = "MateriaÅ‚y konferencyjne" Configuration = "Konfiguracja" confirm_delete = "Czy chcesz to naprawdÄ™ usunąć?" confirm_delete_brief = "Usunąć?" @@ -358,6 +369,7 @@ Due Date = "Termin zwrotu" DVD = "DVD" eBook = "E-book" Edit = "Edytuj" +edit = "edytuj" Edit Library Card = "Edytuj kartÄ™ bibliotecznÄ…" Edit this Advanced Search = "Edytuj wyszukiwanie zaawansowane" edit_list = "Opracuj listÄ™" @@ -386,8 +398,14 @@ Email address is invalid = "E-Mail jest nieważny" Email Record = "WyÅ›lij emailem" Email this = "WyÅ›lij emailem" Email this Search = "WyÅ›lij rezultaty emailem" +email_change_pending_html = "Masz w oczekiwaniu zmianÄ™ adresu e-maila na %%pending%%. Kliknij link w e-mailu weryfikacyjnym wysÅ‚anym na ten adres, aby dokoÅ„czyć zmianÄ™. W razie potrzeby możemy ponownie <a href="%%url%%">wysÅ‚ać e-mail weryfikacyjny</a>." email_failure = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ wysÅ‚ać wiadomoÅ›ci." email_link = "email" +email_login_desc = "Użyj nastÄ™pujÄ…cy link, aby siÄ™ zalogować. JeÅ›li nie zaczÄ…Å‚eÅ› logowania, możesz zignorować tÄ™ wiadomość. PamiÄ™taj, że link jest ważny tylko przez ograniczony czas i tylko na urzÄ…dzeniu, którego użyto do wpisania adresu e-mail." +email_login_link = "Link do logowania: <%%url%%>" +email_login_link_sent = "WysÅ‚aliÅ›my link do logowania na Twój adres e-maila. Dostarczenie linku może chwilÄ™ potrwać. JeÅ›li nie otrzymasz e-maila wkrótce, sprawdź również filtr antyspamowy." +email_login_requested = "Zażądano logowania przy użyciu adresu e-maila pod %%title%%." +email_login_subject = "Zaloguj siÄ™ do %%title%%" email_maximum_recipients_note = "Najwyżej %%max%% adresatów jest dozwolone." email_multiple_recipients_note = "Można podać wiÄ™kszÄ… liczbÄ™ adresatów oddzielonych przecinkami." email_selected = "WyÅ›lij zaznaczone książki przez emaila" @@ -397,6 +415,7 @@ email_subject = "HasÅ‚o przedmiotowe" email_success = "Wiadomość wysyÅ‚ana" Empty = "Puste" Empty Book Bag = "Pusta lista podrÄ™czna" +empty_search_disallowed = "Puste poszukiwanie nie jest dozwolone w bieżącym wyszukiwaniu." Enable Auto Config = "Zaaktywuj autokonfiguracjÄ™" End Page = "Ostatnia strona" Era = "Era" @@ -449,12 +468,13 @@ Favorites = "Ulubione książki" Fee = "OpÅ‚ata" Feedback = "Feedback" feedback_email = "Email" -feedback_login_required = "Najpierw siÄ™ zaloguj." feedback_name = "Nazwisko" Field of activity = "Zakres dziaÅ‚alnoÅ›ci" File Description = "Opis pliku" Filter = "filtr" +Filter Collection = "Filtruj kolekcjÄ™" filter_tags = "Filtruj etykiety" +filter_toggle_entries = "%%count%% filtry" filter_wildcard = "wszystko" Find = "Szukaj" Find More = "Dalsze opcje" @@ -490,6 +510,7 @@ Go to Standard View = "do wersji standartowej" go_to_list = "Idź do listy" google_map_cluster = "Skupienie" google_map_cluster_points = "Aglomeracja" +Government Document = "Dokument rzÄ…dowy" Grid = "do wersji Grid" Group = "Grupa" group_AND = "z WSZYSTKIMI grupami" @@ -516,6 +537,7 @@ history_results = "Rezultat" history_save = "Zapisać?" history_save_link = "Zapisz" history_saved_searches = "Historia wyszukiwania" +history_schedule = "rozkÅ‚ad alertów" history_search = "Wyszukiwanie" history_time = "Czas" hold_available = "do odebrania" @@ -662,6 +684,7 @@ Library Web Search = "Wyszukiwanie w internecie" library_card_edit_password_placeholder = "Nowe hasÅ‚o" lightbox_error = "BÅ‚Ä…d: Nie udaÅ‚o siÄ™ zaÅ‚adować okienka pop-up." Limit To = "Ogranicz do" +Link to full results = "Link do peÅ‚nych wyników" List = "Lista" List Tags = "Stwórz listÄ™ etykiet" list_access_denied = "Nie masz odpowiednich uprawieÅ„ do oglÄ…dania tej listy." @@ -724,9 +747,12 @@ New Item Feed = "RSS-Feed dla nowych nabytków" New Item Search = "Szukaj nowych nabytków" New Item Search Results = "Rezultaty nowych nabytków" New Items = "Nowe nabytki" +New results found for search = "Znaleziono nowe wyniki dla wyszukiwania" New Title = "Nowy tytuÅ‚" +new_email_success = "Twój adres e-mail zostaÅ‚ zmieniony" new_password = "Nowe hasÅ‚o" new_password_success = "HasÅ‚o zostaÅ‚o zmienione" +new_results_heading = "%%count%% najnowsze wyniki" new_user_welcome_subject = "Twoje konto czytelnika w %%library%%" new_user_welcome_text = "Witamy w %%library%%. UtworzyliÅ›my nowe konto czytelnika dla %%firstname%% %%lastname%%. Twoja nazwa użytkownika jest %%username%%. Utwórz hasÅ‚o na tej stronie: %%url%%" Newspaper = "Gazeta" @@ -742,6 +768,7 @@ No Preference = "Bez preferencji" No reviews were found for this record = "Ten zapis nie ma recenzji." No Tags = "Nie ma etykietki" no_description = "Nie ma specyfikacji." +no_email_address = "Brak adresu e-mail." no_items_selected = "Nic nie wybraÅ‚eÅ›" nohit_active_filters = "Jeden lub wiÄ™cej filtrów zostaÅ‚ zastosowany do wyszukiwania. JeÅ›li usuniesz filtry, możesz otrzymać wiÄ™cej rezultatów." nohit_change_tab = "WyszukiwaÅ‚eÅ› w etykiecie "%%activeTab%%". Może znajdziesz coÅ› w jednej z innych etykiet:" @@ -793,6 +820,63 @@ number_decimal_point = "," number_thousands_separator = "." OAI Server = "Server OAI" Occupation = "Zawód" +od_account_noaccess = "To konto czytelnika nie ma dostÄ™pu do zasobów w Overdrive" +od_account_problem = "WystÄ…piÅ‚ problem z Twoim kontem. %%message%%" +od_audiobook-mp3 = "MP3 audiobook" +od_audiobook-overdrive = "OverDrive posÅ‚uchaj audiobook" +od_avail_avail = "DostÄ™pne:" +od_avail_holds = "Zamówienia:" +od_avail_total = "Suma egzemplarzy:" +od_but_cancel_hold = "Anuluj zamówienie" +od_but_checkout = "wypożyczyć poprzez Overdrive" +od_but_checkout_s = "wypożyczyć" +od_but_gettitle = "Pobierz tytuÅ‚" +od_but_gettitle_s = "Pobierz" +od_but_hold = "Zamów poprzez Overdrive" +od_but_hold_s = "Zamów" +od_but_return = "Zwróć ten tytuÅ‚" +od_cancel_hold = "Anuluj zamówienie w Overdrive" +od_checkout = "wypożyczyć poprzez Overdrive" +od_code_connection_failed = "PoÅ‚Ä…czenie do Overdrive nieudaÅ‚o siÄ™. JeÅ›li problem bÄ™dzie siÄ™ powtarzaÅ‚, skontaktuj siÄ™ ze swojÄ… bibliotekÄ…." +od_code_contentnotavail = "Ten tytuÅ‚ nie jest dostÄ™pny w Twojej okolicy." +od_code_login_for_avail = "Zaloguj siÄ™, aby sprawdzić dostÄ™pność" +od_code_resource_not_found = "Nie znaleziono tytuÅ‚u" +od_content = "TytuÅ‚y z Overdrive" +od_dl_formats = "ObsÅ‚ugiwane formaty" +od_docheckout_failure = "Nie można wypożyczyć tego tytuÅ‚u." +od_docheckout_success = "Ten tytuÅ‚ zostaÅ‚ przez Ciebie wypożyczony. Termin zwrotu mija %%expireDate%%" +od_early_return = "Overdrive wczesny zwrot" +od_ebook-epub-adobe = "Adobe EPUB e-book" +od_ebook-epub-open = "Open EPUB e-book" +od_ebook-kindle = "Kindle e-book" +od_ebook-mediado = "MediaDo Reader e-book" +od_ebook-overdrive = "OverDrive Read e-book" +od_ebook-pdf-adobe = "Adobe PDF e-book" +od_ebook-pdf-open = "Open PDF e-book" +od_expires_on = "Termin zwrotu tego tytuÅ‚u mija %%due_date%%." +od_get_title = "ÅšciÄ…gnij z Overdrive" +od_gettitle_failure = "Nie można Å›ciÄ…gnąć tego tytuÅ‚u." +od_help_linktext = "Strona pomocy Overdrive" +od_history = "Historia Overdrive" +od_hold = "Zamówienie w Overdrive" +od_hold_cancel_failure = "Anulowanie zamówienia w Overdrive nieudaÅ‚o siÄ™." +od_hold_cancel_success = "Zamówienie zostaÅ‚o anulowane." +od_hold_email = "Adres e-maila do powiadomienia o zamówieniu: %%holdEmailAddress%%." +od_hold_now_avail = "To zamówienie czeka na odebranie. Termin zwrotu mija %%expireDate%%." +od_hold_place_failure = "Zamówienie nieudaÅ‚o siÄ™." +od_hold_place_success = "Ten tytuÅ‚ zostaÅ‚ zamówiony. JesteÅ› na miejscu %%holdListPosition%%" +od_hold_placed_on = "Zamówione w %%holdPlacedDate%%." +od_hold_queue = "Miejsce %%holdPosition%% z %%numberOfHolds%% w kolejce." +od_holds = "Zamówienie w Overdrive" +od_info_unavail = "Ta informacja jest obecnie niedostÄ™pna." +od_is_checkedout = "Masz tytuÅ‚ na twoim koncie. Termin zwrotu mija %%due_date%%." +od_is_on_hold = "ZamówiÅ‚eÅ› ten tytuÅ‚." +od_loans = "Wypożyczenia w Overdrive" +od_mycontent_help = "Aby uzyskać informacje i pomoc w pobieraniu tych tytułów, zobacz <a href="%%url%%">stronÄ™ pomocy Overdrive</a>." +od_none_found = "Nie znaleziono tytułów." +od_return_failure = "Nie nieudaÅ‚o siÄ™ zwrócić tego tytuÅ‚u." +od_return_success = "TytuÅ‚ zostaÅ‚ zwrócony." +od_video-streaming = "Plik wideo przesyÅ‚any strumieniowo" of_num_results = "#%%position%% od %%total%% Rezultaty" old_password = "Stare hasÅ‚o" On Reserve = "Zarezerwowane" @@ -822,6 +906,7 @@ pagination_label = "Paginacja" Password = "HasÅ‚o" Password Again = "Powtórz hasÅ‚o" Password cannot be blank = "HasÅ‚o nie może być spacjÄ…" +password_error_auth_old = "Poprzednio używane hasÅ‚o jest nieprawidÅ‚owe" password_error_invalid = "Nowe hasÅ‚o jest nieważne (n.p. zawiera nieprawidÅ‚owe znaki)" password_error_not_unique = "HasÅ‚o nie zostaÅ‚o zmienione" password_maximum_length = "Maksymalna dÅ‚ugość hasÅ‚a to %%maxlength%% znaków" @@ -887,6 +972,7 @@ Read the full review online... = "Przeczytaj caÅ‚Ä… recenzjÄ™..." Recall This = "OdwoÅ‚aj" recaptcha_not_passed = "CAPTCHA nie zweryfikowany" recently_returned_channel_title = "Niedawno zwrócone" +recommend_links_text = "Spróbuj także:" Record Citations = "Cytaty zapisu" Record Count = "Ilość zapisów" Record Type = "Typ zapisu" @@ -905,6 +991,7 @@ recovery_title = "Odzyskanie hasÅ‚a" recovery_too_soon = "WysÅ‚ano za dużo żądaÅ„ odzyskania. Spróbuj później" recovery_user_not_found = "Nie znaleźliÅ›my takiego konta" rectangle_center_message = "To jest punkt Å›rodkowy podÅ›wietlonego prostokÄ…ta." +Reference Material = "MateriaÅ‚ odniesienia" Refine Results = "Redukuj rezultaty" Region = "Region" relais_available = "Ta książka jest dostÄ™pna przez wypożyczenie miÄ™dzybiblioteczne. Chcesz zamówić?" @@ -945,6 +1032,7 @@ Requests = "Zamówienia" Reserves = "Rezerwacje" Reserves Search = "Wyszukiwanie rezerwacji" Reserves Search Results = "Rezultaty rezerwacji" +reset_filters_button = "UsuÅ„ filtry" result_checkbox_label = "Wybierz numer wyniku %%number%%" result_count = "%%count%% Rezultatów" Results = "Rezultaty" @@ -960,6 +1048,11 @@ Save Comment = "Zapisz komentarz" save_search = "Zapisz wyszukiwanie" save_search_remove = "UsuÅ„ zapisane wyszukiwanie" Saved in = "Zapisane w" +schedule_daily = "dzienny" +schedule_explanation = "Otrzymuj powiadomienia poprzez e-maila o nowych wynikach wyszukiwania." +schedule_none = "Å»aden" +schedule_weekly = "tygodniowo" +Scheduled Alert Results = "Wyniki alertów" scholarly_limit = "Ogranicz do artykułów z czasopism" Scroll to Load More = "PrzewiÅ„ w dół aby zobaczyć dalsze rezultaty" Search = "Wyszukiwanie" @@ -974,6 +1067,7 @@ search results of = "Rezultaty dla" Search Tips = "Wskazówka do wyszukiwania" Search Tools = "NarzÄ™dzie wyszukiwania" Search Type = "Sposób wyszukiwania" +Search within collection = "Szukaj w kolekcji" search_AND = "z WSZYSTKIMI sÅ‚owami" search_groups = "Grupa wyszukiwania" search_match = "Zbieżność" @@ -999,6 +1093,7 @@ Sensor Image = "Czujnik obrazu" Serial = "Wydawnictwo ciÄ…gÅ‚e" Series = "Seria" Set = "ZmieÅ„" +show_filters_html = "Pokaż filtry (%%count%%)" showing_items_html = "Rezultaty <strong>%%start%% - %%end%%</strong> Książka" showing_items_of_html = "Rezultaty <strong>%%start%% - %%end%%</strong> od <strong>%%total%%</strong> Książka" showing_results_for_html = "Rezultaty <strong>%%start%% - %%end%%</strong> Rezultaty Dla wyszukiwania '<strong>%%lookfor%%</strong>'" @@ -1046,6 +1141,7 @@ Start a new Basic Search = "Wyszukiwanie" Start Page = "Pierwsza strona" starting from = "od" Status = "Status" +status_transit = "W tranzycie" status_unknown_message = "Możliwość dostÄ™pu nieznana" Storage Retrieval Requests = "Zamówienia magazynowe" storage_retrieval_request_available = "do odebrania" @@ -1121,6 +1217,7 @@ The record you selected is not part of any of your lists. = "Wybrany zapis nie z The record you selected is not part of the selected list. = "Wybrany zapis nie znajduje siÄ™ na tej liÅ›cie." The system is currently unavailable due to system maintenance = "Z powodu przeglÄ…du technicznego system jest niedostÄ™pny." Theme = "Layout" +Thesis = "Praca dyplomowa" This email was sent from = "E-Mail zostaÅ‚ wysÅ‚any przez" This field is required = "Pole konieczne" This item is already part of the following list/lists = "Ten zapis już jest na tej liÅ›cie." @@ -1151,6 +1248,9 @@ unique_tags = "Niepowtarzalne etykiety" University Library = "Biblioteka Uniwersytecka" Unknown = "Nieznany" unrecognized_facet_label = "inne" +unsubscribe_confirmation = "Czy chcesz anulować subskrypcjÄ™?" +unsubscribe_description = "Nie chcesz otrzymywać tej wiadomoÅ›ci w przyszÅ‚oÅ›ci? Anuluj subskrypcjÄ™, korzystajÄ…c z poniższego linku." +unsubscribe_successful = "Subskrypcja anulowana" Upgrade VuFind = "Aktualizacja oprogramowania Vufind" upgrade_description = "Za pomocÄ… tego narzÄ™dzia możesz utrzymać ustawienia przy aktualizacji z wczeÅ›niejszej wersji Vufind." URL = "URL" @@ -1160,6 +1260,14 @@ User Account = "Konto czytelnika" Username = "Nazwa użytkownika" Username cannot be blank = "Nazwa użytkownika nie może być spacjÄ…" Username is already in use in another library card = "Nazwa użytkownika już istnieje" +verification_done = "Twój adres e-maila zostaÅ‚ zweryfikowany." +verification_email_change_sent = "Instrukcje weryfikacji adresu e-mail zostaÅ‚y wysÅ‚ane na nowy adres. Musisz zweryfikować adres, zanim zmiana zacznie obowiÄ…zywać." +verification_email_notification = "WÅ‚aÅ›nie wysÅ‚ano proÅ›bÄ™ o zweryfikowanie adresu e-mail konta w %%library%%." +verification_email_sent = "Instrukcje weryfikacji adresu e-mail zostaÅ‚y wysÅ‚ane na adres zarejestrowany na Twoim koncie." +verification_email_subject = "Weryfikacja adresu e-mail poprzez VuFind" +verification_email_url_pretext = "Możesz zweryfikować Twój adres e-maila pod tym adresem: %%url%%" +verification_too_soon = "Adres e-maila wymaga weryfikacji. Wiadomość zostaÅ‚a wysÅ‚ana na zarejestrowany adres e-mail. JeÅ›li go nie otrzymaÅ‚eÅ›, poczekaj kilka minut i spróbuj ponownie." +verification_user_not_found = "Nie mogliÅ›my znaleźć Twojego konta" VHS = "VHS" Video = "Wideo" Video Clips = "Wideoclips" diff --git a/languages/pt-br.ini b/languages/pt-br.ini index dd4f91057ed21350752ae0264d7dd00d69b64c37..2344a80b7ccd1959bb7e2fdcf7c42dc8045f6d8c 100644 --- a/languages/pt-br.ini +++ b/languages/pt-br.ini @@ -50,6 +50,7 @@ advSearchError_notFound = "A busca que solicitou não foi encontrada." ajax_load_interrupted = "Carregamento interrompido" ajaxview_label_information = "Informação" ajaxview_label_tools = "Ferramentas" +alert_email_address = "Os resultados do alerta agendado serão enviados para o endereço de e-mail" All = "Todos" All Fields = "Todos os campos" All Pages Loaded = "Todas as páginas carregadas" @@ -59,6 +60,7 @@ alphabrowselink_html = "Visualizar entradas pelo %%index%% iniciando com <a href An error has occurred = "Erro na aplicação" An error occurred during execution; please try again later. = "Ocorreu um erro durante a execução; por favor, tente novamente mais tarde." AND = "E" +and = "e" anonymous_tags = "Tags anônimas" APA Citation = "Citação norma APA" applied_filter = "Filtro Aplicado" @@ -71,6 +73,8 @@ authentication_error_admin = "Não é possÃvel fazer login no momento. Contate authentication_error_blank = "Informação de login não pode estar em branco." authentication_error_creation_blocked = "Não possui permissão para criar uma conta." authentication_error_denied = "Credenciais inválidas! Acesso negado." +authentication_error_email_not_verified_html = "Seu endereço de e-mail ainda não foi confirmado. Por favor, verifique o seu filtro de spam para a mensagem de confirmação. Se necessário, podemos <a href="%%url%%"> reenviar o e-mail de confirmação</a>." +authentication_error_in_progress = "A solicitação de autenticação já está sendo processada. Tente novamente mais tarde se precisar começar de novo." authentication_error_invalid = "Login inválido -- por favor, tente novamente." authentication_error_loggedout = "Você fez logoff." authentication_error_technical = "Não pode fazer login no momento. Tente novamente mais tarde, por favor." @@ -96,7 +100,6 @@ Backtrace = "Caminho" Bag = "Cesto" Balance = "Balanço" Barcode = "Código de Barra" -basic_search_keep_filters = "Restringir a busca atual / manter filtros" Be the first to leave a comment = "Seja o primeiro a deixar um comentário" Be the first to tag this record = "seja o primeiro a adicionar uma tag" Bibliographic Details = "Detalhes bibliográficos" @@ -109,8 +112,8 @@ Book Cover = "Capa do livro" bookbag_confirm_empty = "Tem a certeza que quer esvaziar a sua cesta?" bookbag_delete = "Apagar os registros selecionados da cesta" bookbag_delete_selected = "Apagar os registros selecionados" -bookbag_email = "Mandar por email os registros contidos na cesta" -bookbag_email_selected = "Mandar por email os selecionados" +bookbag_email = "Mandar por e-mail os registros contidos na cesta" +bookbag_email_selected = "Mandar por e-mail os selecionados" bookbag_export = "Exportar os registros contidos na cesta" bookbag_export_selected = "Exportar os registros selecionados" bookbag_full = "Cheio" @@ -135,11 +138,11 @@ Browse the Collection Alphabetically = "Navegar a coleção por ordem alfabétic browse_author = "Autor" browse_dewey = "CDU (Dewey)" browse_format = "Formato" -browse_lcc = "Call Number (LC)" +browse_lcc = "Número de Chamada (LC)" browse_publishDate = "Ano de Publicação" browse_title = "TÃtulo" browse_topic = "Assunto" -bulk_email_success = "O(s) item(ns) foram enviados por email" +bulk_email_success = "O(s) item(ns) foram enviados por e-mail" bulk_email_title = "Itens do Catálogo da Biblioteca" bulk_error_missing = "Alguns dados estavam faltando. Seu pedido não foi bem-sucedida." bulk_export_not_supported = "Pedimos desculpa: a sua lista contém registros dos quais o sistema ainda não permite fazer a exportação. Dentro em breve, esta funcionalidade estará 100% operacional. Obrigado pela sua compreensão!" @@ -166,6 +169,7 @@ Call Number = "Ãrea/Cota" callnumber_abbrev = "Cota #" Cannot find record = "Registro não encontrado" Cannot find similar records = "Registros relacionados não encontrados" +cannot set = "Não é possÃvel definir" Cassette = "Cassete" cat_establish_account = "Para poder criar uma conta, por favor insira a seguinte informação:" cat_password_abbrev = "Senha do catálogo" @@ -174,7 +178,12 @@ Catalog Login = "Login no catálogo" Catalog Results = "Resultados do Catálogo" catalog_login_desc = "Entre com as suas credenciais do catálogo da biblioteca." CD = "CD" +Change Email Address = "Mude o endereço de e-mail" Change Password = "Mudar Senha" +change_email_disabled = "Você não tem permissão para alterar seu endereço de e-mail no momento" +change_email_verification_reminder = "A submissão deste formulário enviará um e-mail para o novo endereço; você precisará clicar em um link no e-mail para que a alteração entre em vigor." +change_notification_email_message = "Foi feita uma solicitação para alterar seu endereço de e-mail na %%library%%. Se você não iniciou essa solicitação, faça o login em %%url%% e confirme a integridade da sua conta. Entre em contato com o suporte em %%email%% se você tiver dúvidas ou preocupações." +change_notification_email_subject = "Notificação de alteração de e-mail da conta" channel_add_more = "Adicionar mais canais como este" channel_browse = "Paginar mais registro" channel_expand = "Explorar canais relacionados" @@ -183,6 +192,7 @@ channel_search = "Apresentar itens como resultados de busca" channel_searchbox_label = "Buscar por mais canais:" Check Hold = "Ver Reserva" Check Recall = "Ver Pedido de Devolução" +check_profile = "Verifique as informações do usuário." Checked Out = "Emprestado" Checked Out Items = "Itens emprestados" Checkedout = "Emprestado" @@ -216,6 +226,7 @@ comment_error_load = "Erro: Não foi possÃvel apagar a lista de comentários" comment_error_save = "Erro: Não foi possÃvel salvar o comentário" Comments = "Comentários" Company/Entity = "Companhia/Entidade" +Conference Proceeding = "Anais de Congresso" Configuration = "Configuração" confirm_delete = "Tem certeza que quer apagar este usuário?" confirm_delete_brief = "Apagar registro?" @@ -260,7 +271,7 @@ date_from = "De" date_month_placeholder = "M" date_to = "Até" date_year_placeholder = "A" -Debug Information = "Debug Information" +Debug Information = "Informações de depuração" del_search = "Retirar o grupo de busca" Delete = "Apagar" delete_account_confirm = "Tem certeza que quer remover sua conta?" @@ -296,6 +307,7 @@ Due Date = "Data fim do empréstimo" DVD = "DVD" eBook = "livro eletrônico" Edit = "Editar" +edit = "editar" Edit Library Card = "Editar Cartão de Biblioteca" Edit this Advanced Search = "Editar a Busca Avançada" edit_list = "Editar a Lista" @@ -318,23 +330,30 @@ eds_mode_bool = "Booleano/frase" eds_mode_smart = "Busca com algoritmo SmartText" eds_modes_and_expanders = "Modos de Busca e expansores" Electronic = "Recurso Eletrônico" -Email = "Email" -Email Address = "Endereço de email" -Email address is invalid = "Email inválido" -Email Record = "Gravar email" -Email this = "Enviar por email" -Email this Search = "Enviar busca por email" +Email = "E-mail" +Email Address = "Endereço de e-mail" +Email address is invalid = "E-mail inválido" +Email Record = "Gravar e-mail" +Email this = "Enviar por e-mail" +Email this Search = "Enviar busca por e-mail" +email_change_pending_html = "Você tem uma alteração de e-mail pendente em %%pending%%. clique no link no e-mail de verificação enviado para este endereço para concluir a alteração. Se necessário, podemos <a href="%%url%%">Reenviar o e-mail de verificação </a>." email_failure = "Erro - Mensagem não pode ser enviada" email_link = "Link" +email_login_desc = "Use o link a seguir para efetuar login. Se você não iniciou o login, poderá ignorar esta mensagem com segurança. Observe que o link é válido apenas por tempo limitado e somente com o dispositivo usado para inserir o endereço de e-mail." +email_login_link = "Link para o login: <%%url%%>" +email_login_link_sent = "Enviamos um link de login para o seu endereço de e-mail. Pode levar alguns instantes para o link chegar. Se você não receber o link em breve, verifique também o seu filtro de spam." +email_login_requested = "O login foi solicitado com seu endereço de e-mail em %%title%%." +email_login_subject = "Faça login no %%title%%" email_maximum_recipients_note = "No máximo %%max%% destinatários são permitidos." email_multiple_recipients_note = "Você pode especificar múltiplos destinatários separados por vÃrgulas." -email_selected = "Email selecionado" -email_selected_favorites = "Email os Favoritos selecionados" +email_selected = "E-mail selecionado" +email_selected_favorites = "E-mail os Favoritos selecionados" email_sending = "A enviar a mensagem..." email_subject = "Assunto" email_success = "Mensagem Enviada" Empty = "Esvaziar" Empty Book Bag = "Esvaziar a cesta" +empty_search_disallowed = "Uma consulta vazia não é permitida com o destino de pesquisa atual" Enable Auto Config = "Activar Auto Config" End Page = "Última página" Era = "PerÃodo" @@ -369,16 +388,16 @@ external_auth_unauthorized = "Você não está autorizado a acessar material lic external_auth_unauthorized_desc = "Sua forma de login não oferta acesso a material licenciado. Por favor, faça logoff e se logue novamente de outra forma." facet_list_empty = "Não há dados disponÃveis" facet_list_for = "Lista de facetas para %%field%%" -FAQs = "FAQs" +FAQs = "Perguntas Frequentes" fav_delete = "Apagar os Favoritos selecionados" fav_delete_deleting = "Os Favoritos selecionados serão apagados." fav_delete_fail = "Desculpe, ocorreu um erro. Os seu(s) favorito(s) não foram apagados." fav_delete_missing = "Alguns dados foram perdidos. Os seu(s) favorito(s) não foram apagados." fav_delete_success = "Favorito(s) apagado(s)." fav_delete_warn = "Você está prestes a apagar os favoritos a partir de todas as suas listas - Se você quiser apagar favoritos a partir de uma lista especÃfica, selecione a lista antes de clicar em apagar." -fav_email_fail = "Desculpe, ocorreu um erro. Favorito(s) não enviado(s) por email." -fav_email_missing = "Alguns dados foram perdidos. Favorito(s) não enviado(s) por email." -fav_email_success = "Favorito(s) enviado(s) por email conforme solicitado." +fav_email_fail = "Desculpe, ocorreu um erro. Favorito(s) não enviado(s) por e-mail." +fav_email_missing = "Alguns dados foram perdidos. Favorito(s) não enviado(s) por e-mail." +fav_email_success = "Favorito(s) enviado(s) por e-mail conforme solicitado." fav_export = "Exportar Favoritos" fav_list_delete = "Lista de favoritos apagada." fav_list_delete_cancel = "Lista não apagada." @@ -386,13 +405,14 @@ fav_list_delete_fail = "Desculpe, ocorreu um erro. Lista não eliminada." Favorites = "Itens Guardados" Fee = "Multas" Feedback = "Comentário" -feedback_email = "Email" -feedback_login_required = "Você deve entrar na sua conta antes." +feedback_email = "E-mail" feedback_name = "Nome" Field of activity = "Campo de atividade" File Description = "Descrição de arquivo" Filter = "Filtro" +Filter Collection = "Coleção de filtros" filter_tags = "Tags de filtro" +filter_toggle_entries = "%%count%% filtros" filter_wildcard = "Qualquer" Find = "Buscar" Find More = "Encontrar Mais" @@ -428,10 +448,11 @@ Go to Standard View = "Ir para a visualização padrão" go_to_list = "Ir para a lista" google_map_cluster = "Cluster" google_map_cluster_points = "Pontos de cluster" +Government Document = "Documento Governamental" Grid = "Grid" Group = "Grupo" group_AND = "TODOS os Grupos" -group_OR = "QUALQUER grupos" +group_OR = "QUAISQUER Grupos" Has Illustrations = "Possui ilustrações" Help = "Ajuda" Help with Advanced Search = "Ajuda com a Busca Avançada" @@ -454,6 +475,7 @@ history_results = "Resultados" history_save = "Gravar?" history_save_link = "Gravar" history_saved_searches = "Buscas Guardadas" +history_schedule = "Programação de alerta" history_search = "Busca" history_time = "Hora" hold_available = "DisponÃvel para levantamento" @@ -467,7 +489,7 @@ hold_cancel_success_items = "%%count%% reserva(s) foram canceladas com sucesso" hold_date_invalid = "Por favor, introduza uma data válida" hold_date_past = "Por favor, introduza uma data no futuro" hold_empty_selection = "Não selecionou nenhuma reserva" -hold_error_blocked = "Os seus privilégios não lhe permitem colocar uma reserva sobre este exemplar" +hold_error_blocked = "Os seus privilégios não lhe permitem realizar uma reserva sobre este item" hold_error_fail = "O seu pedido não foi bem-sucedido; por favor, contate o Balcão de Atendimento para ajuda adicional" hold_invalid_pickup = "Não introduziu um local de levantamento válido; por favor, tente novamente" hold_invalid_request_group = "Um pedido inválido foi digitado. Por favor tente novamente" @@ -491,7 +513,7 @@ Holds = "Reservas" Holds and Recalls = "Reservas e Pedidos" Home = "InÃcio" home_browse = "Navegar por" -HTML Full Text = "Texto completo formato HTML" +HTML Full Text = "Texto completo no formato HTML" Identifier = "Identificador" ill_request_available = "DisponÃvel para levantamento" ill_request_cancel = "Cancelar pedido de débito interbiblioteca" @@ -544,10 +566,10 @@ institutional_login_desc = "Entre com o usuário e senha do campus." Instructor = "Professor" Interlibrary Loan Requests = "Pedido de Débito Interbiblioteca" Internet = "Internet" -Invalid Patron Login = "Login Inválido do Usuário" +Invalid Patron Login = "Login de usuário inválido" Invalid phone number. = "Número de telefone inválido." -Invalid Recipient Email Address = "Email inválido de destinatário" -Invalid Sender Email Address = "Email inválido de Remetente" +Invalid Recipient Email Address = "E-mail de destinatário inválido" +Invalid Sender Email Address = "E-mail de remetente inválido" ISBN = "ISBN" ISBN/ISSN = "ISBN/ISSN" ISSN = "ISSN" @@ -600,6 +622,7 @@ Library Web Search = "Buscar na Web da Biblioteca" library_card_edit_password_placeholder = "Nova Senha" lightbox_error = "Erro: janela de pop-ups bloqueada" Limit To = "Limitar a" +Link to full results = "Link para resultados completos" List = "Lista" List Tags = "Listar Tags" list_access_denied = "Você não tem permissão para ver esta lista." @@ -662,9 +685,12 @@ New Item Feed = "Feed de novos itens" New Item Search = "Procurar novos itens" New Item Search Results = "Resultados da Busca de novos itens" New Items = "Novos itens" +New results found for search = "Novos resultados encontrados para pesquisa" New Title = "Novo tÃtulo" +new_email_success = "O seu endereço de e-mail foi alterado com sucesso" new_password = "Nova Senha" new_password_success = "Sua Senha foi alterada com sucesso" +new_results_heading = "%%count%% resultados mais recentes" new_user_welcome_subject = "Sua nova conta na %%library%%" new_user_welcome_text = "Bem vindo a %%library%%. Uma nova conta foi criada para %%firstname%% %%lastname%%. sua identificação é %%username%%. Por favor, coloque uma senha nesta páginga: %%url%%" Newspaper = "Jornal" @@ -680,6 +706,7 @@ No Preference = "Sem preferência" No reviews were found for this record = "Este registro não tem comentários inseridos" No Tags = "Sem tags" no_description = "Descrição não disponÃvel." +no_email_address = "Endereço de e-mail ausente." no_items_selected = "Nenhum item selecionado" nohit_active_filters = "Uma ou mais filtros de facetas têm sido aplicados a esta busca. Se você remover os filtros, você pode recuperar mais resultados" nohit_change_tab = "Você tem buscado na "%%activeTab%%" aba. Você pode encontrar algo em uma das outras abas:" @@ -731,6 +758,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "Servidor OAI" Occupation = "Ocupação" +od_account_noaccess = "Este cartão de biblioteca não tem acesso ao conteúdo no Overdrive" +od_account_problem = "Há um problema com a sua conta. %%message%%" +od_audiobook-mp3 = "Ãudiolivro MP3" +od_audiobook-overdrive = "Ouvir áudiolivro OverDrive" +od_avail_avail = "DisponÃvel:" +od_avail_holds = "Reservas:" +od_avail_total = "Total de cópias:" +od_but_cancel_hold = "Cancelar esta reserva" +od_but_checkout = "Empréstimo através de Overdrive" +od_but_checkout_s = "Empréstimo" +od_but_gettitle = "Baixe este conteúdo" +od_but_gettitle_s = "Baixar" +od_but_hold = "Faça uma reserva atráves do Overdrive" +od_but_hold_s = "Reservas" +od_but_return = "Devolva este tÃtulo" +od_cancel_hold = "Cancelar reserva de Overdrive" +od_checkout = "Empréstimo Overdrive" +od_code_connection_failed = "Conexão com o Overdrive falhou. Se o problema persistir, por favor entre em contato com sua biblioteca." +od_code_contentnotavail = "Este conteúdo não está disponÃvel em sua área." +od_code_login_for_avail = "Login para disponÃbilidade" +od_code_resource_not_found = "TÃtulo não disponÃvel" +od_content = "Conteúdo Overdrive" +od_dl_formats = "Formatos de Download Suportados" +od_docheckout_failure = "Este tÃtulo não pôde ser emprestado." +od_docheckout_success = "Este tÃtulo foi emprestado para você. Expira em %%expireDate%%" +od_early_return = "Devolução antecipada Overdrive" +od_ebook-epub-adobe = "Adobe EPUB eBook" +od_ebook-epub-open = "Open EPUB eBook" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader eBook" +od_ebook-overdrive = "Leia eBook OverDrive" +od_ebook-pdf-adobe = "EBook Adobe PDF" +od_ebook-pdf-open = "Abra o eBook em PDF" +od_expires_on = "Este tÃtulo expira em %%due_date%%." +od_get_title = "Baixar Overdrive" +od_gettitle_failure = "Este tÃtulo não pôde ser baixado." +od_help_linktext = "Ajuda Overdrive" +od_history = "Histórico Overdrive" +od_hold = "Reserva Overdrive" +od_hold_cancel_failure = "A solicitação de cancelamento de reserva falhou." +od_hold_cancel_success = "A reserva foi cancelada com sucesso." +od_hold_email = "Endereço de e-mail para notificação de reserva: %%holdEmailAddress%%." +od_hold_now_avail = "Esta reserva está disponÃvel para empréstimo. O tempo se expira em %%expireDate%%." +od_hold_place_failure = "A solicitação de reserva falhou." +od_hold_place_success = "Este tÃtulo foi colocado em reserva. Sua posição de reserva é %%holdListPosition%%" +od_hold_placed_on = "Reserva colocada em %%holdPlacedDate%%." +od_hold_queue = "Posição %%holdPosition%% de %%numberOfHolds%% na fila de reservas." +od_holds = "Reservas Overdrive" +od_info_unavail = "Esta informação está atualmente indisponÃvel." +od_is_checkedout = "Você tem esse tÃtulo emprestado. Fim do empréstimo em %%due_date%%." +od_is_on_hold = "Você tem esse tÃtulo em reserva." +od_loans = "Empréstimos Overdrive" +od_mycontent_help = "Para obter informações de como realizar o download desses tÃtulos, consulte <a href="%%url%%">Ajuda Overdrive</a>." +od_none_found = "Nenhum tÃtulo econcontrado." +od_return_failure = "Este tÃtulo não pôde ser devolvido." +od_return_success = "Este tÃtulo foi devolvido." +od_video-streaming = "streaming de arquivo de vÃdeo" of_num_results = "#%%position%% de %%total%% resultados" old_password = "Senha Antiga" On Reserve = "Reservado" @@ -760,6 +844,7 @@ pagination_label = "Paginação" Password = "Senha" Password Again = "Senha, novamente" Password cannot be blank = "A senha não pode estar em branco" +password_error_auth_old = "A senha usada anteriormente é inválida" password_error_invalid = "Nova senha é inválida (ex: contêm caracteres inválidos)" password_error_not_unique = "Senha não foi alterada" password_maximum_length = "O tamanho máximo da senha é %%maxlength%% caracteres" @@ -769,9 +854,9 @@ password_only_numeric = "Somente números" Passwords do not match = "As senhas não coincidem." past_days = "Últimos %%range%% Dias" PDF Full Text = "Texto Completo em Formato PDF" -peer_reviewed = "Revisados pelos Pares" +peer_reviewed = "Avaliados pelos Pares" peer_reviewed_limit = "Limitar a artigos de revistas e jornais" -permission_denied = "Você requereu uma página ou ação, mas você não tem a permissão necessária." +permission_denied = "Você solicitou uma página ou ação, mas você não tem a permissão necessária." permission_denied_title = "Permissão negada" Phone Number = "Número do Telefone" Photo = "Foto" @@ -808,7 +893,7 @@ Publication Frequency = "Regularidade de publicação" Publication Information = "Informação da Publicação" Publication Type = "Tipo de Publicação" Publication Year = "Ano de Publicação" -Publication_Place = "Place of Publication" +Publication_Place = "Local de Publicação" Published = "Publicado em" Published in = "Publicado no" Publisher = "Editor" @@ -825,16 +910,17 @@ Read the full review online... = "Leia a análise completa linha ..." Recall This = "Reservar" recaptcha_not_passed = "Código CAPTCHA não coincidiu" recently_returned_channel_title = "Recentemente Devolvidos" +recommend_links_text = "Você também pode tentar:" Record Citations = "Citações do registro" Record Count = "Número de Registros" Record Type = "Tipo de registro" Recover Account = "Recuperar Conta" -recovery_by_email = "Recuperar por email" +recovery_by_email = "Recuperar por e-mail" recovery_by_username = "Recuperar pela identificação do usuário" recovery_disabled = "Recuperação de senha não disponÃvel" recovery_email_notification = "Um pedido foi feito para recuperar a senha para a conta com %%library%%." -recovery_email_sent = "Instruções para recuperação de senha foi enviada para o endereço de Email registrado na conta." -recovery_email_subject = "Recuperação de Conta VuFind" +recovery_email_sent = "Instruções para recuperação de senha foi enviada para o endereço de e-mail registrado na conta." +recovery_email_subject = "Recuperação de Conta do VuFind" recovery_email_url_pretext = "Por favor navegue para esta URL para colocar uma nova senha: %%url%%" recovery_expired_hash = "Este Link de recuperação está expirado" recovery_invalid_hash = "Link de recuperação não reconhecido" @@ -842,17 +928,18 @@ recovery_new_disabled = "Não é permitido a troca de senha neste momento" recovery_title = "Recuperação de Senha" recovery_too_soon = "Muitos pedidos de recuperação feitos, por favor, tente mais tarde" recovery_user_not_found = "Conta não encontrada" -rectangle_center_message = "Este é o ponto centra do retângul marcado" +rectangle_center_message = "Este é o ponto central do retângulo marcado" +Reference Material = "Material de Referência" Refine Results = "Refinar Resultados" Region = "Região" relais_available = "Este item está disponÃvel por meio do empréstimo entre bibliotecas. Gostaria de requisitá-lo?" relais_checking = "Verificando disponibilidade..." -relais_error_html = "Houve um problema com seu pedido. Clique em <a href="%%url%%" target='new'>here</a> para pedir esse item usando o site de empréstimo entre bibliotecas." +relais_error_html = "Houve um problema com seu pedido. Clique em <a href="%%url%%" target='new'>aqui</a> para pedir esse item usando o site de empréstimo entre bibliotecas." relais_request = "Pedido de Empréstimo entre Bibliotecas" relais_requesting = "Pedindo..." relais_search = "Buscar Emprestimo entre Bibliotecas" relais_success_label = "Confirmação:" -relais_success_message = "Identificação do pedido #%%id%% foi criado. Você receberá uma confirmação por E-mail." +relais_success_message = "Identificação do pedido #%%id%% foi criada. Você receberá uma confirmação por e-mail." Related Author = "Autor relacionado" Related Items = "Itens Relacionados" Related Subjects = "Assuntos relacionados" @@ -883,6 +970,7 @@ Requests = "Pedidos" Reserves = "Reservas" Reserves Search = "Busca de Reservas" Reserves Search Results = "Resultados da Busca de Reservas" +reset_filters_button = "Redefinir Filtros" result_checkbox_label = "Selcionar número de resultado %%number%%" result_count = "%%count%% resultados" Results = "Resultados" @@ -898,6 +986,11 @@ Save Comment = "Gravar comentário" save_search = "Salvar a busca" save_search_remove = "Apagar a busca guardada" Saved in = "Na minha lista" +schedule_daily = "Diariamente" +schedule_explanation = "Receber e-mails de alerta sobre novos resultados para pesquisa." +schedule_none = "Nenhum" +schedule_weekly = "Semanal" +Scheduled Alert Results = "Resultados de alerta programados" scholarly_limit = "Limitar a artigos de periódicos cientÃficos" Scroll to Load More = "Navegue para baixo para carregar mais" Search = "Busca" @@ -912,6 +1005,7 @@ search results of = "Resultados da busca de" Search Tips = "Dicas de Busca" Search Tools = "Ferramentas de busca" Search Type = "Tipo de Busca" +Search within collection = "Pesquisar na coleção" search_AND = "TODOS os termos" search_groups = "Grupos de Busca" search_match = "correspondência da busca" @@ -937,19 +1031,20 @@ Sensor Image = "Sensor de Imagem" Serial = "Periódico" Series = "coleção" Set = "Definir" -showing_items_html = "A mostrar <strong>%%start%% - %%end%%</strong> Registros" -showing_items_of_html = "A mostrar <strong>%%start%% - %%end%%</strong> de <strong>%%total%%</strong> Registros" -showing_results_for_html = "A mostrar <strong>%%start%% - %%end%%</strong> resultados para a busca '<strong>%%lookfor%%</strong>'" -showing_results_html = "A mostrar <strong>%%start%% - %%end%%</strong> resultados" -showing_results_of_for_html = "A mostrar <strong>%%start%% - %%end%%</strong> resultados de <strong>%%total%%</strong> para a busca '<strong>%%lookfor%%</strong>'" -showing_results_of_html = "A mostrar <strong>%%start%% - %%end%%</strong> resultados de <strong>%%total%%</strong>" -sidebar_close = "ocultar barra lateral" +show_filters_html = "Mostrar filtros (%%count%%)" +showing_items_html = "Mostrando <strong>%%start%% - %%end%%</strong> Registros" +showing_items_of_html = "Mostrando <strong>%%start%% - %%end%%</strong> de <strong>%%total%%</strong> Registros" +showing_results_for_html = "Mostrando <strong>%%start%% - %%end%%</strong> resultados para a busca '<strong>%%lookfor%%</strong>'" +showing_results_html = "Mostrando <strong>%%start%% - %%end%%</strong> resultados" +showing_results_of_for_html = "Mostrando <strong>%%start%% - %%end%%</strong> resultados de <strong>%%total%%</strong> para a busca '<strong>%%lookfor%%</strong>'" +showing_results_of_html = "Mostrando <strong>%%start%% - %%end%%</strong> resultados de <strong>%%total%%</strong>" +sidebar_close = "Ocultar barra lateral" sidebar_expand = "Expandir barra lateral" Similar Items = "Registros relacionados" Skip to content = "Pular para o conteúdo" skip_confirm = "Tem certeza de que quer pular esta etapa?" skip_fix_metadata = "Não corrigir os metadados no momento." -skip_step = "Saltar esta etapa" +skip_step = "Pular esta etapa" Slide = "Slides" sms_failure = "Erro: Não foi possÃvel enviar a mensagem." sms_phone_number = "Número de telefone " @@ -984,6 +1079,7 @@ Start a new Basic Search = "Iniciar uma nova Busca Básica" Start Page = "Página inicial" starting from = "Com inÃcio em:" Status = "Estado" +status_transit = "Em trânsito" status_unknown_message = "Informação em tempo real indisponÃvel" Storage Retrieval Requests = "Pedido de Recuperação Armazenado" storage_retrieval_request_available = "DisponÃvel para levantamento" @@ -1053,13 +1149,14 @@ test_fix = "Corrigir" test_ok = "OK" Text this = "Enviar por SMS" Thank you for your feedback. = "Obrigado por ter compartilhado a sua opinião." -That email address is already used = "Endereço de email já em uso" -That username is already taken = "Nome do Usuário já em uso" +That email address is already used = "Endereço de e-mail já em uso" +That username is already taken = "Nome de usuário já em uso" The record you selected is not part of any of your lists. = "O registro selecionado não faz parte de nenhuma das listas." The record you selected is not part of the selected list. = "O registro selecionado não faz parte da lista selecionada." The system is currently unavailable due to system maintenance = "O sistema está indisponÃvel devido a manutenção do sistema" Theme = "Tema" -This email was sent from = "Email enviado a partir de" +Thesis = "Tese" +This email was sent from = "E-mail enviado a partir de" This field is required = "Campo obrigatório" This item is already part of the following list/lists = "Este exemplar já faz parte da seguinte lista(s)" This result not is displayed to guests = "Este resultado não é apresentado para usuários convidados (não logados)." @@ -1068,7 +1165,7 @@ Title not available = "TÃtulo não disponÃvel" Title View = "Visualização do TÃtulo" title_hold_place = "Coloque um pedido de TÃtulo" To = "Para" -Too Many Email Recipients = "Excesso de emails destinatário" +Too Many Email Recipients = "Excesso de e-mails destinatário" too_many_favorites = "Essa lista é muito grande para mostrar tudo de uma vez. Tente reorganizar seus favoritos em mais listas ou limite o uso de tags." too_many_new_items = "Há muitos novos registros para mostrar numa única lista. Tente limitar a sua busca." too_many_reserves = "Há demasiadas obras recomendadas para mostrar numa única lista. Tente limitar a sua busca." @@ -1089,6 +1186,9 @@ unique_tags = "Tags únicas" University Library = "Biblioteca Universitária" Unknown = "online" unrecognized_facet_label = "Outro" +unsubscribe_confirmation = "Deseja cancelar a assinatura de e-mail?" +unsubscribe_description = "Não deseja receber esta mensagem no futuro? Cancele a assinatura usando o seguinte link" +unsubscribe_successful = "Assinatura cancelada" Upgrade VuFind = "Upgrade do Vufind" upgrade_description = "Se está atualizando uma versão anterior do VuFind, você pode carregar as configurações antigas com esta ferramenta." URL = "URL" @@ -1098,6 +1198,14 @@ User Account = "conta do usuário" Username = "Login" Username cannot be blank = "O login não pode estar em branco" Username is already in use in another library card = "Identificação já utilizada em outro cartão de biblioteca" +verification_done = "Seu endereço de e-mail foi confirmado com sucesso." +verification_email_change_sent = "As instruções de verificação do endereço de e-mail foram enviadas para o novo endereço de e-mail. Você deve verificar o endereço antes que a alteração tenha efeito." +verification_email_notification = "Foi feita uma solicitação para confirmar seu endereço de e-mail da sua conta com %%library%%." +verification_email_sent = "As instruções para confirmação de endereço de e-mail foram enviadas para o endereço de e-mail registrado nesta conta." +verification_email_subject = "VuFind - E-mail de confirmação" +verification_email_url_pretext = "Você pode confirmar seu endereço de e-mail neste URL: %%url%%" +verification_too_soon = "Seu e-mail requer confirmação. Recentemente, foi enviado uma mensagem para o seu endereço de e-mail registrado. Se você não recebeu, aguarde alguns minutos e tente novamente." +verification_user_not_found = "Não foi possÃvel encontrar sua conta" VHS = "VHS" Video = "VÃdeo" Video Clips = "Video Clipes" @@ -1130,12 +1238,12 @@ with_selected = "selecionados" Year of Publication = "Ano de Publicação" Yesterday = "Ontem" You do not have any fines = "Você não tem quaisquer multas" -You do not have any holds or recalls placed = "Você não tem qualquer reserva ou pedidos colocados" +You do not have any holds or recalls placed = "Você não tem qualquer reserva ou pedidos" You do not have any interlibrary loan requests placed = "Você não tem quaisquer pedidos de empréstimo entre interbibliotecas" You do not have any items checked out = "Você não tem nenhum exemplar emprestado" You do not have any library cards = "Você não tem nenhum cartão de biblioteca" You do not have any saved resources = "Você não possui recursos salvos" -You do not have any storage retrieval requests placed = "Você não tem quaisquer pedidos de recuperação armazenamento colocados" +You do not have any storage retrieval requests placed = "Você não tem quaisquer pedidos de recuperação armazenamento" You must be logged in first = "Você deve entrar na sua conta antes" Your Account = "A sua conta" Your book bag is empty = "a sua cesta de livros está vazia" diff --git a/languages/pt.ini b/languages/pt.ini index 42f04ad733a1256b3d01957d1a41934fa2a41fb3..2532222d5a30eb28f7cd1b7648860b57bf105adc 100644 --- a/languages/pt.ini +++ b/languages/pt.ini @@ -83,7 +83,6 @@ Back to Search Results = "Voltar à lista de resultados" Backtrace = "Caminho" Bag = "Cesto" Balance = "Balanço" -basic_search_keep_filters = "Restringir a pesquisa actual / manter filtros" Be the first to leave a comment = "Seja o primeiro a partilhar um comentário" Be the first to tag this record = "seja o primeiro a adicionar uma tag" Bibliographic Details = "Detalhes bibliográficos" @@ -336,7 +335,6 @@ Favorites = "Favoritos" Fee = "Multas" Feedback = "Comentário" feedback_email = "Email" -feedback_login_required = "Você deve entrar na sua conta antes." feedback_name = "Nome" Filter = "Filtro" filter_tags = "Filtro de Trags" diff --git a/languages/ru.ini b/languages/ru.ini index 2bf4847d34e9a64d8d140705c10b3c361087d17b..ea53b1ffa8f49ad8731010f28bfd9164766769dc 100644 --- a/languages/ru.ini +++ b/languages/ru.ini @@ -106,7 +106,6 @@ Back to Search Results = "ВернутьÑÑ Ðº результатам Ð¿Ð¾Ð¸Ñ Backtrace = "ОтÑлеживание" Bag = "Ðабор" Balance = "БаланÑ" -basic_search_keep_filters = "Сохраните мои текущие фильтры" Be the first to leave a comment = "Ваш комментарий будет первым" Be the first to tag this record = "ТребуетÑÑ 1-Ð°Ñ Ð¼ÐµÑ‚ÐºÐ° запиÑи" Bibliographic Details = "БиблиографичеÑкие подробноÑти" @@ -367,7 +366,6 @@ Favorites = "Избранное" Fee = "взноÑ" Feedback = "обратной ÑвÑзи" feedback_email = "Email" -feedback_login_required = "Сначала войдите в ÑиÑтему." feedback_name = "ИмÑ" File Description = "ОпиÑание файла" Filter = "Фильтр" diff --git a/languages/sl.ini b/languages/sl.ini index aabb4cc9f190eb203c11d929588ef53c116b0c93..22fd6f2ba653d3476644e2d1da4f5643526f03e9 100644 --- a/languages/sl.ini +++ b/languages/sl.ini @@ -75,7 +75,6 @@ Back to Search Results = "Nazaj na iskanje" Backtrace = "Sledi nazaj" Bag = "KoÅ¡arica" Balance = "Saldo" -basic_search_keep_filters = "Ohrani moje filtre" Be the first to leave a comment = "Komentirajte kot prvi" Be the first to tag this record = "prvi oznaÄite" Bibliographic Details = "Bibliografske podrobnosti" @@ -292,7 +291,6 @@ Favorites = "Priljubljene" Fee = "ÄŒlanarina/zamudnina" Feedback = "PiÅ¡ite nam" feedback_email = "Email" -feedback_login_required = "Najprej se morate prijaviti." feedback_name = "Ime" Filter = "Filter" filter_tags = "Filter oznake" diff --git a/languages/sv.ini b/languages/sv.ini index 1bff70336945092ad1d35c74707a66293d8ce3d2..c57b637179013236619f5cb725204b7786b48d70 100644 --- a/languages/sv.ini +++ b/languages/sv.ini @@ -30,7 +30,7 @@ Address = "Adress" adv_search_all = "Alla fält" adv_search_author = "Upphovsman" adv_search_callnumber = "Signum" -adv_search_filters = "Filter i bruk" +adv_search_filters = "Begränsningar i bruk" adv_search_isn = "ISBN/ISSN" adv_search_journaltitle = "Tidskriftstitel" adv_search_label = "Med sökorden" @@ -49,6 +49,7 @@ advSearchError_notFound = "Sökningen hittas inte." ajax_load_interrupted = "Laddning avbruten" ajaxview_label_information = "Information" ajaxview_label_tools = "Verktyg" +alert_email_address = "Sökbevakningens resultat skickas till e-postadressen" All = "Alla" All Fields = "Alla fält" All Pages Loaded = "Alla sidor laddade" @@ -58,6 +59,7 @@ alphabrowselink_html = "Bläddra efter %%index%% frÃ¥n <a href="%%url%%">%%from% An error has occurred = "Det har uppstÃ¥tt ett fel" An error occurred during execution; please try again later. = "Utförandet misslyckades. Försök pÃ¥ nytt efter en stund." AND = "OCH" +and = "och" anonymous_tags = "Anonyma taggar" APA Citation = "APA-referens" applied_filter = "Filter i bruk" @@ -70,6 +72,7 @@ authentication_error_admin = "Inloggningen misslyckades. Kontakta systemadminist authentication_error_blank = "Användarnamn/lösenord kan inte vara tomt." authentication_error_creation_blocked = "Du har inte tillstÃ¥nd att skapa ett konto." authentication_error_denied = "Användarnamn/lösenord stämmer inte! Ingen tillgÃ¥ng till systemet." +authentication_error_email_not_verified_html = "Din e-postadress har inte ännu verifierats. Kontrollera din skräppostmap för verifieringsmeddelandet. Du kan ocksÃ¥ begära <a href="%%url%%">ett nytt verifieringsmeddelande</a>." authentication_error_invalid = "Användarnamn/lösenord stämmer inte. Försök igen." authentication_error_loggedout = "Du har loggat ut." authentication_error_technical = "Inloggningen misslyckades. Försök igen efter en stund." @@ -95,7 +98,6 @@ Backtrace = "Anropsstacken" Bag = "Korg" Balance = "Att betala" Barcode = "Streckkod" -basic_search_keep_filters = "Spara filtreringen i bruk" Be the first to leave a comment = "Lägg till första kommentaren" Be the first to tag this record = "Lägg till första taggen" Bibliographic Details = "Bibliografiska uppgifter" @@ -165,6 +167,7 @@ Call Number = "Signum" callnumber_abbrev = "Signum" Cannot find record = "Posten hittas inte" Cannot find similar records = "Inga liknande poster hittades" +cannot set = "Kan inte sätta" Cassette = "Kassett" cat_establish_account = "Ange följande uppgifter för att hämta din användarinformation:" cat_password_abbrev = "Lösenord i bibliotekssystemet" @@ -173,7 +176,12 @@ Catalog Login = "Logga in med din bibliotekskort" Catalog Results = "Resultat i katalogen" catalog_login_desc = "Ange din bibliotekskortets nummer och lösenord." CD = "CD" +Change Email Address = "Byta e-postadress" Change Password = "Ändra lösenord" +change_email_disabled = "Det är inte möjligt att byta e-postadress" +change_email_verification_reminder = "Du vill fÃ¥ ett meddelande till den nya adressen. Klicka pÃ¥ länken i meddelandet för att konfirmera ändringen." +change_notification_email_message = "Begäran för att byta e-postadressen har gjorts med %%library%%. Om du inte gjort denna begäran, rekommenderar vi att du loggar in pÃ¥ %%url%% och kollar ditt konto. Vänligen kontakta biblioteket via %%email%% om du har nÃ¥gra frÃ¥gor." +change_notification_email_subject = "Verifiering av din e-postadress" channel_add_more = "Mer liknande kanaler" channel_browse = "Bläddra fler poster" channel_expand = "Utforska relaterade kanaler" @@ -182,9 +190,10 @@ channel_search = "Visa som sökresultat" channel_searchbox_label = "Sök efter fler kanaler:" Check Hold = "Kolla reservering" Check Recall = "Kolla Ã¥terkallelse" -Checked Out = "LÃ¥n" +check_profile = "Kontrollera användaruppgifter." +Checked Out = "UtlÃ¥nad" Checked Out Items = "LÃ¥n" -Checkedout = "LÃ¥n" +Checkedout = "UtlÃ¥nad" Checkout Date = "UtlÃ¥ningsdag" Chicago Citation = "Chicago-stil citat" child_record_count = "%%count%% poster" @@ -296,6 +305,7 @@ Due Date = "Förfallodag" DVD = "DVD" eBook = "E-bok" Edit = "Ändra" +edit = "ändra" Edit Library Card = "Uppdatera uppgifter för anslutet bibliotekskort" Edit this Advanced Search = "Ändra den avancerade sökningen" edit_list = "Ändra listan" @@ -324,8 +334,14 @@ Email address is invalid = "Email address is invalid" Email Record = "Skicka posten per e-post" Email this = "Skicka per e-post" Email this Search = "Skicka sökningen per e-post" +email_change_pending_html = "Du har en väntande e-poständring till %%pending%%. Vänligen klicka pÃ¥ länken i verifieringsmeddelandet för att konfirmera ändringen. Vid behov kan vi <a href="%%url%%">skicka verifieringsmeddelandet igen</a>." email_failure = "Fel - meddelandet kunde inte skickas" email_link = "Länk" +email_login_desc = "Använd följande länk för att logga in. Om du inte begärde inloggning, kan du ignorera det här meddelandet. Observera att länken är giltig en begränsad tid och endast i enheten du angav e-postadressen med." +email_login_link = "Länk till inloggning: <%%url%%>" +email_login_link_sent = "Vi skickade en inloggningslänk till din e-postadress. Det kan ta nÃ¥gon tid tills meddelanden kommer fram. Om du inte fÃ¥r länken inom kort, kontrollera även skräppostmappen." +email_login_requested = "Inloggning har begärts med din e-postadress i %%title%%." +email_login_subject = "Logga in till %%title%%" email_maximum_recipients_note = "Som mest %%max%% mottagare tillÃ¥tna." email_multiple_recipients_note = "Du kan ange flera mottagare separerade med kommatecken." email_selected = "E-posta valda" @@ -335,6 +351,7 @@ email_subject = "Ämne" email_success = "Meddelandet har skickats" Empty = "Tom" Empty Book Bag = "Tömma" +empty_search_disallowed = "Det gÃ¥r inte att söka i nuvarande sökmÃ¥l utan söktermer" Enable Auto Config = "Enable Auto Config" End Page = "Sista sidan" Era = "Tidsperiod" @@ -387,12 +404,13 @@ Favorites = "Favoriter" Fee = "Avgift" Feedback = "Respons" feedback_email = "E-post" -feedback_login_required = "Du mÃ¥ste logga in först." feedback_name = "Ditt namn" Field of activity = "VerksamhetsomrÃ¥de" File Description = "Filbeskrivning" Filter = "Filter" +Filter Collection = "Begränsa samlingen" filter_tags = "Filtrera taggar" +filter_toggle_entries = "%%count%% filter" filter_wildcard = "Any" Find = "Sök" Find More = "Sök mera" @@ -454,6 +472,7 @@ history_results = "Resultat" history_save = "Spara" history_save_link = "Spara" history_saved_searches = "Sparade sökningar" +history_schedule = "Sökbevakning" history_search = "Sökning" history_time = "Tid" hold_available = "Kan avhämtas" @@ -600,6 +619,7 @@ Library Web Search = "Webbsökning" library_card_edit_password_placeholder = "Nytt lösenord" lightbox_error = "Fel: kunde inte öppna ett pop-up-fönster." Limit To = "Begränsa till" +Link to full results = "Länk till hela resultatet" List = "Lista" List Tags = "Lista taggar" list_access_denied = "Du har inte rättigheter att se denna lista." @@ -662,9 +682,12 @@ New Item Feed = "Nyhetsflöde" New Item Search = "Sök nyheter i katalogen" New Item Search Results = "Nyhetssökningens resultat" New Items = "Nytt i katalogen" +New results found for search = "Nya resultat för sökning" New Title = "Ny titel" +new_email_success = "Din e-postadress har ändrats" new_password = "Nytt lösenord" new_password_success = "Lösenordet har ändrats" +new_results_heading = "%%count%% nyaste resultat" new_user_welcome_subject = "Ditt nytt konto i %%library%%" new_user_welcome_text = "Välkommen till %%library%%. En ny konto har skapats för %%firstname%% %%lastname%%. Ditt användarnamn är %%username%%. Vänligen lägg till ett lösenord pÃ¥ denna sidan: %%url%%" Newspaper = "Tidning" @@ -680,6 +703,7 @@ No Preference = "Vilken som helst" No reviews were found for this record = "Inga recensioner gällande denna post" No Tags = "Inga taggar" no_description = "Ingen beskrivning tillgänglig." +no_email_address = "E-postadress saknas." no_items_selected = "Inga valda poster." nohit_active_filters = "Filtren i bruk i denna sökning. Om du tar bort filtren, kan du fÃ¥ fler resultat." nohit_change_tab = "Du har sökt i fliken "%%activeTab%%". Du kan hitta nÃ¥got i en av de andra flikar:" @@ -731,6 +755,63 @@ number_decimal_point = "," number_thousands_separator = " " OAI Server = "OAI Servern" Occupation = "Yrke" +od_account_noaccess = "Detta bibliotekskort har inte tillgÃ¥ng till materialet i Overdrive" +od_account_problem = "Det finns ett problem med ditt konto. %%message%%" +od_audiobook-mp3 = "MP3 ljudbok" +od_audiobook-overdrive = "OverDrive Listen ljudbok" +od_avail_avail = "Tillgänglig:" +od_avail_holds = "Reserveringar:" +od_avail_total = "Antal exemplar:" +od_but_cancel_hold = "Annullera denna reservering" +od_but_checkout = "UtlÃ¥na via Overdrive" +od_but_checkout_s = "UtlÃ¥na" +od_but_gettitle = "Ladda ner materialet" +od_but_gettitle_s = "Ladda net" +od_but_hold = "Reservera via Overdrive" +od_but_hold_s = "Reservera" +od_but_return = "Returnera denna titel" +od_cancel_hold = "Annullera reservering via Overdrive" +od_checkout = "Overdrive-utlÃ¥ning" +od_code_connection_failed = "Anslutning till Overdrive misslyckades. Kontakta biblioteket om problemet kvarstÃ¥r." +od_code_contentnotavail = "Detta material är inte tillgängligt via Overdrive." +od_code_login_for_avail = "Logga in för att kolla tillgängligheten" +od_code_resource_not_found = "Titeln hittades inte" +od_content = "Overdrive-material" +od_dl_formats = "Stödda nedladdningsformat" +od_docheckout_failure = "Titeln kunde inte utlÃ¥nas." +od_docheckout_success = "Denna titel har utlÃ¥nats för dig. Förfallodagen är %%expireDate%%" +od_early_return = "Overdrive tidig returnering" +od_ebook-epub-adobe = "Adobe EPUB e-bok" +od_ebook-epub-open = "Open EPUB e-bok" +od_ebook-kindle = "Kindle Book" +od_ebook-mediado = "MediaDo Reader e-bok" +od_ebook-overdrive = "OverDrive Read e-bok" +od_ebook-pdf-adobe = "Adobe PDF e-bok" +od_ebook-pdf-open = "Open PDF e-bok" +od_expires_on = "Denna titel löper ut %%due_date%%." +od_get_title = "Overdrive-nerladdning" +od_gettitle_failure = "Titeln kunde inte laddas ner." +od_help_linktext = "Hjälp med Overdrive" +od_history = "Overdrive-historiken" +od_hold = "Overdrive-reservering" +od_hold_cancel_failure = "Reserveringen kunde inte annulleras. " +od_hold_cancel_success = "Reserveringen har annullerats." +od_hold_email = "E-postadress för reserveringsmeddelandet: %%holdEmailAddress%%." +od_hold_now_avail = "Denna reservering är tillgänglig för utlÃ¥ning. Förfallodagen är %%expireDate%%." +od_hold_place_failure = "Reserveringsbegäran misslyckades." +od_hold_place_success = "Titeln har reserverats. Din placering i kön är %%holdListPosition%%" +od_hold_placed_on = "Reserverad %%holdPlacedDate%%." +od_hold_queue = "Placering %%holdPosition%% av %%numberOfHolds%% i reserveringskön." +od_holds = "Overdrive-reserveringar" +od_info_unavail = "Ingen data tillgänglig" +od_is_checkedout = "Du har denna titel utlÃ¥nad. Förfallodagen är %%due_date%%." +od_is_on_hold = "Du har reserverad titeln." +od_loans = "Overdrive-lÃ¥n" +od_mycontent_help = "För information och hjälp med nedladdning av dessa titel, se <a href="%%url%%">Overdrive Help</a>." +od_none_found = "Inga resultat." +od_return_failure = "Titeln kunde inte returneras." +od_return_success = "Titeln har returnerats." +od_video-streaming = "strömmande video" of_num_results = "%%position%% av %%total%% resultat" old_password = "Gamla lösenordet" On Reserve = "I begränsad användning" @@ -825,6 +906,7 @@ Read the full review online... = "Läs hela recensionen online..." Recall This = "Reservera denna" recaptcha_not_passed = "CAPTCHA inte klarat" recently_returned_channel_title = "Nyligen returnerad" +recommend_links_text = "Du kan försöka ocksÃ¥:" Record Citations = "Hänvisningar gällande denna post" Record Count = "Antal poster" Record Type = "Posttyp" @@ -898,6 +980,11 @@ Save Comment = "Spara kommentar" save_search = "Spara sökningen" save_search_remove = "Radera den sparade sökningen" Saved in = "Sparad" +schedule_daily = "Dagligen" +schedule_explanation = "FÃ¥ e-post om nya resultat för denna sökning." +schedule_none = "Ingen" +schedule_weekly = "En gÃ¥ng per vecka" +Scheduled Alert Results = "Resultat av sökbevakning" scholarly_limit = "Begränsa till artiklar i vetenskapliga tidskrifter" Scroll to Load More = "Bläddra för att ladda mer" Search = "Sökning" @@ -912,6 +999,7 @@ search results of = "sammanlagt" Search Tips = "Söktips" Search Tools = "Sökverktyg" Search Type = "Sökningstyp" +Search within collection = "Sök frÃ¥n samlingen" search_AND = "Alla söktermer (AND)" search_groups = "Sökgrupper" search_match = "Sök" @@ -937,6 +1025,7 @@ Sensor Image = "Fjärranalysbild" Serial = "Periodisk publikation" Series = "Serie" Set = "Justera" +show_filters_html = "Visa begränsningar (%%count%%)" showing_items_html = "Visas resultat <strong>%%start%% - %%end%%</strong>" showing_items_of_html = "Visas <strong>%%start%% - %%end%%</strong> av <strong>%%total%%</strong> resultat" showing_results_for_html = "Visas resultat <strong>%%start%% - %%end%%</strong> för sökning '<strong>%%lookfor%%</strong>'" @@ -984,6 +1073,7 @@ Start a new Basic Search = "Gör en ny enkel sökning" Start Page = "Förstasidan" starting from = "FrÃ¥n och med" Status = "Status" +status_transit = "PÃ¥ väg" status_unknown_message = "Status otillgänglig" Storage Retrieval Requests = "Magasinsbeställningar" storage_retrieval_request_available = "Kan avhämtas" @@ -1089,6 +1179,9 @@ unique_tags = "Unika taggar" University Library = "Universitetsbibliotek" Unknown = "Okänd" unrecognized_facet_label = "Annan" +unsubscribe_confirmation = "Vill du säkert avsluta prenumerationen?" +unsubscribe_description = "Önskar du inte fÃ¥ detta meddelande i fortsättningen? Avsluta prenumerationen via nedanstÃ¥ende länk" +unsubscribe_successful = "Prenumerationen avslutad" Upgrade VuFind = "Uppgradera VuFind" upgrade_description = "Om du uppgraderar en tidigare Vufind version, kan du ladda dina gamla inställningar med detta verktyg." URL = "URL" @@ -1098,6 +1191,14 @@ User Account = "Användarkonto" Username = "Användarnamn / streckkod" Username cannot be blank = "Användarnamn kan inte vara tomt" Username is already in use in another library card = "Användarnamnet är redan i bruk för ett annat bibliotekskort" +verification_done = "Din e-postadress har verifierats." +verification_email_change_sent = "Intruktioner för att verifiera adressen har skickats till den nya e-postadressen. Du mÃ¥ste verifiera adressen innan den tar effekt." +verification_email_notification = "Begäran för att verifiera din e-postadress med %%library%% har skickats." +verification_email_sent = "Intruktioner för att verifiera din e-postadress har skickats till e-postadressen registrerad med ditt konto." +verification_email_subject = "Verifiering av e-postadress i VuFind" +verification_email_url_pretext = "Du kan verifiera din e-postadress med detta länk: %%url%%" +verification_too_soon = "Din e-postadress kräver verifiering. En meddelande har skickats till din e-postadress. Om du inte fÃ¥r meddelanden, var god försök pÃ¥ nytt efter en stund." +verification_user_not_found = "Ditt konto hittades inte" VHS = "VHS" Video = "Video" Video Clips = "Videoklipp" diff --git a/languages/tr.ini b/languages/tr.ini index 0fce69ff68a08ce241453f7f905423fb14779c97..5977d7e7de66a8d57116af25020b5ff5811ec531 100644 --- a/languages/tr.ini +++ b/languages/tr.ini @@ -61,6 +61,7 @@ advSearchError_notFound = "Ä°stemiÅŸ olduÄŸunuz tarama bulunamadı." ajax_load_interrupted = "Yükleme yarıda kesildi" ajaxview_label_information = "Bilgi" ajaxview_label_tools = "Araçlar" +alert_email_address = "Planlanmýþ uyarý sonuçlarý e-posta adresine gönderilecek" All = "Tümü" All Fields = "Tüm Alanlar" All Pages Loaded = "Bütün Sayfalar Yüklendi" @@ -70,6 +71,7 @@ alphabrowselink_html = "<a href="%%url%%">%%from%%</a> baÅŸlayarak %%index%% tar An error has occurred = "Bir Hata Oldu" An error occurred during execution; please try again later. = "Yürütme sırasında bir hata oluÅŸtu; lütfen tekrar deneyin." AND = "VE" +and = "ve" anonymous_tags = "Anonim Etiketler" APA Citation = "APA Alıntı" applied_filter = "Uygulanmış Flitre" @@ -82,6 +84,8 @@ authentication_error_admin = "Yönetici GiriÅŸi Onaylanmadı" authentication_error_blank = "BoÅŸ Yönetici GiriÅŸi Hatası" authentication_error_creation_blocked = "Bir hesap oluÅŸturmak için izniniz yok." authentication_error_denied = "Onaylanmadı" +authentication_error_email_not_verified_html = "E-posta adresiniz henüz doÄŸrulanmadı. Lütfen doÄŸrulama mesajı için spam filtrenizi ve kutusunu kontrol ediniz. EÄŸer gerekli ise, <a href="%%url%%"> baÄŸlantısından doÄŸrulama e-postası göderebiliriz.</a>." +authentication_error_in_progress = "Kimlik doðrulama isteði zaten iþleniyor. Yeniden baþlamanýz gerekiyorsa lütfen daha sonra tekrar deneyin." authentication_error_invalid = "Geçersiz giriÅŸ -- Lütfen tekrar deneyin" authentication_error_loggedout = "Oturumunuz kapandi." authentication_error_technical = "GiriÅŸ Onayı Hatası" @@ -107,7 +111,6 @@ Backtrace = "Geri izleme" Bag = "Sepet" Balance = "Denge" Barcode = "Barkod" -basic_search_keep_filters = "Güncel filitrelerimle devam et." Be the first to leave a comment = "Ä°lk yorumlayan siz olun" Be the first to tag this record = "Ä°lk siz ekleyin" Bibliographic Details = "Detaylı Bibliyografya" @@ -177,6 +180,7 @@ Call Number = "Yer Numarası" callnumber_abbrev = "Yer Numarası Kısaltması" Cannot find record = "Kayıt bulunamıyor" Cannot find similar records = "Benzer kayıt bulunamadı" +cannot set = "Ayarlanamýyor" Cassette = "Kaset" cat_establish_account = "Kullanıcı hesabınızla baÄŸlantı kurulamadığından, lütfen takip eden bilgileri doldurun:" cat_password_abbrev = "Katalog Åžifresi" @@ -185,7 +189,12 @@ Catalog Login = "Katalog Giriþi" Catalog Results = "Katalog Sonucu" catalog_login_desc = "Kütüphane katalog kimlik bilgilerinizi giriniz." CD = "CD" +Change Email Address = "Eposta Adresini Deðiþtir" Change Password = "Åžifre DeÄŸiÅŸtir" +change_email_disabled = "Þu anda e-posta adresinizi deðiþtiremezsiniz" +change_email_verification_reminder = "Bu formu gönderdiðinizde yeni adrese bir e-posta gönderilecektir; deðiþiklik etkinleþtirilmeden önce e-postadaki bir baðlantýyý týklamanýz gerekecektir." +change_notification_email_message = "E-posta adresinizi %%library%% kütüphanesinde deðiþtirmek için bir istek yapýldý. Bu isteði siz baþlatmadýysanýz, %%url%% adresinden giriþ yapmak ve hesabýnýzýn doðruluðunu onaylamak isteyebilirsiniz. Sorularýnýz veya kaygýlarýnýz için lütfen %%e-posta%% adresinden destek birimine baþvurun." +change_notification_email_subject = "Hesap E-posta Deðiþikliði Bildirimi" channel_add_more = "Bunun gibi daha fazla kanal ekleyin" channel_browse = "Daha fazla kayıda gözat" channel_expand = "Ä°lgili kanalları keÅŸfedin" @@ -194,6 +203,7 @@ channel_search = "Öğeleri arama sonuçları olarak göster" channel_searchbox_label = "Daha fazla kanal ara:" Check Hold = "Ayrılmışları Kontrol Et" Check Recall = "Ä°adeleri Kontrol Et" +check_profile = "Kullanýcý bilgisini kontrol edin." Checked Out = "Ödünçte" Checked Out Items = "Ä°ade Ettiklerim" Checkedout = "Ödünç VerilmiÅŸ" @@ -227,6 +237,7 @@ comment_error_load = "Yorum Yüklenemedi" comment_error_save = "Yorum Saklanmadı" Comments = "Yorumlar" Company/Entity = "Åžirket/Varlık" +Conference Proceeding = "Konferans Tutanakları" Configuration = "Konfigürasyon" confirm_delete = "Bunu silmek istediÄŸinizden emin misiniz?" confirm_delete_brief = "Kayıt Silinsin mi?" @@ -307,6 +318,7 @@ Due Date = "Ä°ade tarihi" DVD = "DVD" eBook = "Ekitap" Edit = "Düzenle" +edit = "düzenle" Edit Library Card = "Kütüphane Kartını Düzenle" Edit this Advanced Search = "Bu Sorguyu Düzenle" edit_list = "Listeyi düzenle" @@ -335,8 +347,14 @@ Email address is invalid = "Eposta adresi geçerli deÄŸil" Email Record = "Kaydı eposta ile gönder" Email this = "E-posta Gönder" Email this Search = "Taramayı e-posta ile gönder" +email_change_pending_html = "Beklemede %%Pending%% olan bir e-posta deðiþikliði isteðiniz var. Lütfen deðiþikliði tamamlamak için bu adrese gönderilen doðrulama e-postasýndaki baðlantýya týklayýn. Gerekirse, <a href="%%url%%"> Doðrulama E-postasýný Yeniden Gönderebiliriz</a>." email_failure = "Eposta Hatası" email_link = "Eposta BaÄŸlantısı" +email_login_desc = "Lütfen giriþ yapmak için aþaðýdaki baðlantýyý kullanýn. Giriþ yapmadýysanýz, bu mesajý güvenle yok sayabilirsiniz. Lütfen baðlantýnýn yalnýzca sýnýrlý bir süre için geçerli olduðunu ve yalnýzca e-posta adresini girmek için kullandýðýnýz cihaz için geçerli olduðunu unutmayýn." +email_login_link = "Giriþ baðlantýsý: <%%url%%>" +email_login_link_sent = "E-posta adresinize bir giriþ baðlantýsý gönderdik. Baðlantýnýn ulaþmasý biraz zaman alabilir. Baðlantýyý kýsa sürede almazsanýz, lütfen spam filtrenizi de kontrol edin." +email_login_requested = "E-posta adresinizle %%title%% adresinden giriþ yapmanýz istendi." +email_login_subject = "%%title%% için giriþ yapýn" email_maximum_recipients_note = "Maksimum %%max%% alıcıya izin verilir." email_multiple_recipients_note = "Virgül ile ayırarak birden fazla alıcı belirtebilirsiniz." email_selected = "SeçilmiÅŸleri e-posta ile gönder" @@ -346,6 +364,7 @@ email_subject = "Konu" email_success = "Eposta Gönderildi" Empty = "BoÅŸ" Empty Book Bag = "Kitap çantası boÅŸ" +empty_search_disallowed = "Mevcut arama hedefiyle boþ bir sorguya izin verilmiyor" Enable Auto Config = "Otomatik Yapılandırma Etkin" End Page = "Sayfa Sonu" Era = "Yüzyıl" @@ -398,12 +417,13 @@ Favorites = "Favorilerim" Fee = "Gecikme Cezası" Feedback = "Görüsleriniz" feedback_email = "Eposta" -feedback_login_required = "Ä°lk önce giriÅŸ yapmalısınız." feedback_name = "Adiniz" Field of activity = "Aktivite Alanı" File Description = "Dosya tanımı" Filter = "Filtre" +Filter Collection = "Flitre koleksiyonu" filter_tags = "Etiketleri Flitrele" +filter_toggle_entries = "%%count%% flitre" filter_wildcard = "Herhangi" Find = "Tara" Find More = "DiÄŸer Taramalar" @@ -439,6 +459,7 @@ Go to Standard View = "Standart Görünüme Git" go_to_list = "Listeye Git" google_map_cluster = "Küme" google_map_cluster_points = "Kümeler" +Government Document = "Hükümet Belgesi" Grid = "Sistem" Group = "Okuyucu Grubu" group_AND = "Bütün Gruplar" @@ -465,6 +486,7 @@ history_results = "Sonuçlar" history_save = "Kaydet?" history_save_link = "BaÄŸlantıyı Kaydet" history_saved_searches = "KaydedilmiÅŸ taramalarınız" +history_schedule = "Uyarý programý" history_search = "Tarama" history_time = "Zaman" hold_available = "Ödünç alınabilir" @@ -611,6 +633,7 @@ Library Web Search = "Kütüphane Web Taraması" library_card_edit_password_placeholder = "Yeni Åžifre" lightbox_error = "Lightbox Hatası" Limit To = "AÅŸağıdaki seçenekler ile taramanızı sınırlandırabilirsiniz." +Link to full results = "Tam sonuçlara baðlantý" List = "Liste" List Tags = "Etiket Listesi" list_access_denied = "Bu listeyi görüntülemeye yetkiniz yok." @@ -673,9 +696,12 @@ New Item Feed = "Yeni Kayıt Beselemesi" New Item Search = "Yeni Kayıt Tara" New Item Search Results = "Yeni Kayıt Tarama Sonucu" New Items = "Yeni Gelen Materyaller" +New results found for search = "Arama için yeni sonuçlar bulundu" New Title = "Yeni BaÅŸlık" +new_email_success = "E-posta adresiniz baþarýyla deðiþtirildi" new_password = "Yeni Åžifre" new_password_success = "Åžifreniz baÅŸarı ile deÄŸiÅŸtirildi" +new_results_heading = "En yeni sonuçlar "%%count%%" new_user_welcome_subject = "%%library%% deki yeni hesabınız" new_user_welcome_text = "%%library%% ye HoÅŸ Geldiniz. %%firstname%% %%lastname%% için yeni hesabınız açılmıştır. Kullanıcı adınız %%username%%. %%url%% bu baÄŸlantıya tıklayarak ÅŸifrenizi oluÅŸturunuz" Newspaper = "Gazete" @@ -691,6 +717,7 @@ No Preference = "Tercih Yapma" No reviews were found for this record = "Bu kayıt için tanıtım/eleÅŸtiri bulunamadı" No Tags = " Etiket eklenmemiÅŸ" no_description = "Tanımlama yok" +no_email_address = "E-posta adresi eksik." no_items_selected = "Kayıt seçilmedi" nohit_active_filters = "Bu arama için bir veya daha fazla faset filtresi uygulanmış. Filtreleri kaldırırsanız daha fazla sonuç alabilirsiniz." nohit_change_tab = ""%%activeTab%%" sekmesi içinde arama yapmaktasınız. DiÄŸer sekmelerin birisi içinde bir ÅŸeyler bulabilirsiniz:" @@ -742,6 +769,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "OAI Sunucu" Occupation = "Meslek" +od_account_noaccess = "Bu kütüphane kartının Overdrive'daki içeriÄŸe eriÅŸimi yoktur" +od_account_problem = "Hesabınızla ilgili bir sorun var. %%message%%" +od_audiobook-mp3 = "MP3 sesli kitap" +od_audiobook-overdrive = "OverDrive sesli kitabı dinle" +od_avail_avail = "EriÅŸilebilir:" +od_avail_holds = "Tutar:" +od_avail_total = "Toplam Kopya:" +od_but_cancel_hold = "Bu ayırmayı iptal et" +od_but_checkout = "Overdrive Ãœzerinden Ödünç" +od_but_checkout_s = "Ödünç Al" +od_but_gettitle = "Bu içeriÄŸi indir" +od_but_gettitle_s = "Ä°ndir" +od_but_hold = "Overdrive Ãœzerinden Bir ayırma koyun" +od_but_hold_s = "Ayırma Yeri" +od_but_return = "Bu baÅŸlığı iade et" +od_cancel_hold = "Overdrive Ayırmayı iptal et" +od_checkout = "Overdrive Ödünç Alma" +od_code_connection_failed = "Overdrive'a baÄŸlantı baÅŸarısız oldu. Sorun devam ederse, lütfen kütüphanenize baÅŸvurun." +od_code_contentnotavail = "Bu içerik bölgenizde mevcut deÄŸil." +od_code_login_for_avail = "Kullanılabilirlik için giriÅŸ yapın" +od_code_resource_not_found = "BaÅŸlık bulunamadı" +od_content = "Overdrive İçeriÄŸi" +od_dl_formats = "Desteklenen Ä°ndirme Formatları" +od_docheckout_failure = "Bu baÅŸlık ödünç alınamadı." +od_docheckout_success = "Bu baÅŸlık starafınızdan ödünç alındı. %%expireDate%%" Tarihinde sona eriyor" +od_early_return = "Overdrive Erken Ä°ade" +od_ebook-epub-adobe = "Adobe EPUB eKitap" +od_ebook-epub-open = "EPUB eKitabı Aç" +od_ebook-kindle = "Kindle Kitabı" +od_ebook-mediado = "MediaDo eKitap Okuyucu" +od_ebook-overdrive = "OverDrive eKitap Oku" +od_ebook-pdf-adobe = "Adobe PDF eKitap" +od_ebook-pdf-open = "PDF eKitap Aç" +od_expires_on = "%%due_date%% tarihinde bu baÅŸlığın süresi doluyor." +od_get_title = "Overdrive Ä°ndirme" +od_gettitle_failure = "Bu baÅŸlık indirilemedi." +od_help_linktext = "Overdrive Yardım" +od_history = "Overdrive Tarihçe" +od_hold = "Overdrive Ayırmalar" +od_hold_cancel_failure = "Ayırtma iptal isteÄŸi baÅŸarısız oldu. " +od_hold_cancel_success = "Ayırtma baÅŸarıyla iptal edildi." +od_hold_email = "Ayırtma bildirimi için e-posta adresi: %%holdEmailAddress%%." +od_hold_now_avail = "Bu ayırtma kullanıma hazırdır. %%expireDate%% tarihinde süresi dolacaktır." +od_hold_place_failure = "Ayırtme isteÄŸi baÅŸarısız oldu." +od_hold_place_success = "Bu baÅŸlık daha önceden ayırtıldı. Ayırma durumunuz %%holdListPosition%%" +od_hold_placed_on = "%%holdPlacedDate%% tarihine kadar ayırt." +od_hold_queue = "%%holdPosition%% ayırtma durumunda %%numberOfHolds%%. sıradasınız." +od_holds = "Overdrive Ayırtmalar" +od_info_unavail = "Bu bilgi ÅŸu anda kullanılamıyor." +od_is_checkedout = "Bu baÅŸlığı ödünç alabilirsiniz. %%due_date%% tarihinde süre bitecek." +od_is_on_hold = "Bu baÅŸlığı ayırttınız" +od_loans = "Overdrive Ödünç Vermeleri" +od_mycontent_help = "Bu baÅŸlıkları indirme hakkında bilgi ve yardım için, <a href="%%url%%">Overdrive Yardım</a> baÄŸlantısına tıklayınız." +od_none_found = "BaÅŸlık bulunamadı." +od_return_failure = "Bu baÅŸlık iade edilmedi." +od_return_success = "Bu baÅŸlık iade edildi." +od_video-streaming = "video akış dosyası" of_num_results = "#%%position%% arası kayıtlar. %%total%% sonuçlar" old_password = "Eski Åžifre" On Reserve = "Rezerve" @@ -771,6 +855,7 @@ pagination_label = "Sayfa Numaralandırma" Password = "Parola" Password Again = "Åžifrenizi tekrar girin" Password cannot be blank = "Åžifre boÅŸ olamaz" +password_error_auth_old = "Daha önce kullanılan ÅŸifre geçersiz" password_error_invalid = "Yeni Åžifre geçersizi (geçersiz karakterler içeriyor olabilir)" password_error_not_unique = "Åžifre deÄŸiÅŸmedi" password_maximum_length = "Maksimum ÅŸifre uzunluÄŸu %%maxlength%% karakter" @@ -836,6 +921,7 @@ Read the full review online... = "EleÅŸtirinin tamamını online oku..." Recall This = "Rezerve" recaptcha_not_passed = "CAPTCHA kodu uygun deÄŸil" recently_returned_channel_title = "Son Zamanlarda Ä°ade Edilenler" +recommend_links_text = "Ayrýca deneyebilirsiniz:" Record Citations = "Alıntılar" Record Count = "Kayıt Sayısı" Record Type = "Kayıt Türü" @@ -854,6 +940,7 @@ recovery_title = "Åžifre Kurtarma" recovery_too_soon = "Çok fazla kurtarma isteÄŸi yapıldı, lütfen daha sonra tekrar deneyiniz" recovery_user_not_found = "Hesabınız bulunamadı" rectangle_center_message = "Vurgulanan dikdörtgen için orta nokta budur" +Reference Material = "Referans Materyali" Refine Results = "Sonuçları Daraltın" Region = "Bölge" relais_available = "Bu kayda Kütüphanelerarası Ödünç Verme yoluyla ulaşılabilir. Talep etmek ister misiniz?" @@ -894,6 +981,7 @@ Requests = "Ä°stekler" Reserves = "Rezerve" Reserves Search = "Rezerve Tara" Reserves Search Results = "Rezerve Tarama Sonucu" +reset_filters_button = "Filtreleri Sıfırla" result_checkbox_label = "%%number%% Sonuç numarasını seç" result_count = "%%count%% sonuçlar" Results = "Sonuçlar" @@ -909,6 +997,11 @@ Save Comment = "Yorumunuzu saklayın" save_search = "Taramayı sakla" save_search_remove = "Saklanmış taramayı sil" Saved in = "Kaydedildi" +schedule_daily = "Günlük" +schedule_explanation = "Arama için yeni sonuçlar hakkýnda uyarý e-postalarý alýn." +schedule_none = "Hiç" +schedule_weekly = "Haftalýk" +Scheduled Alert Results = "Planlanmýþ uyarý sonuçlarý" scholarly_limit = "Scholarly Limit" Scroll to Load More = "Daha fazla yükleme için ilerleyin" Search = "Tarama" @@ -923,6 +1016,7 @@ search results of = "Tarama Sonucu" Search Tips = "Arama Yardımı" Search Tools = "Tarama Araçları" Search Type = "Arama Türü" +Search within collection = "Koleksiyonda ara" search_AND = "Bütün Terimler" search_groups = "Grupları Ara" search_match = "EÅŸleÅŸtir" @@ -948,6 +1042,7 @@ Sensor Image = "Sensör Resmi" Serial = "Süreli" Series = "Seri Bilgileri" Set = "Set" +show_filters_html = "Gösterilen flitre sayısı (%%count%%)" showing_items_html = "Gösterilen <strong>%%start%% - %%end%%</strong> Kayıtlar" showing_items_of_html = "Gösterilen <strong>%%start%% - %%end%%</strong> arası kayıtlar. <strong>%%total%%</strong> Kayıtlar" showing_results_for_html = "Gösterilen <strong>%%start%% - %%end%%</strong> sonuçlar sonuç. Aranan kelime '<strong>%%lookfor%%</strong>'" @@ -995,6 +1090,7 @@ Start a new Basic Search = "Yeni Basit Tarama" Start Page = "BaÅŸlangıç Sayfası" starting from = "BaÅŸlangıçtan" Status = "Durum" +status_transit = "Geçiþ Halinde" status_unknown_message = "Konumu eriÅŸilebilir deÄŸil." Storage Retrieval Requests = "Depolama Alma Ä°stekleri" storage_retrieval_request_available = "Ödünç alınabilir" @@ -1070,6 +1166,7 @@ The record you selected is not part of any of your lists. = "SeçmiÅŸ olduÄŸunuz The record you selected is not part of the selected list. = "SeçmiÅŸ olduÄŸunuz kayıt seçilmiÅŸ bir listenin parçası deÄŸildir." The system is currently unavailable due to system maintenance = "Sistem bakım nedeniyle ÅŸu anda eriÅŸilebilir deÄŸildir." Theme = "Tema" +Thesis = "Tez" This email was sent from = "BU postayı gönderen" This field is required = "Bu alan zorunludur" This item is already part of the following list/lists = "Bu kayıt takip eden liste/listelerin bir parçasıdır" @@ -1100,6 +1197,9 @@ unique_tags = "Benzersiz Etiketler" University Library = "Ãœniversite Kütüphanesi" Unknown = "Bilinmiyor" unrecognized_facet_label = "DiÄŸer" +unsubscribe_confirmation = "E-posta aboneliðini iptal etmek istiyor musunuz?" +unsubscribe_description = "Artýk bu mesajý almak istemiyor musunuz? Aþaðýdaki baðlantýyý kullanarak aboneliði iptal edin" +unsubscribe_successful = "Abonelik iptal edildi" Upgrade VuFind = "VuFindı Güncelle" upgrade_description = "EÄŸer önceki Vufind versiyonundan yükseltiyorsanız, Bu araç ile eski ayarlarınızı yükleyebilirsiniz." URL = "URL" @@ -1109,6 +1209,14 @@ User Account = "Kullanıcı Hesabı" Username = "Kullanıcı" Username cannot be blank = "Kullanıcı adı boÅŸ olamaz" Username is already in use in another library card = "Kullanıcı adı baÅŸka kütüphane kartında zaten kullanılıyor" +verification_done = "E-posta adresiniz baÅŸarıyla doÄŸrulandı." +verification_email_change_sent = "E-posta adresi doðrulama talimatlarý yeni e-posta adresine gönderildi. Deðiþikliðin etkili olmasý için adresi doðrulamanýz gerekir." +verification_email_notification = "%%library%% hesabınız için e-posta adresinizi doÄŸrulamak üzere bir istek yapıldı.." +verification_email_sent = "Bu hesapta kayıtlı e-posta adresine e-posta adresi doÄŸrulama talimatları gönderildi.." +verification_email_subject = "VuFind E-posta DoÄŸrulama" +verification_email_url_pretext = "E-posta adresinizi %%url%% baÄŸlantısından doÄŸrulayabilirsiniz." +verification_too_soon = "E-postanız doÄŸrulama gerektiriyor. Kayıtlı e-posta adresinize yakın zamanda bir e-posta gönderildi. Almadıysanız, lütfen birkaç dakika bekleyin ve tekrar deneyin." +verification_user_not_found = "Hesabınızı bulamadık" VHS = "VHS" Video = "Video" Video Clips = "Video Klibi" diff --git a/languages/vi.ini b/languages/vi.ini index 6013d40c55dedf6dd775e6d0694c8c9c77760db2..21f31bf46c151d71d30185944d871ab2596bcfdf 100644 --- a/languages/vi.ini +++ b/languages/vi.ini @@ -50,6 +50,7 @@ advSearchError_notFound = "Tìm kiếm của bạn đã yêu cầu không tìm t ajax_load_interrupted = "Äang tải bị gián Ä‘oạn" ajaxview_label_information = "Thông tin" ajaxview_label_tools = "Công cụ" +alert_email_address = "Kết quả cảnh báo theo lịch sẽ được gá»i đến địa chỉ email" All = "Tất cả" All Fields = "Tất cả các trÆ°á»ng" All Pages Loaded = "Tải tất cả các trang" @@ -59,6 +60,7 @@ alphabrowselink_html = "Mục duyệt nhanh bằng %%index%% bắt đầu từ < An error has occurred = "Lá»—i đã xảy ra" An error occurred during execution; please try again later. = "Lá»—i xảy ra trong thi hà nh ; vui lòng thá» lại sau." AND = "Và " +and = "và " anonymous_tags = "Thẻ ẩn danh" APA Citation = "TrÃch dẫn APA" applied_filter = "Ãp dụng bá»™ lá»c" @@ -71,6 +73,8 @@ authentication_error_admin = "Chúng tôi không thể nháºt kà bạn tại th authentication_error_blank = "Thông tin đăng nháºp không thể được trống." authentication_error_creation_blocked = " Bạn không được phép tạo ra tà i khoản. " authentication_error_denied = "Khả năng không trùng khá»›p! Truy cáºp bị từ chối." +authentication_error_email_not_verified_html = "Äịa chỉ email của bạn chÆ°a được xác minh. Vui lòng kiểm tra thÆ° rác của bạn để tìm tin nhắn xác minh. Nếu cần, chúng tôi có thể <a href="%%url%%"> Gá»i lại email xác minh </a>." +authentication_error_in_progress = "Yêu cầu xác thá»±c đã được xá» lý. Vui lòng thá» lại sau nếu bạn cần kiểm tra lại." authentication_error_invalid = "Không hợp lệ mã đăng nháºp -- vui lòng thá» lại." authentication_error_loggedout = "Bạn có đăng xuất." authentication_error_technical = "Chúng tôi không thể nháºt kà bạn tại thá»i gian nà y. Xin vui lòng thá» lại sau." @@ -96,7 +100,6 @@ Backtrace = "Rút lui" Bag = "Giá»" Balance = "Cân đối" Barcode = "Mã vạch" -basic_search_keep_filters = "Giữ lại bá»™ lá»c hiện hà nh" Be the first to leave a comment = "Là ngÆ°á»i đầu tiên ghi lá»i nháºn xét" Be the first to tag this record = "Là ngÆ°á»i đầu tiên thẻ bản ghi nà y" Bibliographic Details = "Chi tiết vá» thÆ° mục" @@ -166,6 +169,7 @@ Call Number = "SÃ´Ì hiệu" callnumber_abbrev = "Gá»i #" Cannot find record = "Không thể tìm bản ghi" Cannot find similar records = "Không tìm ra được bản ghi tÆ°Æ¡ng tá»±" +cannot set = "Không thể thiết láºp" Cassette = "Băng cát xét" cat_establish_account = "Äể thiết láºp hồ sÆ¡ tà i khoản của bạn, xin vui lòng nháºp thông tin sau:" cat_password_abbrev = "Ca-ta-lô máºt khẩu" @@ -174,7 +178,12 @@ Catalog Login = "Ca-ta-lô đăng nháºp" Catalog Results = "Ca-ta-lô kết quả" catalog_login_desc = "Nháºp khả năng danh mục liệt kê của thÆ° viện của bạn." CD = "CD" +Change Email Address = "Thay đổi địa chỉ email" Change Password = "Äổi máºt khẩu" +change_email_disabled = "Bạn không được phép thay đổi địa chỉ email của bạn tại thá»i Ä‘iểm nà y" +change_email_verification_reminder = "Biểu mẫu nà y sẽ gá»i má»™t email đến địa chỉ má»›i; bạn sẽ phải nhấp và o má»™t liên kết trong email trÆ°á»›c khi thay đổi có hiệu lá»±c." +change_notification_email_message = "Má»™t yêu cầu đã được thá»±c hiện để thay đổi địa chỉ email của bạn tại thÆ° viện %%library%%. Nếu bạn không thá»±c hiện yêu cầu nà y, bạn có thể muốn đăng nháºp tại %%url%% và xác nháºn tÃnh toà n vẹn của tà i khoản của bạn. Vui lòng liên hệ vá»›i bá»™ pháºn há»— trợ tại %%email%% nếu bạn có thắc mắc hoặc quan tâm." +change_notification_email_subject = "Thông báo thay đổi email tà i khoản" channel_add_more = "Thêm các kênh nhÆ° thế nà y" channel_browse = "Duyệt nhanh bản ghi hÆ¡n" channel_expand = "Các kênh liên quan thám hiểm" @@ -183,6 +192,7 @@ channel_search = "Hiển thị khoản mục nhÆ° kết quả tìm kiếm" channel_searchbox_label = "Tìm kiếm các kênh hÆ¡n:" Check Hold = "Kiểm tra ảnh hưởng" Check Recall = "Kiểm tra thu hồi" +check_profile = "Kiểm tra thông tin ngÆ°á»i dùng." Checked Out = "Äã kiểm tra" Checked Out Items = "Các mục đã kiểm tra" Checkedout = "Äã kiểm tra" @@ -216,6 +226,7 @@ comment_error_load = "Lá»—i: Không thể hiển thị danh sách bình luáºn" comment_error_save = "Lá»—i: Không thể lÆ°u bình luáºn" Comments = "Những bình luáºn" Company/Entity = "Công ty/Doanh nghiệp" +Conference Proceeding = "Há»™i nghị Ä‘ang tiến hà nh" Configuration = "Cấu hình" confirm_delete = "Bạn có chắc muốn xóa nó?" confirm_delete_brief = "Xóa danh mục?" @@ -296,6 +307,7 @@ Due Date = "Ngà y đáo hạn" DVD = "ÄÄ©a DVD" eBook = "eBook" Edit = "Chỉnh sá»a" +edit = "chỉnh sá»a" Edit Library Card = "Chỉnh sá»a thẻ thÆ° viện" Edit this Advanced Search = "Chỉnh sá»a tìm kiếm nâng cao nà y" edit_list = "Chỉnh sá»a danh sách" @@ -324,8 +336,14 @@ Email address is invalid = "Äịa chỉ email không hợp lệ" Email Record = "Bản ghi email" Email this = "Email nà y" Email this Search = "Tìm kiếm email nà y" +email_change_pending_html = "Bạn có má»™t thay đổi email Ä‘ang chá» xá» lý thà nh %%pending%%. Vui lòng nhấp và o liên kết trong email xác minh được gá»i đến địa chỉ nà y để hoà n tất thay đổi. Nếu cần, chúng tôi có thể <a href="%%url%%">Gá»i lại email xác minh</a>." email_failure = "Lá»—i - Tin nhắn không thể gởi" email_link = "Liên kết" +email_login_desc = "Vui lòng sá» dụng liên kết sau để đăng nháºp. Nếu bạn không bắt đầu đăng nháºp, bạn có thể bá» qua thông báo nà y má»™t cách an toà n. Xin lÆ°u ý rằng liên kết chỉ có hiệu lá»±c trong má»™t thá»i gian giá»›i hạn và chỉ vá»›i thiết bị bạn đã sá» dụng để nháºp địa chỉ email." +email_login_link = "Liên kết để đăng nháºp: <%%url%%>" +email_login_link_sent = "Chúng tôi đã gá»i má»™t liên kết đăng nháºp đến địa chỉ email của bạn. Có thể mất má»™t và i phút để liên kết đến. Nếu bạn không sá»›m nháºn được liên kết, vui lòng kiểm tra thÆ° rác của bạn." +email_login_requested = "Äăng nháºp đã được yêu cầu vá»›i địa chỉ email của bạn tại %%title%%." +email_login_subject = "Äăng nháºp và o %%title%%" email_maximum_recipients_note = "Tối Ä‘a %%max%% ngÆ°á»i nháºn được phép." email_multiple_recipients_note = "Bạn có thể chỉ định nhiá»u ngÆ°á»i nháºn được phân tách bằng dấu phẩy." email_selected = "Email đã chá»n" @@ -335,6 +353,7 @@ email_subject = "Môn há»c" email_success = "Tin nhắn đã gởi" Empty = "Trống" Empty Book Bag = "Túi sách rá»—ng" +empty_search_disallowed = "Má»™t truy vấn trống không được phép vá»›i mục tiêu tìm kiếm hiện tại" Enable Auto Config = "Báºt cấu hình tá»± Ä‘á»™ng" End Page = "Cuối trang" Era = "Ká»· nguyên" @@ -387,12 +406,13 @@ Favorites = "Các mục đã lÆ°u" Fee = "Há»c phÃ" Feedback = "Phản hồi" feedback_email = "Email" -feedback_login_required = "Bạn phải đăng nháºp trÆ°á»›c." feedback_name = "Tên" Field of activity = "LÄ©nh vá»±c hoạt Ä‘á»™ng" File Description = "Mô tả táºp tin" Filter = "Bá»™ lá»c" +Filter Collection = "Bá»™ sÆ°u táºp bá»™ lá»c" filter_tags = "Thẻ bá»™ lá»c" +filter_toggle_entries = "%%count%% bá»™ lá»c" filter_wildcard = "Bất kì" Find = "Tìm kiếm" Find More = "Tìm thêm" @@ -428,6 +448,7 @@ Go to Standard View = "Chuyển đến chế Ä‘á»™ xem chuẩn" go_to_list = "Chuyển tá»›i danh sách" google_map_cluster = "Cluster" google_map_cluster_points = "Äiểm Cluster" +Government Document = "Tà i liệu chÃnh phủ" Grid = "LÆ°á»›i" Group = "Nhóm" group_AND = "Tất cả các nhóm" @@ -454,6 +475,7 @@ history_results = "Các kết quả" history_save = "LÆ°u không?" history_save_link = "LÆ°u" history_saved_searches = "Tìm kiếm đã lÆ°u" +history_schedule = "Lịch trình thông báo" history_search = "Tìm kiếm" history_time = "Thá»i gian" hold_available = "Có sẵn để nháºn" @@ -600,6 +622,7 @@ Library Web Search = "Tìm kiếm của thÆ° viện" library_card_edit_password_placeholder = "Máºt khẩu má»›i" lightbox_error = "Lá»—i: Không thể tải há»™p báºt lên" Limit To = "Giá»›i hạn" +Link to full results = "Liên kết đến kết quả đầy đủ" List = "Danh sách" List Tags = "Danh sách thẻ" list_access_denied = "Bạn không có quyá»n xem danh sách nà y." @@ -662,9 +685,12 @@ New Item Feed = "Nguồn cấp dữ liệu sách má»›i" New Item Search = "Tìm kiếm sách má»›i" New Item Search Results = "Kết quả tìm kiếm sách má»›i" New Items = "Sách má»›i" +New results found for search = "Kết quả má»›i được tìm thấy" New Title = "Tiêu Ä‘á» má»›i" +new_email_success = "Äịa chỉ email của bạn đã được thay đổi thà nh công" new_password = "Máºt khẩu má»›i" new_password_success = "Máºt khẩu của bạn đã được thay đổi thà nh công" +new_results_heading = "%%count%% kết quả má»›i nhất" new_user_welcome_subject = "Tà i khoản má»›i của bạn tại %%library%%" new_user_welcome_text = "Xin chà o tá»›i %%library%%. Má»™t tà i khoản má»›i đã được mở cho %%firstname%% %%lastname%%. Tà i khoản của bạn là %%username%%. Vui lòng đặt máºt khẩu trên trang nà y: %%url%%" Newspaper = "Báo chÃ" @@ -680,6 +706,7 @@ No Preference = "Không có sở thÃch" No reviews were found for this record = "Không tìm thấy bà i đánh giá nà o cho hồ sÆ¡ nà y" No Tags = "Không có thẻ" no_description = "Mô tả không khả dụng." +no_email_address = "Äịa chỉ email bị thiếu." no_items_selected = "Không có mục nà o được chá»n" nohit_active_filters = "Má»™t hoặc nhiá»u bá»™ lá»c khÃa cạnh đã được áp dụng cho tìm kiếm nà y. Nếu bạn xóa bá»™ lá»c, bạn có thể truy xuất thêm kết quả." nohit_change_tab = "Bạn đã tìm kiếm trong tab "%%activeTab%%". Bạn có thể tìm thấy thứ gì đó ở má»™t trong các tab khác:" @@ -731,6 +758,63 @@ number_decimal_point = "." number_thousands_separator = "," OAI Server = "Máy chủ OAI" Occupation = "Nghá» nghiệp" +od_account_noaccess = "Thẻ thÆ° viện nà y không có quyá»n truy cáºp và o ná»™i dung trong Overdrive" +od_account_problem = "Có má»™t vấn Ä‘á» vá»›i tà i khoản của bạn. %%message%%" +od_audiobook-mp3 = "MP3 audiobook" +od_audiobook-overdrive = "OverDrive Nghe audiobook" +od_avail_avail = "Có sẵn:" +od_avail_holds = "Giữ:" +od_avail_total = "Tổng số bản sao:" +od_but_cancel_hold = "Hủy bá» giữ nà y" +od_but_checkout = "Thanh toán qua Overdrive" +od_but_checkout_s = "Kiểm tra" +od_but_gettitle = "Tải ná»™i dung nà y" +od_but_gettitle_s = "Tải vá»" +od_but_hold = "Äặt má»™t khoản giữ quá mức" +od_but_hold_s = "Giữ chá»—" +od_but_return = "Trả lại danh hiệu nà y" +od_cancel_hold = "Hủy giữ quá mức" +od_checkout = "Thanh toán vượt mức" +od_code_connection_failed = "Kết nối vá»›i Overdrive không thà nh công. Nếu vấn Ä‘á» vẫn còn, xin vui lòng liên hệ vá»›i thÆ° viện của bạn." +od_code_contentnotavail = "Ná»™i dung nà y không có sẵn trong khu vá»±c của bạn." +od_code_login_for_avail = "Äăng nháºp để sẵn sà ng" +od_code_resource_not_found = "Không tìm thấy tiêu Ä‘á»" +od_content = "Ná»™i dung quá mức" +od_dl_formats = "Äịnh dạng tải xuống được há»— trợ" +od_docheckout_failure = "Tiêu Ä‘á» nà y không thể được kiểm tra." +od_docheckout_success = "Tiêu Ä‘á» nà y đã được kiểm tra cho bạn. Nó hết hạn và o %%expireDate%%" +od_early_return = "Vượt quá sá»›m" +od_ebook-epub-adobe = "Sách Ä‘iện tá» Adobe EPUB" +od_ebook-epub-open = "Mở sách Ä‘iện tá» EPUB" +od_ebook-kindle = "Sách Kindle" +od_ebook-mediado = "Sách Ä‘iện tá» MediaDo Reader" +od_ebook-overdrive = "OverDrive Äá»c sách Ä‘iện tá»" +od_ebook-pdf-adobe = "Sách Ä‘iện tá» Adobe PDF" +od_ebook-pdf-open = "Mở sách Ä‘iện tá» PDF" +od_expires_on = "Tiêu Ä‘á» nà y hết hạn và o %%due_date%%." +od_get_title = "Tải xuống quá mức" +od_gettitle_failure = "Tiêu Ä‘á» nà y không thể được tải xuống." +od_help_linktext = "Trợ giúp quá mức" +od_history = "Lịch sá» vượt mức" +od_hold = "Nắm giữ quá mức" +od_hold_cancel_failure = "Yêu cầu hủy giữ không thà nh công." +od_hold_cancel_success = "Việc giữ đã bị hủy thà nh công." +od_hold_email = "Äịa chỉ Email để thông báo giữ: %%holdEmailAddress%%." +od_hold_now_avail = "Nắm giữ nà y có sẵn để kiểm tra. Thanh toán hết hạn và o %%expireDate%%." +od_hold_place_failure = "Yêu cầu giữ không thà nh công." +od_hold_place_success = "Tiêu Ä‘á» nà y đã được giữ lại. Vị trà giữ của bạn là %%holdListPosition%%" +od_hold_placed_on = "Giữ trên %%holdPlacedDate%%." +od_hold_queue = "Vị trà %%holdPosition%% của %%numberOfHolds%% trong hà ng đợi giữ." +od_holds = "Nắm giữ quá mức" +od_info_unavail = "Thông tin nà y hiện không có sẵn." +od_is_checkedout = "Bạn đã kiểm tra tiêu Ä‘á» nà y. Äó là do %%due_date%%." +od_is_on_hold = "Bạn có tiêu Ä‘á» nà y bị giữ." +od_loans = "Mượn quá mức" +od_mycontent_help = "Äể biết thông tin vá» và giúp tải xuống các tiêu Ä‘á» nà y, xem <a href="%%url%%">Overdrive Help</a>." +od_none_found = "Không tìm thấy tiêu Ä‘á»." +od_return_failure = "Tiêu Ä‘á» nà y không thể được trả lại." +od_return_success = "Danh hiệu nà y đã được trả lại." +od_video-streaming = "phát trá»±c tuyến táºp tin video" of_num_results = "#%%position%% của %%total%% kết quả" old_password = "Máºt khẩu cÅ©" On Reserve = "Ngà y dá»± trữ" @@ -760,6 +844,7 @@ pagination_label = "Phân trang" Password = "Máºt khẩu" Password Again = "Nháºp lại máºt khẩu" Password cannot be blank = "Không được để trống máºt khẩu" +password_error_auth_old = "Máºt khẩu được sá» dụng trÆ°á»›c đó không hợp lệ" password_error_invalid = "Máºt khẩu má»›i không hợp lệ (và dụ: chứa các ký tá»± không hợp lệ)" password_error_not_unique = "Máºt khẩu không được thay đổi" password_maximum_length = "Äá»™ dà i máºt khẩu tối Ä‘a là %%maxlength%% ký tá»±" @@ -825,6 +910,7 @@ Read the full review online... = "Äá»c toà n bá»™ đánh giá trá»±c tuyến . Recall This = "Gá»i lại Ä‘iá»u nà y" recaptcha_not_passed = "CAPTCHA không được thông qua" recently_returned_channel_title = "Äã trả lại gần đây" +recommend_links_text = "Bạn cÅ©ng có thể thá»:" Record Citations = "Ghi lại trÃch dẫn" Record Count = "Số bản ghi" Record Type = "Loại bản ghi" @@ -843,6 +929,7 @@ recovery_title = "Khôi phục máºt khẩu" recovery_too_soon = "Quá nhiá»u yêu cầu khôi phục đã được thá»±c hiện, vui lòng thá» lại sau" recovery_user_not_found = "Chúng tôi không thể tìm thấy tà i khoản của bạn" rectangle_center_message = "Äây là điểm trung tâm của hình chữ nháºt được đánh dấu" +Reference Material = "Tà i liệu tham khảo" Refine Results = "Tinh chỉnh kết quả" Region = "Miá»n" relais_available = "Mục nà y có sẵn thông qua cho mượn Liên thÆ° viện. Bạn có muốn yêu cầu không?" @@ -883,6 +970,7 @@ Requests = "Yêu cầu" Reserves = "Dá»± trữ" Reserves Search = "Tìm kiếm dá»± trữ" Reserves Search Results = "LÆ°u trữ kết quả tìm kiếm" +reset_filters_button = "Äặt lại bá»™ lá»c" result_checkbox_label = "Chá»n kết quả số %%number%%" result_count = "%%count%% kết quả" Results = "Kết quả" @@ -898,6 +986,11 @@ Save Comment = "LÆ°u nháºn xét" save_search = "LÆ°u tìm kiếm" save_search_remove = "Xóa Tìm kiếm đã LÆ°u" Saved in = "Äã lÆ°u trong" +schedule_daily = "Hằng ngà y" +schedule_explanation = "Nháºn email thông báo vá» kết quả má»›i để tìm kiếm." +schedule_none = "Không" +schedule_weekly = "Hà ng tuần" +Scheduled Alert Results = "Kết quả cảnh báo theo lịch trình" scholarly_limit = "Giá»›i hạn bà i viết từ các tạp chà há»c thuáºt" Scroll to Load More = "Cuá»™n để tải thêm" Search = "Tìm kiếm" @@ -912,6 +1005,7 @@ search results of = "Kết quả tìm kiếm của" Search Tips = "Mẹo tìm kiếm" Search Tools = "Công cụ tìm kiếm" Search Type = "Loại tìm kiếm" +Search within collection = "Tìm kiếm trong bá»™ sÆ°u táºp" search_AND = "Tất cả Ä‘iá»u khoản" search_groups = "Tìm kiếm nhóm" search_match = "Phù hợp" @@ -937,6 +1031,7 @@ Sensor Image = "Hình ảnh cảm biến" Serial = "Số seri" Series = "Loạt" Set = "Bá»™" +show_filters_html = "Hiển thị bá»™ lá»c (%%count%%)" showing_items_html = "Äang hiển thị <strong>%%start%% - %%end%%</strong> quyển sách" showing_items_of_html = "Äang hiển thị <strong>%%start%% - %%end%%</strong> của <strong>%%total%%</strong> quyển sách" showing_results_for_html = "Äang hiển thị <strong>%%start%% - %%end%%</strong> kết quả tìm kiếm '<strong>%%lookfor%%</strong>'" @@ -984,6 +1079,7 @@ Start a new Basic Search = "Bắt đầu má»™t tìm kiếm cÆ¡ bản má»›i" Start Page = "Trang bắt đầu" starting from = "Bắt đầu từ" Status = "Trạng thái" +status_transit = "Äang váºn chuyển" status_unknown_message = "Trạng thái trá»±c tiếp không khả dụng" Storage Retrieval Requests = "Yêu cầu truy xuất bá»™ nhá»›" storage_retrieval_request_available = "Có sẵn cho tìm kiếm" @@ -1059,6 +1155,7 @@ The record you selected is not part of any of your lists. = "Bản ghi bạn Ä‘ The record you selected is not part of the selected list. = "Bản ghi bạn đã chá»n không thuá»™c danh sách được chá»n." The system is currently unavailable due to system maintenance = "Hệ thống hiện không khả dụng do bảo trì hệ thống" Theme = "Chủ Ä‘á»" +Thesis = "Luáºn văn" This email was sent from = "Email nà y đã được gá»i từ" This field is required = "TrÆ°á»ng nà y là bắt buá»™c" This item is already part of the following list/lists = "Sách nà y đã là má»™t phần của danh sách / các danh sách" @@ -1089,6 +1186,9 @@ unique_tags = "Thẻ duy nhất" University Library = "ThÆ° viện trÆ°á»ng đại há»c" Unknown = "Không biết" unrecognized_facet_label = "Khác" +unsubscribe_confirmation = "Bạn có muốn hủy đăng ký email?" +unsubscribe_description = "Không muốn nháºn tin nhắn nà y trong tÆ°Æ¡ng lai? Hủy đăng ký bằng cách sá» dụng liên kết sau" +unsubscribe_successful = "Äăng ký bị hủy" Upgrade VuFind = "Nâng cấp Vufind" upgrade_description = "Nếu bạn Ä‘ang nâng cấp phiên bản VuFind trÆ°á»›c đó, bạn có thể tải cà i đặt cÅ© của mình bằng công cụ nà y." URL = "URL" @@ -1098,6 +1198,14 @@ User Account = "Tà i khoản ngÆ°á»i dùng" Username = "Tên ngÆ°á»i dùng" Username cannot be blank = "Tên ngÆ°á»i dùng không thể trống" Username is already in use in another library card = "Tên ngÆ°á»i dùng đã được sá» dụng trong má»™t thẻ thÆ° viện khác" +verification_done = "Äịa chỉ email của bạn đã được xác minh thà nh công." +verification_email_change_sent = "HÆ°á»›ng dẫn xác minh địa chỉ email đã được gá»i đến địa chỉ email má»›i. Bạn phải xác minh địa chỉ trÆ°á»›c khi thay đổi có hiệu lá»±c." +verification_email_notification = "Má»™t yêu cầu vừa được Ä‘Æ°a ra để xác minh địa chỉ email cho tà i khoản của bạn vá»›i %%library%%." +verification_email_sent = "HÆ°á»›ng dẫn xác minh địa chỉ email đã được gá»i đến địa chỉ email đã đăng ký vá»›i tà i khoản nà y." +verification_email_subject = "Xác minh email VuFind" +verification_email_url_pretext = "Bạn có thể xác minh địa chỉ email của mình tại URL nà y: %%url%%" +verification_too_soon = "Email của bạn yêu cầu xác nháºn. Má»™t email gần đây đã được gá»i đến địa chỉ email đã đăng ký của bạn. Nếu bạn không nháºn được nó, vui lòng đợi và i phút và thá» lại." +verification_user_not_found = "Chúng tôi không thể tìm thấy tà i khoản của bạn" VHS = "VHS" Video = "Video" Video Clips = "Video Clips" diff --git a/languages/zh-cn.ini b/languages/zh-cn.ini index b3b0b7f8647a2c821f79b4f4a0bef5de92959ff5..c34d7c13ad47a65c3146b731474f73c22a76cd9d 100644 --- a/languages/zh-cn.ini +++ b/languages/zh-cn.ini @@ -64,7 +64,6 @@ Back to Search Results = "返回结果列表" Backtrace = "追踪" Bag = "书包" Balance = "ä½™é¢" -basic_search_keep_filters = "ä¿ç•™æˆ‘ç›®å‰çš„过滤器" Be the first to leave a comment = "æˆä¸ºç¬¬ä¸€ä¸ªå‘表评论" Be the first to tag this record = "æˆä¸ºç¬¬ä¸€ä¸ªæ ‡è®°æ¤è®°å½•" Bibliographic Details = "书目详细资料" @@ -241,7 +240,6 @@ fav_list_delete_fail = "很抱æ‰ï¼Œå‘生错误. æ‚¨çš„åˆ—è¡¨æ²¡è¢«åˆ é™¤." Favorites = "最爱" Fee = "è´¹" feedback_email = "电å邮件" -feedback_login_required = "您必须先登录" Find = "检索" Find More = "查找更多" Find New Items = "寻找新项目" diff --git a/languages/zh.ini b/languages/zh.ini index ba6278e3e947943cc344ae46afd0b4d5c7dc0cd3..258bf7edbd0a802c3e1cba52a03afb797c12ed9b 100644 --- a/languages/zh.ini +++ b/languages/zh.ini @@ -64,7 +64,6 @@ Back to Search Results = "返回çµæžœåˆ—表" Backtrace = "追蹤" Bag = "書包" Balance = "餘é¡" -basic_search_keep_filters = "ä¿ç•™æˆ‘ç›®å‰çš„éŽæ¿¾å™¨" Be the first to leave a comment = "æˆç‚ºç¬¬ä¸€å€‹ç™¼è¡¨è©•è«–" Be the first to tag this record = "æˆç‚ºç¬¬ä¸€å€‹æ¨™è¨˜æ¤è¨˜éŒ„" Bibliographic Details = "書目詳細資料" @@ -241,7 +240,6 @@ fav_list_delete_fail = "很抱æ‰ï¼Œç™¼ç”ŸéŒ¯èª¤. 您的列表沒被刪除." Favorites = "最愛" Fee = "è²»" feedback_email = "é›»å郵件" -feedback_login_required = "æ‚¨å¿…é ˆå…ˆç™»éŒ„" Find = "檢索" Find More = "查找更多" Find New Items = "å°‹æ‰¾æ–°é …ç›®" diff --git a/local/config/vufind/CollectionTabs.ini b/local/config/vufind/CollectionTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..01c9c6314fe07d61a2d2b1cc32b0ec01186fb41a --- /dev/null +++ b/local/config/vufind/CollectionTabs.ini @@ -0,0 +1,26 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2022 ############## +; +; This is the ISIL-instance-specific default 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/CollectionTabs.ini + +; This file controls the tab display in the Collection view (as opposed to the +; single record view). The structure is identical to RecordTabs.ini; see the +; comments in that file for details on the meanings of the various settings. + +[finc\RecordDriver\AbstractBase] +tabs[CollectionList] = CollectionList +tabs[HierarchyTree] = CollectionHierarchyTree +tabs[Details] = StaffViewArray + +[finc\RecordDriver\SolrDico] +tabs[CollectionList] = CollectionList +tabs[HierarchyTree] = HierarchyTree +tabs[Details] = StaffViewArray +defaultTab = CollectionList diff --git a/local/config/vufind/RecordTabs.ini b/local/config/vufind/RecordTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..916b7c4ce21c63692e2c27eed1108acc617a3b2b --- /dev/null +++ b/local/config/vufind/RecordTabs.ini @@ -0,0 +1,91 @@ +;#################################################################### +;##################### DO NOT DELETE THIS HEADER #################### +;################### Leipzig University Library © 2022 ############## +; +; This is the ISIL-instance-specific default 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/RecordTabs.ini + +; This file controls the display of tabs on the Record page for various types of +; records. Each section name matches a record driver class name, and those settings +; will be used when displaying that type of record. If no settings are found for a +; particular class, its parent classes will be checked in turn; thus, you could set +; up global defaults using a [VuFind\RecordDriver\AbstractBase] section. +; +; Within each section, the following settings are supported: +; +; tabs[X] = Y -- This activates a tab, using "X" to identify that tab in the URL, +; and using a service named "Y" loaded from the RecordTab plugin +; manager. The order of tabs entries controls display order. +; defaultTab -- This matches an "X" value from a tabs setting, and controls which +; tab is active by default. If empty, the global default tab setting +; (defaultRecordTab) from config.ini will be used. +; backgroundLoadedTabs[] -- This repeatable setting can be used to identify tabs +; that should be asynchronously loaded in the background to improve +; performance. Use the "X" value from the tabs setting as the id. + +[finc\RecordDriver\SolrDefault] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = null +tabs[Details] = StaffViewArray + +[finc\RecordDriver\SolrAI] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = null +tabs[Details] = StaffViewAI + +[finc\RecordDriver\SolrMarc] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = null +tabs[Details] = StaffViewMARC + +[finc\RecordDriver\SolrMarcFincPDA] +tabs[AcquisitionPDA] = AcquisitionPDA +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = null +tabs[Details] = StaffViewMARC + +[finc\RecordDriver\SolrLido] +tabs[Holdings] = HoldingsILS +tabs[Description] = DescriptionLido +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewArray diff --git a/local/languages/de.ini b/local/languages/de.ini index 3c2577c93378759a286e50d4be6ffc9e0d849894..c707c4cdee8ce2c93d305d10a0f8be73a0200955 100644 --- a/local/languages/de.ini +++ b/local/languages/de.ini @@ -182,7 +182,7 @@ Accompanying material = Beilagen Account = Konto Add = Hinzufügen Add a Note = "Bemerkung hinzufügen" -Add to Book Bag = "Zur Zwischenablage hinzufügen" +Add to Book Bag = "Merken" Add to favorites = "Zu den Favoriten hinzufügen" Add your comment = "Lesermeinung speichern" Additional Information = "Zusatzinformationen" @@ -481,6 +481,7 @@ Reference management = "Literaturverwaltung" Related Title = "Zugehörige Werke" Renew Login = "Bitte, erneut einloggen" Remove all Filters = "entfernen Sie alle Suchfilter" +Remove from Book Bag = "Nicht mehr merken" Repeat your password = "Wiederhole das Passwort" Reserve = Vormerkbar Reserved = "Vorgemerkte Medien" @@ -679,6 +680,7 @@ cat_establish_account = "Bitte geben Sie folgende Angaben ein" cat_password_abbrev = "Passwort Katalog" cat_username_abbrev = "Benutzername Katalog" cited_articles = "Cited Articles" +clear_tag_filter = "Filter zurücksetzen" close = schließen comment_error_load = "Fehler: Laden der Kommentareinträge fehlgeschlagen" comment_error_save = "Fehler: Speichern des Kommentars fehlgeschlagen" @@ -886,6 +888,7 @@ create a new list = "erstellen Sie eine neue Liste" page_reload_hint = "Seite wird neu geladen" page_reload_on_select_hint = "Seite wird bei Auswahl des Filters '%%filter_name%%' neu geladen" page_reload_on_deselect_hint = "Seite wird neu geladen, wenn der Filter '%%filter_name%%' abgewählt wird" +page_reload_on_deselect_all_hint = "Seite wird neu geladen, wenn Filter zurückgesetzt werden" page_reload_on_exclude_hint = "Seite wird neu geladen, wenn der Filter '%%filter_name%%' aus den Suchergebnissen ausgeschlossen wird" password_too_short = "Zu kurz" password_very_weak = "Sehr schwach" diff --git a/local/languages/en.ini b/local/languages/en.ini index c1fcba84f3b1b50b43c3650e98c5611acba3dcc2..9a7f111725b8c59b3cfc3b9403d0b7ee68e4fb6a 100644 --- a/local/languages/en.ini +++ b/local/languages/en.ini @@ -848,6 +848,7 @@ create a new list = "Create a New List" page_reload_hint = "Page will refresh immediately" page_reload_on_select_hint = "Page will refresh when filter '%%filter_name%%' is selected" page_reload_on_deselect_hint = "Page will refresh when filter '%%filter_name%%' is deselected" +page_reload_on_deselect_all_hint = "Page will refresh when filters are deselected" page_reload_on_exclude_hint = "Page will refresh when filter '%%filter_name%%' is selected for exclusion from the search results" password_too_short = "Too short" password_very_weak = "Very weak" diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index de4b68990a08e58f2876c51371c6a644ed930d8b..cd3d16423ee8dc214d7a2008b00cad60f38f9e8c 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -45,6 +45,19 @@ $config = [ ] ], ], + 'shortlink' => [ + 'type' => 'Zend\Router\Http\Segment', + 'options' => [ + 'route' => '/short/[:id]', + 'constraints' => [ + 'id' => '[a-zA-Z0-9]+', + ], + 'defaults' => [ + 'controller' => 'Shortlink', + 'action' => 'redirect', + ] + ], + ], 'legacy-alphabrowse-results' => [ 'type' => 'Zend\Router\Http\Literal', 'options' => [ @@ -143,6 +156,7 @@ $config = [ 'VuFind\Controller\FeedbackController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\Search2Controller' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\Search2recordController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\Search2collectionController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', 'VuFind\Controller\HelpController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\HierarchyController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\IndexController' => 'VuFind\Controller\IndexControllerFactory', @@ -152,6 +166,7 @@ $config = [ 'VuFind\Controller\MissingrecordController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\MyResearchController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\OaiController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\OverdriveController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\Pazpar2Controller' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\PrimoController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\PrimorecordController' => 'VuFind\Controller\AbstractBaseFactory', @@ -161,6 +176,7 @@ $config = [ 'VuFind\Controller\RelaisController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\SearchController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\ShibbolethLogoutNotificationController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ShortlinkController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\SummonController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\SummonrecordController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\TagController' => 'VuFind\Controller\AbstractBaseFactory', @@ -169,6 +185,9 @@ $config = [ 'VuFind\Controller\WorldcatController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\WorldcatrecordController' => 'VuFind\Controller\AbstractBaseFactory', ], + 'initializers' => [ + 'VuFind\ServiceManager\ServiceInitializer', + ], 'aliases' => [ 'AJAX' => 'VuFind\Controller\AjaxController', 'ajax' => 'VuFind\Controller\AjaxController', @@ -216,6 +235,8 @@ $config = [ 'feedback' => 'VuFind\Controller\FeedbackController', 'Search2' => 'VuFind\Controller\Search2Controller', 'search2' => 'VuFind\Controller\Search2Controller', + 'Search2Collection' => 'VuFind\Controller\Search2collectionController', + 'search2collection' => 'VuFind\Controller\Search2collectionController', 'Search2Record' => 'VuFind\Controller\Search2recordController', 'search2record' => 'VuFind\Controller\Search2recordController', 'Help' => 'VuFind\Controller\HelpController', @@ -236,6 +257,8 @@ $config = [ 'myresearch' => 'VuFind\Controller\MyResearchController', 'OAI' => 'VuFind\Controller\OaiController', 'oai' => 'VuFind\Controller\OaiController', + 'Overdrive' => 'VuFind\Controller\OverdriveController', + 'overdrive' => 'VuFind\Controller\OverdriveController', 'Pazpar2' => 'VuFind\Controller\Pazpar2Controller', 'pazpar2' => 'VuFind\Controller\Pazpar2Controller', 'Primo' => 'VuFind\Controller\PrimoController', @@ -254,6 +277,8 @@ $config = [ 'search' => 'VuFind\Controller\SearchController', 'ShibbolethLogoutNotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController', 'shibbolethlogoutnotification' => 'VuFind\Controller\ShibbolethLogoutNotificationController', + 'Shortlink' => 'VuFind\Controller\ShortlinkController', + 'shortlink' => 'VuFind\Controller\ShortlinkController', 'Summon' => 'VuFind\Controller\SummonController', 'summon' => 'VuFind\Controller\SummonController', 'SummonRecord' => 'VuFind\Controller\SummonrecordController', @@ -273,18 +298,21 @@ $config = [ 'controller_plugins' => [ 'factories' => [ 'VuFind\Controller\Plugin\DbUpgrade' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Controller\Plugin\Favorites' => 'VuFind\Controller\Plugin\Factory::getFavorites', - 'VuFind\Controller\Plugin\Followup' => 'VuFind\Controller\Plugin\Factory::getFollowup', - 'VuFind\Controller\Plugin\Holds' => 'VuFind\Controller\Plugin\Factory::getHolds', - 'VuFind\Controller\Plugin\ILLRequests' => 'VuFind\Controller\Plugin\Factory::getILLRequests', - 'VuFind\Controller\Plugin\NewItems' => 'VuFind\Controller\Plugin\Factory::getNewItems', - 'VuFind\Controller\Plugin\Permission' => 'VuFind\Controller\Plugin\Factory::getPermission', - 'VuFind\Controller\Plugin\Recaptcha' => 'VuFind\Controller\Plugin\Factory::getRecaptcha', + 'VuFind\Controller\Plugin\Favorites' => 'VuFind\Controller\Plugin\FavoritesFactory', + 'VuFind\Controller\Plugin\Followup' => 'VuFind\Controller\Plugin\FollowupFactory', + 'VuFind\Controller\Plugin\Holds' => 'VuFind\Controller\Plugin\AbstractRequestBaseFactory', + 'VuFind\Controller\Plugin\ILLRequests' => 'VuFind\Controller\Plugin\AbstractRequestBaseFactory', + 'VuFind\Controller\Plugin\NewItems' => 'VuFind\Controller\Plugin\NewItemsFactory', + 'VuFind\Controller\Plugin\Permission' => 'VuFind\Controller\Plugin\PermissionFactory', + 'VuFind\Controller\Plugin\Recaptcha' => 'VuFind\Controller\Plugin\RecaptchaFactory', 'VuFind\Controller\Plugin\Renewals' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Controller\Plugin\Reserves' => 'VuFind\Controller\Plugin\Factory::getReserves', - 'VuFind\Controller\Plugin\ResultScroller' => 'VuFind\Controller\Plugin\Factory::getResultScroller', - 'VuFind\Controller\Plugin\StorageRetrievalRequests' => 'VuFind\Controller\Plugin\Factory::getStorageRetrievalRequests', - 'Zend\Mvc\Plugin\FlashMessenger\FlashMessenger' => 'VuFind\Controller\Plugin\Factory::getFlashMessenger', + 'VuFind\Controller\Plugin\Reserves' => 'VuFind\Controller\Plugin\ReservesFactory', + 'VuFind\Controller\Plugin\ResultScroller' => 'VuFind\Controller\Plugin\ResultScrollerFactory', + 'VuFind\Controller\Plugin\StorageRetrievalRequests' => 'VuFind\Controller\Plugin\AbstractRequestBaseFactory', + 'Zend\Mvc\Plugin\FlashMessenger\FlashMessenger' => 'VuFind\Controller\Plugin\FlashMessengerFactory', + ], + 'initializers' => [ + 'VuFind\ServiceManager\ServiceInitializer', ], 'aliases' => [ 'dbUpgrade' => 'VuFind\Controller\Plugin\DbUpgrade', @@ -305,8 +333,9 @@ $config = [ 'service_manager' => [ 'allow_override' => true, 'factories' => [ - 'ProxyManager\Configuration' => 'VuFind\Service\Factory::getProxyConfig', + 'ProxyManager\Configuration' => 'VuFind\Service\ProxyConfigFactory', 'VuFind\AjaxHandler\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Auth\EmailAuthenticator' => 'VuFind\Auth\EmailAuthenticatorFactory', 'VuFind\Auth\ILSAuthenticator' => 'VuFind\Auth\ILSAuthenticatorFactory', 'VuFind\Auth\Manager' => 'VuFind\Auth\ManagerFactory', 'VuFind\Auth\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', @@ -342,6 +371,7 @@ $config = [ 'VuFind\Db\AdapterFactory' => 'VuFind\Service\ServiceWithConfigIniFactory', 'VuFind\Db\Row\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Db\Table\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\DigitalContent\OverdriveConnector' => 'VuFind\DigitalContent\OverdriveConnectorFactory', 'VuFind\DoiLinker\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Export' => 'VuFind\ExportFactory', 'VuFind\Favorites\FavoritesService' => 'VuFind\Favorites\FavoritesServiceFactory', @@ -353,6 +383,7 @@ $config = [ 'VuFind\Hierarchy\TreeDataFormatter\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Hierarchy\TreeDataSource\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Hierarchy\TreeRenderer\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Http\PhpEnvironment\Request' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\ILS\Connection' => 'VuFind\ILS\ConnectionFactory', 'VuFind\ILS\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\ILS\Logic\Holds' => 'VuFind\ILS\Logic\LogicFactory', @@ -360,15 +391,19 @@ $config = [ 'VuFind\ILS\HoldSettings' => 'VuFind\ILS\HoldSettingsFactory', 'VuFind\Log\Logger' => 'VuFind\Log\LoggerFactory', 'VuFind\Mailer\Mailer' => 'VuFind\Mailer\Factory', + 'VuFind\MetadataVocabulary\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Net\IpAddressUtils' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\OAI\Server' => 'VuFind\OAI\ServerFactory', + 'VuFind\OAI\Server\Auth' => 'VuFind\OAI\ServerFactory', 'VuFind\QRCode\Loader' => 'VuFind\QRCode\LoaderFactory', 'VuFind\Recommend\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Record\Cache' => 'VuFind\Record\CacheFactory', 'VuFind\Record\FallbackLoader\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Record\Loader' => 'VuFind\Record\LoaderFactory', - 'VuFind\Record\Router' => 'VuFind\Record\RouterFactory', + 'VuFind\Record\Router' => 'VuFind\Service\ServiceWithConfigIniFactory', 'VuFind\RecordDriver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\RecordTab\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\RecordTab\TabManager' => 'VuFind\RecordTab\TabManagerFactory', 'VuFind\Related\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Resolver\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Role\PermissionManager' => 'VuFind\Role\PermissionManagerFactory', @@ -377,6 +412,7 @@ $config = [ 'VuFind\Search\History' => 'VuFind\Search\HistoryFactory', 'VuFind\Search\Memory' => 'VuFind\Search\MemoryFactory', 'VuFind\Search\FacetCache\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\Search\Factory\UrlQueryHelperFactory' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\Search\Options\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Search\Params\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Search\Results\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', @@ -389,17 +425,24 @@ $config = [ 'VuFind\SMS\SMSInterface' => 'VuFind\SMS\Factory', 'VuFind\Solr\Writer' => 'VuFind\Solr\WriterFactory', 'VuFind\Tags' => 'VuFind\TagsFactory', + 'VuFind\UrlShortener\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\UrlShortener\UrlShortenerInterface' => 'VuFind\UrlShortener\ServiceFactory', 'VuFind\Validator\Csrf' => 'VuFind\Validator\CsrfFactory', - 'VuFindHttp\HttpService' => 'VuFind\Service\Factory::getHttp', - 'VuFindSearch\Service' => 'VuFind\Service\Factory::getSearchService', - 'Zend\Db\Adapter\Adapter' => 'VuFind\Service\Factory::getDbAdapter', - 'Zend\Mvc\I18n\Translator' => 'VuFind\Service\Factory::getTranslator', + 'VuFindHttp\HttpService' => 'VuFind\Service\HttpServiceFactory', + 'VuFindSearch\Service' => 'VuFind\Service\SearchServiceFactory', + 'Zend\Db\Adapter\Adapter' => 'VuFind\Db\AdapterFactory', + 'Zend\Http\PhpEnvironment\RemoteAddress' => 'VuFind\Http\PhpEnvironment\RemoteAddressFactory', + 'Zend\Mvc\I18n\Translator' => 'VuFind\I18n\Translator\TranslatorFactory', 'Zend\Session\SessionManager' => 'VuFind\Session\ManagerFactory', ], + 'delegators' => [ + 'VuFind\Http\PhpEnvironment\Request' => [ \Zend\Mvc\Console\Service\ConsoleRequestDelegatorFactory::class ], + ], 'initializers' => [ 'VuFind\ServiceManager\ServiceInitializer', ], 'aliases' => [ + 'Request' => 'VuFind\Http\PhpEnvironment\Request', 'VuFind\AccountCapabilities' => 'VuFind\Config\AccountCapabilities', 'VuFind\AuthManager' => 'VuFind\Auth\Manager', 'VuFind\AuthPluginManager' => 'VuFind\Auth\PluginManager', @@ -485,6 +528,7 @@ $config = [ 'config_reader' => [ /* see VuFind\Config\PluginManager for defaults */ ], // PostgreSQL sequence mapping 'pgsql_seq_mapping' => [ + 'auth_hash' => ['id', 'auth_hash_id_seq'], 'comments' => ['id', 'comments_id_seq'], 'external_session' => ['id', 'external_session_id_seq'], 'oai_resumption' => ['id', 'oai_resumption_id_seq'], @@ -493,6 +537,7 @@ $config = [ 'resource_tags' => ['id', 'resource_tags_id_seq'], 'search' => ['id', 'search_id_seq'], 'session' => ['id', 'session_id_seq'], + 'shortlinks' => ['id', 'shortlinks_id_seq'], 'tags' => ['id', 'tags_id_seq'], 'user' => ['id', 'user_id_seq'], 'user_card' => ['id', 'user_card_id_seq'], @@ -523,6 +568,7 @@ $config = [ 'hierarchy_treedatasource' => [ /* see VuFind\Hierarchy\TreeDataSource\PluginManager for defaults */ ], 'hierarchy_treerenderer' => [ /* see VuFind\Hierarchy\TreeRenderer\PluginManager for defaults */ ], 'ils_driver' => [ /* See VuFind\ILS\Driver\PluginManager for defaults */ ], + 'metadatavocabulary' => [ /* See VuFind\MetadataVocabulary\PluginManager for defaults */], 'recommend' => [ /* See VuFind\Recommend\PluginManager for defaults */ ], 'record_fallbackloader' => [ /* See VuFind\Record\FallbackLoader\PluginManager for defaults */ ], 'recorddriver' => [ /* See VuFind\RecordDriver\PluginManager for defaults */ ], @@ -535,110 +581,7 @@ $config = [ 'search_params' => [ /* See VuFind\Search\Params\PluginManager for defaults */ ], 'search_results' => [ /* See VuFind\Search\Results\PluginManager for defaults */ ], 'session' => [ /* see VuFind\Session\PluginManager for defaults */ ], - ], - // This section behaves just like recorddriver_tabs below, but is used for - // the collection module instead of the standard record view. - 'recorddriver_collection_tabs' => [ - 'VuFind\RecordDriver\AbstractBase' => [ - 'tabs' => [ - 'CollectionList' => 'CollectionList', - 'HierarchyTree' => 'CollectionHierarchyTree', - ], - 'defaultTab' => null, - ], - ], - // This section controls which tabs are used for which record driver classes. - // Each sub-array is a map from a tab name (as used in a record URL) to a tab - // service (found in recordtab plugin manager settings above). If a - // particular record driver is not defined here, it will inherit - // configuration from a configured parent class. The defaultTab setting may - // be used to specify the default active tab; if null, the value from the - // relevant .ini file will be used. You can also specify which tabs are - // loaded in the background when arriving at a record tabs view with - // backgroundLoadedTabs as a list of tab indexes. - 'recorddriver_tabs' => [ - 'VuFind\RecordDriver\EDS' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Pazpar2' => [ - 'tabs' => [ - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Primo' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\SolrAuthDefault' => [ - 'tabs' => [ - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\SolrAuthMarc' => [ - 'tabs' => [ - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\DefaultRecord' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => 'SimilarItemsCarousel', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - // 'backgroundLoadedTabs' => ['UserComments', 'Details'] - ], - 'VuFind\RecordDriver\SolrMarc' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => 'SimilarItemsCarousel', - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Summon' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\WorldCat' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsWorldCat', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], + 'urlshortener' => [ /* see VuFind\UrlShortener\PluginManager for defaults */ ], ], ], // Authorization configuration: @@ -671,9 +614,8 @@ $recordRoutes = [ 'summonrecord' => 'SummonRecord', 'worldcatrecord' => 'WorldcatRecord', 'search2record' => 'Search2Record', - - // For legacy (1.x/2.x) compatibility: - 'vufindrecord' => 'Record', + 'search2collection' => 'Search2Collection', + 'search2collectionrecord' => 'Search2Record', ]; // Define dynamic routes -- controller => [route name => action] @@ -707,19 +649,24 @@ $staticRoutes = [ 'LibGuides/Home', 'LibGuides/Results', 'LibraryCards/Home', 'LibraryCards/SelectCard', 'LibraryCards/DeleteCard', - 'MyResearch/Account', 'MyResearch/ChangePassword', 'MyResearch/CheckedOut', - 'MyResearch/Delete', 'MyResearch/DeleteAccount', 'MyResearch/DeleteList', - 'MyResearch/Edit', 'MyResearch/Email', 'MyResearch/Favorites', + 'MyResearch/Account', 'MyResearch/ChangeEmail', 'MyResearch/ChangePassword', + 'MyResearch/CheckedOut', 'MyResearch/Delete', 'MyResearch/DeleteAccount', + 'MyResearch/DeleteList', 'MyResearch/Edit', 'MyResearch/Email', + 'MyResearch/EmailNotVerified', 'MyResearch/Favorites', 'MyResearch/Fines', 'MyResearch/HistoricLoans', 'MyResearch/Holds', 'MyResearch/Home', 'MyResearch/ILLRequests', 'MyResearch/Logout', 'MyResearch/NewPassword', 'MyResearch/Profile', 'MyResearch/Recover', 'MyResearch/SaveSearch', - 'MyResearch/StorageRetrievalRequests', 'MyResearch/UserLogin', - 'MyResearch/Verify', 'OAI/Server', 'Pazpar2/Home', 'Pazpar2/Search', + 'MyResearch/StorageRetrievalRequests', + 'MyResearch/Unsubscribe', 'MyResearch/UserLogin', + 'MyResearch/Verify', 'MyResearch/VerifyEmail', 'OAI/Server', + 'Overdrive/MyContent','Overdrive/Hold', + 'Pazpar2/Home', 'Pazpar2/Search', 'Primo/Advanced', 'Primo/Home', 'Primo/Search', 'QRCode/Show', 'QRCode/Unavailable', 'Records/Home', 'Relais/Login', 'Relais/Request', - 'Search/Advanced', 'Search/CollectionFacetList', 'Search/Email', + 'Search/Advanced', 'Search/CollectionFacetList', + 'Search/EditMemory', 'Search/Email', 'Search/FacetList', 'Search/History', 'Search/Home', 'Search/NewItem', 'Search/OpenSearch', 'Search/Reserves', 'Search/ReservesFacetList', 'Search/Results', 'Search/Suggest', diff --git a/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql b/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql new file mode 100644 index 0000000000000000000000000000000000000000..275661e1cdaa5ec9c2cab92aab52781dd0a053e3 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.0/001-add-shortlinks-table.sql @@ -0,0 +1,10 @@ +-- +-- Table structure for table shortlinks +-- + +CREATE TABLE shortlinks ( +id SERIAL, +path text, +created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, +PRIMARY KEY (id) +); diff --git a/module/VuFind/sql/migrations/pgsql/6.0/001-modify-user-columns.sql b/module/VuFind/sql/migrations/pgsql/6.0/001-modify-user-columns.sql new file mode 100644 index 0000000000000000000000000000000000000000..146a407fdb58571d3500d2ca71cb923898156865 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.0/001-modify-user-columns.sql @@ -0,0 +1,7 @@ +-- +-- Modifications to table `user` +-- + +ALTER TABLE "user" + ADD COLUMN email_verified timestamp DEFAULT NULL; + diff --git a/module/VuFind/sql/migrations/pgsql/6.1/001-modify-user-columns.sql b/module/VuFind/sql/migrations/pgsql/6.1/001-modify-user-columns.sql new file mode 100644 index 0000000000000000000000000000000000000000..65d8f19f7a08e39e1666f60bdaba20515ca32944 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.1/001-modify-user-columns.sql @@ -0,0 +1,12 @@ +-- +-- Modifications to table `user` +-- + +ALTER TABLE "user" + ADD COLUMN pending_email varchar(255) NOT NULL DEFAULT ''; + +ALTER TABLE "user" + ADD COLUMN user_provided_email boolean NOT NULL DEFAULT '0'; + +ALTER TABLE "user" + ADD COLUMN last_language varchar(30) NOT NULL DEFAULT ''; diff --git a/module/VuFind/sql/migrations/pgsql/6.1/002-modify-id-columns.sql b/module/VuFind/sql/migrations/pgsql/6.1/002-modify-id-columns.sql new file mode 100644 index 0000000000000000000000000000000000000000..f04e288cbec8937d96fce1b2b3ad9a10dc336052 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.1/002-modify-id-columns.sql @@ -0,0 +1,20 @@ +-- +-- Modifications to table `search` +-- + +ALTER TABLE "search" + ALTER COLUMN id SET DATA TYPE bigint; + +-- +-- Modifications to table `session` +-- + +ALTER TABLE "session" + ALTER COLUMN id SET DATA TYPE bigint; + +-- +-- Modifications to table `external_session` +-- + +ALTER TABLE "external_session" + ALTER COLUMN id SET DATA TYPE bigint; diff --git a/module/VuFind/sql/migrations/pgsql/6.1/003-modify-search-table.sql b/module/VuFind/sql/migrations/pgsql/6.1/003-modify-search-table.sql new file mode 100644 index 0000000000000000000000000000000000000000..121aa3c1cd98f42194d2568a313da82604f52979 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.1/003-modify-search-table.sql @@ -0,0 +1,11 @@ +-- +-- Modifications to table `search` +-- + +ALTER TABLE "search" + ADD COLUMN notification_frequency int NOT NULL DEFAULT '0', + ADD COLUMN last_notification_sent timestamp NOT NULL DEFAULT '2000-01-01 00:00:00', + ADD COLUMN notification_base_url varchar(255) NOT NULL DEFAULT ''; + +CREATE INDEX notification_frequency_idx ON search (notification_frequency); +CREATE INDEX notification_base_url_idx ON search (notification_base_url); diff --git a/module/VuFind/sql/migrations/pgsql/6.1/004-add-auth-hash-table.sql b/module/VuFind/sql/migrations/pgsql/6.1/004-add-auth-hash-table.sql new file mode 100644 index 0000000000000000000000000000000000000000..5898a0ebc3eab531d5aa76e1915812090b60de51 --- /dev/null +++ b/module/VuFind/sql/migrations/pgsql/6.1/004-add-auth-hash-table.sql @@ -0,0 +1,15 @@ +-- +-- Table structure for table auth_hash +-- + +CREATE TABLE auth_hash ( +id BIGSERIAL, +session_id varchar(128), +hash varchar(255), +type varchar(50), +data text, +created timestamp NOT NULL default '1970-01-01 00:00:00', +PRIMARY KEY (id), +UNIQUE (hash, type) +); +CREATE INDEX auth_hash_created_idx on auth_hash(created); diff --git a/module/VuFind/sql/mysql.sql b/module/VuFind/sql/mysql.sql index 8d46fbeacf60880b359525c2c309ba9eddf02f0c..d4e1340ae7be69e65246a1f6b1cc25e4ab99b1c3 100644 --- a/module/VuFind/sql/mysql.sql +++ b/module/VuFind/sql/mysql.sql @@ -112,7 +112,7 @@ CREATE TABLE `resource_tags` ( /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `search` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` bigint unsigned NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL DEFAULT '0', `session_id` varchar(128) DEFAULT NULL, `folder_id` int(11) DEFAULT NULL, @@ -121,10 +121,15 @@ CREATE TABLE `search` ( `saved` int(1) NOT NULL DEFAULT '0', `search_object` blob, `checksum` int(11) DEFAULT NULL, + `notification_frequency` int(11) NOT NULL DEFAULT '0', + `last_notification_sent` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', + `notification_base_url` varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `folder_id` (`folder_id`), - KEY `session_id` (`session_id`) + KEY `session_id` (`session_id`), + KEY `notification_frequency` (`notification_frequency`), + KEY `notification_base_url` (`notification_base_url`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; @@ -135,7 +140,7 @@ CREATE TABLE `search` ( /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `session` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` bigint unsigned NOT NULL AUTO_INCREMENT, `session_id` varchar(128) DEFAULT NULL, `data` mediumtext, `last_used` int(12) NOT NULL DEFAULT '0', @@ -153,7 +158,7 @@ CREATE TABLE `session` ( /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `external_session` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` bigint unsigned NOT NULL AUTO_INCREMENT, `session_id` varchar(128) NOT NULL, `external_session_id` varchar(255) NOT NULL, `created` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', @@ -163,6 +168,20 @@ CREATE TABLE `external_session` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `shortlinks` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `shortlinks` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `path` mediumtext NOT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `tags` -- @@ -190,6 +209,9 @@ CREATE TABLE `user` ( `firstname` varchar(50) NOT NULL DEFAULT '', `lastname` varchar(50) NOT NULL DEFAULT '', `email` varchar(255) NOT NULL DEFAULT '', + `email_verified` datetime DEFAULT NULL, + `pending_email` varchar(255) NOT NULL DEFAULT '', + `user_provided_email` tinyint(1) NOT NULL DEFAULT '0', `cat_id` varchar(255) DEFAULT NULL, `cat_username` varchar(50) DEFAULT NULL, `cat_password` varchar(70) DEFAULT NULL, @@ -201,6 +223,7 @@ CREATE TABLE `user` ( `verify_hash` varchar(42) NOT NULL DEFAULT '', `last_login` datetime NOT NULL DEFAULT '2000-01-01 00:00:00', `auth_method` varchar(50) DEFAULT NULL, + `last_language` varchar(30) NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`), UNIQUE KEY `cat_id` (`cat_id`) @@ -299,3 +322,24 @@ CREATE TABLE `record` ( UNIQUE KEY `record_id_source` (`record_id`, `source`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `auth_hash` +-- + +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `auth_hash` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `session_id` varchar(128) DEFAULT NULL, + `hash` varchar(255) NOT NULL DEFAULT '', + `type` varchar(50) DEFAULT NULL, + `data` mediumtext, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `session_id` (`session_id`), + UNIQUE KEY `hash_type` (`hash`, `type`), + KEY `created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + diff --git a/module/VuFind/sql/pgsql.sql b/module/VuFind/sql/pgsql.sql index c92723756a2925261c632607db7af0f1fa4a332b..39abd1cbc298764d4e2d35c13e6a6ecd528f37b7 100644 --- a/module/VuFind/sql/pgsql.sql +++ b/module/VuFind/sql/pgsql.sql @@ -69,7 +69,7 @@ CREATE INDEX resource_tags_list_id_idx ON resource_tags (list_id); DROP TABLE IF EXISTS "search"; CREATE TABLE search ( -id SERIAL, +id BIGSERIAL, user_id int NOT NULL DEFAULT '0', session_id varchar(128), folder_id int DEFAULT NULL, @@ -78,11 +78,31 @@ title varchar(20) DEFAULT NULL, saved int NOT NULL DEFAULT '0', search_object bytea, checksum int DEFAULT NULL, +notification_frequency int NOT NULL DEFAULT '0', +last_notification_sent timestamp NOT NULL DEFAULT '2000-01-01 00:00:00', +notification_base_url varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (id) ); CREATE INDEX search_user_id_idx ON search (user_id); CREATE INDEX search_folder_id_idx ON search (folder_id); CREATE INDEX session_id_idx ON search (session_id); +CREATE INDEX notification_frequency_idx ON search (notification_frequency); +CREATE INDEX notification_base_url_idx ON search (notification_base_url); + +-- -------------------------------------------------------- + +-- +-- Table structure for table shortlinks +-- + +DROP TABLE IF EXISTS "shortlinks"; + +CREATE TABLE shortlinks ( +id SERIAL, +path text, +created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, +PRIMARY KEY (id) +); -- -------------------------------------------------------- @@ -115,6 +135,9 @@ pass_hash varchar(60) DEFAULT NULL, firstname varchar(50) NOT NULL DEFAULT '', lastname varchar(50) NOT NULL DEFAULT '', email varchar(255) NOT NULL DEFAULT '', +email_verified timestamp DEFAULT NULL, +pending_email varchar(255) NOT NULL DEFAULT '', +user_provided_email boolean NOT NULL DEFAULT '0', cat_id varchar(255) DEFAULT NULL, cat_username varchar(50) DEFAULT NULL, cat_password varchar(70) DEFAULT NULL, @@ -126,6 +149,7 @@ created timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', verify_hash varchar(42) NOT NULL DEFAULT '', last_login timestamp NOT NULL DEFAULT '1970-01-01 00:00:00', auth_method varchar(50) DEFAULT NULL, +last_language varchar(30) NOT NULL DEFAULT '', PRIMARY KEY (id), UNIQUE (username), UNIQUE (cat_id) @@ -183,7 +207,7 @@ CREATE INDEX user_resource_list_id_idx ON user_resource (list_id); DROP TABLE IF EXISTS "session"; CREATE TABLE session ( -id SERIAL, +id BIGSERIAL, session_id varchar(128), data text, last_used int NOT NULL default 0, @@ -200,7 +224,7 @@ CREATE INDEX last_used_idx on session(last_used); DROP TABLE IF EXISTS "external_session"; CREATE TABLE external_session ( -id SERIAL, +id BIGSERIAL, session_id varchar(128) NOT NULL, external_session_id varchar(255) NOT NULL, created timestamp NOT NULL default '1970-01-01 00:00:00', @@ -282,6 +306,24 @@ CONSTRAINT user_card_ibfk_1 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELE CREATE INDEX user_card_cat_username_idx ON user_card (cat_username); CREATE INDEX user_card_user_id_idx ON user_card (user_id); +-- +-- Table structure for table auth_hash +-- + +DROP TABLE IF EXISTS "auth_hash"; + +CREATE TABLE auth_hash ( +id BIGSERIAL, +session_id varchar(128), +hash varchar(255), +type varchar(50), +data text, +created timestamp NOT NULL default '1970-01-01 00:00:00', +PRIMARY KEY (id), +UNIQUE (hash, type) +); +CREATE INDEX auth_hash_created_idx on auth_hash(created); + -- -------------------------------------------------------- -- diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php index 19b2b2c6c3a3fc7650d6baf6c34497edc74142e8..c3f04c2ff316a68aba6aa381a6c84ba88ee509ab 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractIlsAndUserActionFactory.php @@ -61,10 +61,10 @@ class AbstractIlsAndUserActionFactory array $options = null ) { return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\ILS\Connection'), - $container->get('VuFind\Auth\ILSAuthenticator'), - $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\ILS\Connection::class), + $container->get(\VuFind\Auth\ILSAuthenticator::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(), ...($options ?: []) ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php index 7191dedc770f286acd82fbeeb74160052054132d..b4b7d987eddf7710866244c703525dfa8876e0a6 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/AbstractRelaisActionFactory.php @@ -63,10 +63,10 @@ class AbstractRelaisActionFactory if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $user = $container->get('VuFind\Auth\Manager')->isLoggedIn(); + $user = $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(); return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Connection\Relais'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Connection\Relais::class), $user ?: null ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php b/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php index 2e9e9b1d1c20e5346d6750e92beede1eef7c9cab..3eb2ebca3ed26ba155bc9fe30ede5585dada4315 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/CommentRecordFactory.php @@ -62,13 +62,14 @@ class CommentRecordFactory implements \Zend\ServiceManager\Factory\FactoryInterf if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + $tablePluginManager = $container->get(\VuFind\Db\Table\PluginManager::class); $controllerPluginManager = $container->get('ControllerPluginManager'); - $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); return new $requestedName( - $tablePluginManager->get('VuFind\Db\Table\Resource'), - $controllerPluginManager->get('VuFind\Controller\Plugin\Recaptcha'), - $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $tablePluginManager->get(\VuFind\Db\Table\Resource::class), + $controllerPluginManager + ->get(\VuFind\Controller\Plugin\Recaptcha::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(), $capabilities->getCommentSetting() !== 'disabled' ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php index 95cadcf5e32cbfa44a0344a1013ebb39ce801289..3ec532e9246e7e7ca160eea7433111d8f965b8af 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/DeleteRecordCommentFactory.php @@ -63,11 +63,11 @@ class DeleteRecordCommentFactory if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); - $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + $tablePluginManager = $container->get(\VuFind\Db\Table\PluginManager::class); + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); return new $requestedName( - $tablePluginManager->get('VuFind\Db\Table\Comments'), - $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $tablePluginManager->get(\VuFind\Db\Table\Comments::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(), $capabilities->getCommentSetting() !== 'disabled' ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/DoiLookupFactory.php b/module/VuFind/src/VuFind/AjaxHandler/DoiLookupFactory.php index fb41749f91e84acb254463b29a21f84b861489e4..d7a1b30c81268c247cbbe3bcc12b4c276fa674aa 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/DoiLookupFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/DoiLookupFactory.php @@ -62,8 +62,9 @@ class DoiLookupFactory implements \Zend\ServiceManager\Factory\FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); - $pluginManager = $container->get('VuFind\DoiLinker\PluginManager'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $pluginManager = $container->get(\VuFind\DoiLinker\PluginManager::class); return new $requestedName($pluginManager, $config->DOI->resolver ?? null); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php index d00b91d9a7b3959a7f3631910720e2792b36e77e..6006304bbbf74f6c05f30fbec4d7879e4d3d8194 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetACSuggestionsFactory.php @@ -64,8 +64,8 @@ class GetACSuggestionsFactory implements throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Autocomplete\Suggester') + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Autocomplete\Suggester::class) ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php b/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php index d211fe353fc79a62710a3885653d5ba5ed30fe6e..69d57c6223b42af51dafd120878132c15de8f913 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetFacetData.php @@ -108,11 +108,7 @@ class GetFacetData extends AbstractBase $facets = []; } else { $facetList = $facets[$facet]['data']['list']; - - if (!empty($sort)) { - $this->facetHelper->sortFacetList($facetList, $sort == 'top'); - } - + $this->facetHelper->sortFacetList($facetList, $sort); $facets = $this->facetHelper->buildFacetArray( $facet, $facetList, $results->getUrlQuery(), false ); diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php index 3517a19930b05b2d092b08f67018ecd1c2464d57..63fabc1d84b631f99a4c5c5f71c1fc53f962ac88 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetFacetDataFactory.php @@ -63,9 +63,9 @@ class GetFacetDataFactory implements \Zend\ServiceManager\Factory\FactoryInterfa throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Search\Solr\HierarchicalFacetHelper'), - $container->get('VuFind\Search\Results\PluginManager') + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class), + $container->get(\VuFind\Search\Results\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php index 75547b84c562eba219287f566379d536f48fb53e..8409ebec5a70274ea6290d7722fdf14e183e5fb3 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetIlsStatusFactory.php @@ -63,8 +63,8 @@ class GetIlsStatusFactory implements \Zend\ServiceManager\Factory\FactoryInterfa throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\ILS\Connection'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\ILS\Connection::class), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php index f4bb3289d83e096af65818fbad174d6d5b39d97d..b08728276fb7d84f2bbbe17a7b22b99b922651d2 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatuses.php @@ -139,9 +139,7 @@ class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface { $transList = []; foreach ($list as $current) { - $transList[] = $this->translate( - $transPrefix . $current, [], $current - ); + $transList[] = $this->translateWithPrefix($transPrefix, $current); } return $transList; } @@ -166,9 +164,10 @@ class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface // If there is only one value in the list, or if we're in "first" mode, // send back the first list value: if ($mode == 'first' || count($list) == 1) { - return $transPrefix - ? $this->translate($transPrefix . $list[0], [], $list[0]) - : $list[0]; + if ($transPrefix) { + return $this->translateWithPrefix($transPrefix, $list[0]); + } + return $list[0]; } elseif (count($list) == 0) { // Empty list? Return a blank string: return ''; @@ -331,7 +330,7 @@ class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface protected function getItemStatusGroup($record, $messages, $callnumberSetting) { // Summarize call number, location and availability info across all items: - $locations = []; + $locations = []; $use_unknown_status = $available = false; foreach ($record as $info) { // Find an available copy @@ -364,7 +363,7 @@ class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface 'availability' => $details['available'] ?? false, 'location' => htmlentities( - $this->translate('location_' . $location, [], $location), + $this->translateWithPrefix('location_', $location), ENT_COMPAT, 'UTF-8' ), 'callnumbers' => @@ -494,8 +493,9 @@ class GetItemStatuses extends AbstractBase implements TranslatorAwareInterface $record, $messages, $locationSetting, $callnumberSetting ); } - // If a full status display has been requested, append the HTML: - if ($showFullStatus) { + // If a full status display has been requested and no errors were + // encountered, append the HTML: + if ($showFullStatus && empty($record[0]['error'])) { $current['full_status'] = $this->renderer->render( 'ajax/status-full.phtml', [ 'statusItems' => $record, diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php index f0f50fc838b7a1a171b95f6e943ce7c6613580e4..ceb821a0d9a8c51e5571b55b308bb7726e9764c6 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetItemStatusesFactory.php @@ -63,11 +63,11 @@ class GetItemStatusesFactory implements \Zend\ServiceManager\Factory\FactoryInte throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Config\PluginManager')->get('config'), - $container->get('VuFind\ILS\Connection'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\ILS\Connection::class), $container->get('ViewRenderer'), - $container->get('VuFind\ILS\Logic\Holds') + $container->get(\VuFind\ILS\Logic\Holds::class) ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php b/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php index bf22e820f581229ec24baea9dcfbc0ab729acaec..670f8285f9d566acf0f83c422a6a65c92f9a66fb 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetLibraryPickupLocations.php @@ -75,10 +75,8 @@ class GetLibraryPickupLocations extends AbstractIlsAndUserAction ->getILLPickupLocations($id, $pickupLib, $patron); foreach ($results as &$result) { if (isset($result['name'])) { - $result['name'] = $this->translate( - 'location_' . $result['name'], - [], - $result['name'] + $result['name'] = $this->translateWithPrefix( + 'location_', $result['name'] ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php index aa5cad433b08f412c4b37497048db073ae0bbbdb..2d59fa021fa58f03410247b2c1351db16941b5f2 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordCommentsAsHTMLFactory.php @@ -64,7 +64,7 @@ class GetRecordCommentsAsHTMLFactory throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Record\Loader'), + $container->get(\VuFind\Record\Loader::class), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php index e8c4a1a866d2b3938fa27ae90d9c570c638b9850..f30d23363b61baf455a42bee72e055d27aa45cd7 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php @@ -28,7 +28,7 @@ namespace VuFind\AjaxHandler; use VuFind\Record\Loader; -use VuFind\RecordTab\PluginManager as TabManager; +use VuFind\RecordTab\TabManager; use Zend\Http\PhpEnvironment\Request; use Zend\Mvc\Controller\Plugin\Params; use Zend\View\Renderer\RendererInterface; @@ -72,7 +72,7 @@ class GetRecordDetails extends AbstractBase * * @var TabManager */ - protected $pluginManager; + protected $tabManager; /** * View renderer @@ -87,16 +87,16 @@ class GetRecordDetails extends AbstractBase * @param array $config ZF configuration * @param Request $request HTTP request * @param Loader $loader Record loader - * @param TabManager $pm RecordTab plugin manager + * @param TabManager $tm Record Tab manager * @param RendererInterface $renderer Renderer */ public function __construct(array $config, Request $request, Loader $loader, - TabManager $pm, RendererInterface $renderer + TabManager $tm, RendererInterface $renderer ) { $this->config = $config; $this->request = $request; $this->recordLoader = $loader; - $this->pluginManager = $pm; + $this->tabManager = $tm; $this->renderer = $renderer; } @@ -115,11 +115,8 @@ class GetRecordDetails extends AbstractBase '/\W/', '', trim(strtolower($params->fromQuery('type'))) ); - $details = $this->pluginManager->getTabDetailsForRecord( - $driver, - $this->config['vufind']['recorddriver_tabs'], - $this->request, - 'Information' + $details = $this->tabManager->getTabDetailsForRecord( + $driver, $this->request, 'Information' ); $html = $this->renderer->render( @@ -128,9 +125,8 @@ class GetRecordDetails extends AbstractBase 'defaultTab' => $details['default'], 'driver' => $driver, 'tabs' => $details['tabs'], - 'backgroundTabs' => $this->pluginManager->getBackgroundTabNames( - $driver, $this->config['vufind']['recorddriver_tabs'] - ) + 'backgroundTabs' => $this->tabManager + ->getBackgroundTabNames($driver), ] ); return $this->formatResponse(compact('html')); diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php index 1174242c9abbdf59abc6b1dc775f558e02973b0c..8730340ed54abb6095cd599f28e85d0464a5523d 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php @@ -66,8 +66,8 @@ class GetRecordDetailsFactory return new $requestedName( $container->get('Config'), $container->get('Request'), - $container->get('VuFind\Record\Loader'), - $container->get('VuFind\RecordTab\PluginManager'), + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\RecordTab\TabManager::class), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php index 8e4d746e203502885bf69a6fd3a535267dd4829f..b643a7596ff4ed6db56e618fdb342452753f5e3e 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordTagsFactory.php @@ -62,10 +62,10 @@ class GetRecordTagsFactory implements \Zend\ServiceManager\Factory\FactoryInterf if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + $tablePluginManager = $container->get(\VuFind\Db\Table\PluginManager::class); return new $requestedName( - $tablePluginManager->get('VuFind\Db\Table\Tags'), - $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $tablePluginManager->get(\VuFind\Db\Table\Tags::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php b/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php index 82e82f02ad0bfdba517ce7e651e217e3ff97cd8a..73ab22d9cfa51840ed579d891dd7f44e00479d99 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRequestGroupPickupLocations.php @@ -77,9 +77,8 @@ class GetRequestGroupPickupLocations extends AbstractIlsAndUserAction $results = $this->ils->getPickupLocations($patron, $details); foreach ($results as &$result) { if (isset($result['locationDisplay'])) { - $result['locationDisplay'] = $this->translate( - 'location_' . $result['locationDisplay'], - [], + $result['locationDisplay'] = $this->translateWithPrefix( + 'location_', $result['locationDisplay'] ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php index fc5072f68e45262a04ebb25fdf805b69a37010cf..55d1f52103baa12bce6c719499cca6dfad465c75 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetResolverLinksFactory.php @@ -64,10 +64,10 @@ class GetResolverLinksFactory throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Resolver\Driver\PluginManager'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Resolver\Driver\PluginManager::class), $container->get('ViewRenderer'), - $container->get('VuFind\Config\PluginManager')->get('config') + $container->get(\VuFind\Config\PluginManager::class)->get('config') ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php index 7429b2565081b68238d0719c9778f7cc713fc837..cdad3bdcff3c5bc2fed6ff26802167a5e4dcc99d 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetSaveStatusesFactory.php @@ -63,8 +63,8 @@ class GetSaveStatusesFactory implements \Zend\ServiceManager\Factory\FactoryInte throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Auth\Manager')->isLoggedIn(), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn(), $container->get('ControllerPluginManager')->get('url') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetSideFacets.php b/module/VuFind/src/VuFind/AjaxHandler/GetSideFacets.php index b8568d7a5a39e1cdbb3c20c1f9c42a07a28f519b..237b28cb7811ba5dc20e8f5c2e66d73505328dd2 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetSideFacets.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetSideFacets.php @@ -132,7 +132,13 @@ class GetSideFacets extends \VuFind\AjaxHandler\AbstractBase return $this->formatResponse('', self::STATUS_HTTP_ERROR); } - $recommend = $results->getRecommendations($configLocation)[0]; + $recommend = $results->getRecommendations($configLocation)[0] ?? null; + if (null === $recommend) { + return $this->formatResponse( + 'Invalid config requested', + self::STATUS_HTTP_BAD_REQUEST + ); + } $context = [ 'recommend' => $recommend, @@ -235,6 +241,7 @@ class GetSideFacets extends \VuFind\AjaxHandler\AbstractBase } else { $context['facet'] = $facet; $context['cluster'] = $facetSet[$facet] ?? []; + $context['collapsedFacets'] = []; $response[$facet]['html'] = $this->renderer->render( 'Recommend/SideFacets/facet.phtml', $context diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetSideFacetsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetSideFacetsFactory.php index 9ea6a726bd58d091f57336b643f5b481fbab269a..420747730f597c251b065fce68ee70dd2caa4ada 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetSideFacetsFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetSideFacetsFactory.php @@ -63,11 +63,11 @@ class GetSideFacetsFactory implements \Zend\ServiceManager\Factory\FactoryInterf throw new \Exception('Unexpected options passed to factory.'); } $result = new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Recommend\PluginManager'), - $container->get('VuFind\SearchRunner'), - $container->get('VuFind\Search\Solr\HierarchicalFacetHelper'), - $container->get('VuFind\Config\PluginManager')->get('facets'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Recommend\PluginManager::class), + $container->get(\VuFind\SearchRunner::class), + $container->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class), + $container->get(\VuFind\Config\PluginManager::class)->get('facets'), $container->get('ViewRenderer') ); return $result; diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php b/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php index 79dc5421816ec2a255e0190c8d922b80ed086ecd..410a44ad8033c972ead88fb94fe36288c1db0f52 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetVisData.php @@ -148,7 +148,7 @@ class GetVisData extends AbstractBase } $paramsObj->getOptions()->disableHighlighting(); $paramsObj->getOptions()->spellcheckEnabled(false); - $filters = $paramsObj->getFilters(); + $filters = $paramsObj->getRawFilters(); $rawDateFacets = $params->fromQuery('facetFields'); $dateFacets = empty($rawDateFacets) ? [] : explode(':', $rawDateFacets); $fields = $this->processDateFacets($filters, $dateFacets); diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php index 8835cf458ab893213a26d3152a19c0bb156dd8d4..ab528e3c55a9fb2bb6722720f292f2e74fcddc97 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetVisDataFactory.php @@ -63,8 +63,8 @@ class GetVisDataFactory implements \Zend\ServiceManager\Factory\FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Search\Results\PluginManager')->get('Solr') + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Search\Results\PluginManager::class)->get('Solr') ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php b/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php index e229b158bfe8bb126896286864ba93ce2c131f16..824a091815da1f30ca57672af144a5a01c5cc62a 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/KeepAliveFactory.php @@ -62,6 +62,8 @@ class KeepAliveFactory implements \Zend\ServiceManager\Factory\FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - return new $requestedName($container->get('Zend\Session\SessionManager')); + return new $requestedName( + $container->get(\Zend\Session\SessionManager::class) + ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php b/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php index b445a47cee8434a767042663f793a9f97a837118..06a0de3a6b01ecd64362ba0c778d88f64750ef63 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/RecommendFactory.php @@ -28,6 +28,7 @@ namespace VuFind\AjaxHandler; use Interop\Container\ContainerInterface; +use VuFind\Search\Results\PluginManager as ResultsManager; /** * Factory for Recommend AJAX handler. @@ -63,9 +64,9 @@ class RecommendFactory implements \Zend\ServiceManager\Factory\FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Session\Settings'), - $container->get('VuFind\Recommend\PluginManager'), - $container->get('VuFind\Search\Results\PluginManager')->get('Solr'), + $container->get(\VuFind\Session\Settings::class), + $container->get(\VuFind\Recommend\PluginManager::class), + $container->get(ResultsManager::class)->get('Solr'), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php b/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php index d8f08e00f3155ab6880ed948dc7aed3c9bf281fe..fbe8e953a5e7cf9ab4be2d71e91601aac0ffcb68 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/SystemStatusFactory.php @@ -62,12 +62,12 @@ class SystemStatusFactory implements \Zend\ServiceManager\Factory\FactoryInterfa if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $tablePluginManager = $container->get('VuFind\Db\Table\PluginManager'); + $tablePluginManager = $container->get(\VuFind\Db\Table\PluginManager::class); return new $requestedName( - $container->get('Zend\Session\SessionManager'), - $container->get('VuFind\Search\Results\PluginManager'), - $container->get('VuFind\Config\PluginManager')->get('config'), - $tablePluginManager->get('VuFind\Db\Table\Session') + $container->get(\Zend\Session\SessionManager::class), + $container->get(\VuFind\Search\Results\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $tablePluginManager->get(\VuFind\Db\Table\Session::class) ); } } diff --git a/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php b/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php index 97a1e947396bf39d7ad532ed490a56cf2a699d9a..8f75dddafb5c08eced249b53c0f53112b6428b63 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/TagRecordFactory.php @@ -63,9 +63,9 @@ class TagRecordFactory implements \Zend\ServiceManager\Factory\FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Record\Loader'), - $container->get('VuFind\Tags'), - $container->get('VuFind\Auth\Manager')->isLoggedIn() + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Tags::class), + $container->get(\VuFind\Auth\Manager::class)->isLoggedIn() ); } } diff --git a/module/VuFind/src/VuFind/Auth/AbstractBase.php b/module/VuFind/src/VuFind/Auth/AbstractBase.php index b4eb40ab255afc8abf5342de15bd3e69c1136f71..e01ecb93d2e11f5466e7312cd5a4280da530dae1 100644 --- a/module/VuFind/src/VuFind/Auth/AbstractBase.php +++ b/module/VuFind/src/VuFind/Auth/AbstractBase.php @@ -121,6 +121,36 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface, $this->configValidated = false; } + /** + * Whether this authentication method needs CSRF checking for the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function needsCsrfCheck($request) + { + // Enabled by default + return true; + } + + /** + * Returns any authentication method this request should be delegated to. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return string|bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getDelegateAuthMethod(\Zend\Http\PhpEnvironment\Request $request) + { + // No delegate by default + return false; + } + /** * Validate configuration parameters. This is a support method for getConfig(), * so the configuration MUST be accessed using $this->config; do not call diff --git a/module/VuFind/src/VuFind/Auth/CAS.php b/module/VuFind/src/VuFind/Auth/CAS.php index 7194eab6a1b2dd0ffb4f47643b356f36f0a1a08b..341ea989e42adb3c10af8e2668d90eebea9f2588 100644 --- a/module/VuFind/src/VuFind/Auth/CAS.php +++ b/module/VuFind/src/VuFind/Auth/CAS.php @@ -142,7 +142,9 @@ class CAS extends AbstractBase foreach ($attribsToCheck as $attribute) { if (isset($cas->$attribute)) { $value = $casauth->getAttribute($cas->$attribute); - if ($attribute != 'cat_password') { + if ($attribute == 'email') { + $user->updateEmail($value); + } elseif ($attribute != 'cat_password') { $user->$attribute = ($value === null) ? '' : $value; } else { $catPassword = $value; @@ -283,8 +285,9 @@ class CAS extends AbstractBase ) { $casauth->setDebug($cas->log); } + $protocol = constant($cas->protocol ?? 'SAML_VERSION_1_1'); $casauth->client( - SAML_VERSION_1_1, $cas->server, (int)$cas->port, $cas->context, false + $protocol, $cas->server, (int)$cas->port, $cas->context, false ); if (isset($cas->CACert) && !empty($cas->CACert)) { $casauth->setCasServerCACert($cas->CACert); diff --git a/module/VuFind/src/VuFind/Auth/ChoiceAuth.php b/module/VuFind/src/VuFind/Auth/ChoiceAuth.php index f7aa7521e775453c8b8eb2574863a3bffc865274..adbbeb42d4a04460937777b24ca20daaff796f83 100644 --- a/module/VuFind/src/VuFind/Auth/ChoiceAuth.php +++ b/module/VuFind/src/VuFind/Auth/ChoiceAuth.php @@ -323,6 +323,34 @@ class ChoiceAuth extends AbstractBase return $this->proxyAuthMethod('updatePassword', func_get_args()); } + /** + * Returns any authentication method this request should be delegated to. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return string|bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getDelegateAuthMethod(\Zend\Http\PhpEnvironment\Request $request) + { + return $this->proxyAuthMethod('getDelegateAuthMethod', func_get_args()); + } + + /** + * Is the configured strategy on the list of legal options? + * + * @return bool + */ + protected function hasLegalStrategy() + { + // Do a case-insensitive search of the strategy list: + return in_array( + strtolower($this->strategy), + array_map('strtolower', $this->strategies) + ); + } + /** * Proxy auth method; a helper function to be called like: * return $this->proxyAuthMethod(METHOD, func_get_args()); @@ -340,7 +368,7 @@ class ChoiceAuth extends AbstractBase return false; } - if (!in_array($this->strategy, $this->strategies)) { + if (!$this->hasLegalStrategy()) { throw new InvalidArgumentException("Illegal setting: {$this->strategy}"); } $authenticator = $this->getPluginManager()->get($this->strategy); @@ -420,4 +448,21 @@ class ChoiceAuth extends AbstractBase } return isset($user) && $user instanceof User; } + + /** + * Whether this authentication method needs CSRF checking for the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return bool + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function needsCsrfCheck($request) + { + if (!$this->strategy) { + return true; + } + return $this->proxyAuthMethod('needsCsrfCheck', func_get_args()); + } } diff --git a/module/VuFind/src/VuFind/Auth/ChoiceAuthFactory.php b/module/VuFind/src/VuFind/Auth/ChoiceAuthFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4a298a13e6e5aa27c5d20ff51f52b01b6da42563 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/ChoiceAuthFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for ChoiceAuth authentication module. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for ChoiceAuth authentication module. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ChoiceAuthFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $session = new \Zend\Session\Container( + 'ChoiceAuth', $container->get(\Zend\Session\SessionManager::class) + ); + $auth = new $requestedName($session); + $auth->setPluginManager($container->get(PluginManager::class)); + return $auth; + } +} diff --git a/module/VuFind/src/VuFind/Auth/Database.php b/module/VuFind/src/VuFind/Auth/Database.php index 307802c0d6f7f7ddcb8faab9f9b54ad918e611ab..f81a82eae6140528a2d5e1ad158262bb2394b068 100644 --- a/module/VuFind/src/VuFind/Auth/Database.php +++ b/module/VuFind/src/VuFind/Auth/Database.php @@ -29,8 +29,10 @@ */ namespace VuFind\Auth; +use VuFind\Db\Row\User; use VuFind\Db\Table\User as UserTable; use VuFind\Exception\Auth as AuthException; +use VuFind\Exception\AuthEmailNotVerified as AuthEmailNotVerifiedException; use Zend\Crypt\Password\Bcrypt; use Zend\Http\PhpEnvironment\Request; @@ -67,7 +69,7 @@ class Database extends AbstractBase * @param Request $request Request object containing account credentials. * * @throws AuthException - * @return \VuFind\Db\Row\User Object representing logged-in user. + * @return User Object representing logged-in user. */ public function authenticate($request) { @@ -84,6 +86,9 @@ class Database extends AbstractBase throw new AuthException('authentication_error_invalid'); } + // Verify email address: + $this->checkEmailVerified($user); + // If we got this far, the login was successful: return $user; } @@ -106,7 +111,7 @@ class Database extends AbstractBase * @param Request $request Request object containing new account details. * * @throws AuthException - * @return \VuFind\Db\Row\User New user row. + * @return User New user row. */ public function create($request) { @@ -126,6 +131,9 @@ class Database extends AbstractBase $user = $this->createUserFromParams($params, $userTable); $user->save(); + // Verify email address: + $this->checkEmailVerified($user); + return $user; } @@ -135,7 +143,7 @@ class Database extends AbstractBase * @param Request $request Request object containing new account details. * * @throws AuthException - * @return \VuFind\Db\Row\User New user row. + * @return User New user row. */ public function updatePassword($request) { @@ -190,6 +198,28 @@ class Database extends AbstractBase $this->validatePasswordAgainstPolicy($params['password']); } + /** + * Check if the user's email address has been verified (if necessary) and + * throws exception if not. + * + * @param User $user User to check + * + * @return void + * @throws AuthEmailNotVerifiedException + */ + protected function checkEmailVerified($user) + { + $config = $this->getConfig(); + $verify_email = $config->Authentication->verify_email ?? false; + if ($verify_email && !$user->checkEmailVerified()) { + $exception = new AuthEmailNotVerifiedException( + 'authentication_error_email_not_verified_html' + ); + $exception->user = $user; + throw $exception; + } + } + /** * Check that the user's password matches the provided value. * @@ -358,14 +388,14 @@ class Database extends AbstractBase * @param string[] $params Parameters returned from collectParamsFromRequest() * @param UserTable $table The VuFind user table * - * @return \VuFind\Db\Row\User A user row object + * @return User A user row object */ protected function createUserFromParams($params, $table) { $user = $table->createRowForUsername($params['username']); $user->firstname = $params['firstname']; $user->lastname = $params['lastname']; - $user->email = $params['email']; + $user->updateEmail($params['email'], true); if ($this->passwordHashingEnabled()) { $bcrypt = new Bcrypt(); $user->pass_hash = $bcrypt->create($params['password']); diff --git a/module/VuFind/src/VuFind/Auth/Email.php b/module/VuFind/src/VuFind/Auth/Email.php new file mode 100644 index 0000000000000000000000000000000000000000..9c9e9a0ee7d04f8af008182e2402be458688a722 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/Email.php @@ -0,0 +1,162 @@ +<?php +/** + * Email authentication module. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +namespace VuFind\Auth; + +use VuFind\Exception\Auth as AuthException; + +/** + * Email authentication module. + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +class Email extends AbstractBase +{ + /** + * Email Authenticator + * + * @var EmailAuthenticator + */ + protected $emailAuthenticator; + + /** + * Constructor + * + * @param EmailAuthenticator $emailAuth Email authenticator + */ + public function __construct(EmailAuthenticator $emailAuth) + { + $this->emailAuthenticator = $emailAuth; + } + + /** + * Attempt to authenticate the current user. Throws exception if login fails. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * account credentials. + * + * @throws AuthException + * @return \VuFind\Db\Row\User Object representing logged-in user. + */ + public function authenticate($request) + { + // This is a dual-mode method: + // First, try to find a user account with the provided email address and send + // a login link. + // Second, log the user in with the hash from the login link. + + $email = trim($request->getPost()->get('username')); + $hash = $request->getQuery('hash'); + if (!$email && !$hash) { + throw new AuthException('authentication_error_blank'); + } + + if (!$hash) { + // Validate the credentials: + $user = $this->getUserTable()->getByEmail($email, false); + if ($user) { + $loginData = [ + 'vufind_id' => $user['id'] + ]; + $this->emailAuthenticator->sendAuthenticationLink( + $user['email'], + $loginData, + ['auth_method' => 'email'] + ); + } + // Don't reveal the result + throw new \VuFind\Exception\AuthInProgress('email_login_link_sent'); + } + + $loginData = $this->emailAuthenticator->authenticate($hash); + if (isset($loginData['vufind_id'])) { + return $this->getUserTable()->getById($loginData['vufind_id']); + } else { + return $this->processUser($loginData); + } + + // If we got this far, we have a problem: + throw new AuthException('authentication_error_invalid'); + } + + /** + * Whether this authentication method needs CSRF checking for the request. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return bool + */ + public function needsCsrfCheck($request) + { + // Disable CSRF if we get a hash in the request + return $request->getQuery('hash') ? false : true; + } + + /** + * Update the database using login user details, then return the User object. + * + * @param array $info User details returned by the login initiator like ILS. + * + * @throws AuthException + * @return \VuFind\Db\Row\User Processed User object. + */ + protected function processUser($info) + { + // Check to see if we already have an account for this user: + $userTable = $this->getUserTable(); + if (!empty($info['id'])) { + $user = $userTable->getByCatalogId($info['id']); + if (empty($user)) { + $user = $userTable->getByUsername($info['email']); + $user->saveCatalogId($info['id']); + } + } else { + $user = $userTable->getByUsername($info['email']); + } + + // No need to store a password in VuFind's main password field: + $user->password = ''; + + // Update user information based on received data: + $fields = ['firstname', 'lastname', 'email', 'major', 'college']; + foreach ($fields as $field) { + $user->$field = $info[$field] ?? ' '; + } + + // Update the user in the database, then return it to the caller: + $user->saveCredentials( + $info['cat_username'] ?? ' ', + $info['cat_password'] ?? ' ' + ); + + return $user; + } +} diff --git a/module/VuFind/src/VuFind/Auth/EmailAuthenticator.php b/module/VuFind/src/VuFind/Auth/EmailAuthenticator.php new file mode 100644 index 0000000000000000000000000000000000000000..19f1faa775e9c54f9cb1c593488ab8054c52965f --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/EmailAuthenticator.php @@ -0,0 +1,252 @@ +<?php +/** + * Class for managing email-based authentication. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +namespace VuFind\Auth; + +use VuFind\DB\Table\AuthHash as AuthHashTable; +use VuFind\Exception\Auth as AuthException; +use Zend\Http\PhpEnvironment\RemoteAddress; + +/** + * Class for managing email-based authentication. + * + * This class provides functionality for authentication based on a known-valid email + * address. + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:authentication_handlers Wiki + */ +class EmailAuthenticator implements \VuFind\I18n\Translator\TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Session Manager + * + * @var \Zend\Session\SessionManager + */ + protected $sessionManager = null; + + /** + * CSRF Validator + * + * @var \VuFind\Validator\Csrf $csrf CSRF validator + */ + protected $csrf = null; + + /** + * Mailer + * + * @var \VuFind\Mailer\Mailer + */ + protected $mailer = null; + + /** + * View Renderer + * + * @var \Zend\View\Renderer\RendererInterface + */ + protected $viewRenderer = null; + + /** + * Remote address + * + * @var RemoteAddress + */ + protected $remoteAddress; + + /** + * Configuration + * + * @var \Zend\Config\Config + */ + protected $config; + + /** + * How long a login request is considered to be valid (seconds) + * + * @var int + */ + protected $loginRequestValidTime = 600; + + /** + * Database table for authentication hashes + * + * @var AuthHashTable + */ + protected $authHashTable; + + /** + * Constructor + * + * @param \Zend\Session\SessionManager $session Session Manager + * @param \VuFind\Validator\Csrf $csrf CSRF Validator + * @param \VuFind\Mailer\Mailer $mailer Mailer + * @param \Zend\View\Renderer\RendererInterface $viewRenderer View Renderer + * @param RemoteAddress $remoteAddr Remote address + * @param \Zend\Config\Config $config Configuration + * @param AuthHashTable $authHash AuthHash Table + */ + public function __construct(\Zend\Session\SessionManager $session, + \VuFind\Validator\Csrf $csrf, \VuFind\Mailer\Mailer $mailer, + \Zend\View\Renderer\RendererInterface $viewRenderer, + RemoteAddress $remoteAddr, + \Zend\Config\Config $config, AuthHashTable $authHash + ) { + $this->sessionManager = $session; + $this->csrf = $csrf; + $this->mailer = $mailer; + $this->viewRenderer = $viewRenderer; + $this->remoteAddress = $remoteAddr; + $this->config = $config; + $this->authHashTable = $authHash; + } + + /** + * Send an email authentication link to the specified email address. + * + * Stores the required information in the session. + * + * @param string $email Email address to send the link to + * @param array $data Information from the authentication request (such as + * user details) + * @param array $urlParams Default parameters for the generated URL + * @param string $linkRoute The route to use as the base url for the login link + * @param string $subject Email subject + * @param string $template Email message template + * + * @return void + */ + public function sendAuthenticationLink($email, $data, + $urlParams, $linkRoute = 'myresearch-home', + $subject = 'email_login_subject', + $template = 'Email/login-link.phtml' + ) { + // Make sure we've waited long enough + $recoveryInterval = isset($this->config->Authentication->recover_interval) + ? $this->config->Authentication->recover_interval + : 60; + $sessionId = $this->sessionManager->getId(); + + if (($row = $this->authHashTable->getLatestBySessionId($sessionId)) + && time() - strtotime($row['created']) < $recoveryInterval + ) { + throw new AuthException('authentication_error_in_progress'); + } + + $this->csrf->trimTokenList(5); + $linkData = [ + 'timestamp' => time(), + 'data' => $data, + 'email' => $email, + 'ip' => $this->remoteAddress->getIpAddress() + ]; + $hash = $this->csrf->getHash(true); + + $row = $this->authHashTable + ->getByHashAndType($hash, AuthHashTable::TYPE_EMAIL); + + $row['session_id'] = $sessionId; + $row['data'] = json_encode($linkData); + $row->save(); + + $serverHelper = $this->viewRenderer->plugin('serverurl'); + $urlHelper = $this->viewRenderer->plugin('url'); + $urlParams['hash'] = $hash; + $viewParams = $linkData; + $viewParams['url'] = $serverHelper( + $urlHelper($linkRoute, [], ['query' => $urlParams]) + ); + $viewParams['title'] = $this->config->Site->title; + + $message = $this->viewRenderer->render($template, $viewParams); + $from = !empty($this->config->Mail->user_email_in_from) + ? $email + : ($this->config->Mail->default_from ?? $this->config->Site->email); + $subject = $this->translator->translate($subject); + $subject = str_replace('%%title%%', $viewParams['title'], $subject); + + $this->mailer->send($email, $from, $subject, $message); + } + + /** + * Authenticate using a hash + * + * @param string $hash Hash + * + * @return array + * @throws AuthException + */ + public function authenticate($hash) + { + $row = $this->authHashTable + ->getByHashAndType($hash, AuthHashTable::TYPE_EMAIL, false); + if (!$row) { + throw new AuthException('authentication_error_denied'); + } + $linkData = json_decode($row['data'], true); + $row->delete(); + + if (time() - strtotime($row['created']) > $this->loginRequestValidTime) { + throw new AuthException('authentication_error_denied'); + } + + // Require same session id or IP address: + $sessionId = $this->sessionManager->getId(); + if ($row['session_id'] !== $sessionId + && $linkData['ip'] !== $this->remoteAddress->getIpAddress() + ) { + throw new AuthException('authentication_error_denied'); + } + + return $linkData['data']; + } + + /** + * Check if the given request is a valid login request + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return bool + */ + public function isValidLoginRequest(\Zend\Http\PhpEnvironment\Request $request) + { + $hash = $request->getPost()->get( + 'hash', + $request->getQuery()->get('hash', '') + ); + if ($hash) { + $row = $this->authHashTable + ->getByHashAndType($hash, AuthHashTable::TYPE_EMAIL, false); + return !empty($row); + } + return false; + } +} diff --git a/module/VuFind/src/VuFind/Auth/EmailAuthenticatorFactory.php b/module/VuFind/src/VuFind/Auth/EmailAuthenticatorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2cfa4abb87974cf576e2e7d3716b194bdd96a2b9 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/EmailAuthenticatorFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * Factory for email authenticator module. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for email authenticator module. + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class EmailAuthenticatorFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\Zend\Session\SessionManager::class), + $container->get(\VuFind\Validator\Csrf::class), + $container->get(\VuFind\Mailer\Mailer::class), + $container->get('ViewRenderer'), + $container->get(\Zend\Http\PhpEnvironment\RemoteAddress::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\Db\Table\PluginManager::class) + ->get(\VuFind\Db\Table\AuthHash::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Auth/EmailFactory.php b/module/VuFind/src/VuFind/Auth/EmailFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..60dc9055212f634d000e3c17ce35f19be50975a1 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/EmailFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Email authentication module. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Email authentication module. + * + * @category VuFind + * @package Authentication + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class EmailFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(EmailAuthenticator::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Auth/Facebook.php b/module/VuFind/src/VuFind/Auth/Facebook.php index 2ca46ee2410629d553ef70b464900018d3596e7e..c63b2bef1cae6b959c5beeb80cf6cb27ff7ce8cf 100644 --- a/module/VuFind/src/VuFind/Auth/Facebook.php +++ b/module/VuFind/src/VuFind/Auth/Facebook.php @@ -121,7 +121,7 @@ class Facebook extends AbstractBase implements $user->lastname = $details->last_name; } if (isset($details->email)) { - $user->email = $details->email; + $user->updateEmail($details->email); } // Save and return the user object: diff --git a/module/VuFind/src/VuFind/Auth/FacebookFactory.php b/module/VuFind/src/VuFind/Auth/FacebookFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..29426d0226c49a6f9289be47d76e40d2af77c233 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/FacebookFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for Facebook authentication module. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Facebook authentication module. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FacebookFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $session = new \Zend\Session\Container( + 'Facebook', $container->get(\Zend\Session\SessionManager::class) + ); + return new $requestedName($session); + } +} diff --git a/module/VuFind/src/VuFind/Auth/Factory.php b/module/VuFind/src/VuFind/Auth/Factory.php deleted file mode 100644 index e7c773b7ca76fb057a9d6bdbdf819961c8346003..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Auth/Factory.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php -/** - * Factory for authentication services. - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Authentication - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Auth; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for authentication services. - * - * @category VuFind - * @package Authentication - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the ChoiceAuth plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return ChoiceAuth - */ - public static function getChoiceAuth(ServiceManager $sm) - { - $container = new \Zend\Session\Container( - 'ChoiceAuth', $sm->get('Zend\Session\SessionManager') - ); - $auth = new ChoiceAuth($container); - $auth->setPluginManager($sm->get('VuFind\Auth\PluginManager')); - return $auth; - } - - /** - * Construct the Facebook plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Facebook - */ - public static function getFacebook(ServiceManager $sm) - { - $container = new \Zend\Session\Container( - 'Facebook', $sm->get('Zend\Session\SessionManager') - ); - return new Facebook($container); - } - - /** - * Construct the ILS plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return ILS - */ - public static function getILS(ServiceManager $sm) - { - return new ILS( - $sm->get('VuFind\ILS\Connection'), - $sm->get('VuFind\Auth\ILSAuthenticator') - ); - } - - /** - * Construct the MultiAuth plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return MultiAuth - */ - public static function getMultiAuth(ServiceManager $sm) - { - $auth = new MultiAuth(); - $auth->setPluginManager($sm->get('VuFind\Auth\PluginManager')); - return $auth; - } - - /** - * Construct the MultiILS plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return MultiILS - */ - public static function getMultiILS(ServiceManager $sm) - { - return new MultiILS( - $sm->get('VuFind\ILS\Connection'), - $sm->get('VuFind\Auth\ILSAuthenticator') - ); - } - - /** - * Construct the Shibboleth plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Shibboleth - */ - public static function getShibboleth(ServiceManager $sm) - { - return new Shibboleth( - $sm->get('Zend\Session\SessionManager') - ); - } - - /** - * Construct the AlmaDatabase plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return AlmaDatabase - */ - public static function getAlmaDatabase(ServiceManager $sm) - { - return new AlmaDatabase( - $sm->get('VuFind\ILS\Connection'), - $sm->get('VuFind\Auth\ILSAuthenticator') - ); - } -} diff --git a/module/VuFind/src/VuFind/Auth/ILS.php b/module/VuFind/src/VuFind/Auth/ILS.php index c39b3aeb6624b5a4a00ac7452e3a4fca14341a61..8923d7aa042daf07b759867bed9fa8c911df256f 100644 --- a/module/VuFind/src/VuFind/Auth/ILS.php +++ b/module/VuFind/src/VuFind/Auth/ILS.php @@ -58,17 +58,27 @@ class ILS extends AbstractBase protected $catalog = null; /** - * Set the ILS connection for this object. + * Email Authenticator + * + * @var EmailAuthenticator + */ + protected $emailAuthenticator; + + /** + * Constructor * * @param \VuFind\ILS\Connection $connection ILS connection to set * @param \VuFind\ILS\Authenticator $authenticator ILS authenticator + * @param EmailAuthenticator $emailAuth Email authenticator */ public function __construct( \VuFind\ILS\Connection $connection, - \VuFind\Auth\ILSAuthenticator $authenticator + \VuFind\Auth\ILSAuthenticator $authenticator, + EmailAuthenticator $emailAuth = null ) { $this->setCatalog($connection); $this->authenticator = $authenticator; + $this->emailAuthenticator = $emailAuth; } /** @@ -107,27 +117,9 @@ class ILS extends AbstractBase { $username = trim($request->getPost()->get('username')); $password = trim($request->getPost()->get('password')); - if ($username == '' || $password == '') { - throw new AuthException('authentication_error_blank'); - } - - // Connect to catalog: - try { - $patron = $this->getCatalog()->patronLogin($username, $password); - } catch (AuthException $e) { - // Pass Auth exceptions through - throw $e; - } catch (\Exception $e) { - throw new AuthException('authentication_error_technical'); - } - - // Did the patron successfully log in? - if ($patron) { - return $this->processILSUser($patron); - } + $loginMethod = $this->getILSLoginMethod(); - // If we got this far, we have a problem: - throw new AuthException('authentication_error_invalid'); + return $this->handleLogin($username, $password, $loginMethod); } /** @@ -208,6 +200,88 @@ class ILS extends AbstractBase return $user; } + /** + * What login method does the ILS use (password, email, vufind) + * + * @param string $target Login target (MultiILS only) + * + * @return string + */ + public function getILSLoginMethod($target = '') + { + $config = $this->getCatalog()->checkFunction( + 'patronLogin', ['patron' => ['cat_username' => "$target.login"]] + ); + return $config['loginMethod'] ?? 'password'; + } + + /** + * Returns any authentication method this request should be delegated to. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object. + * + * @return string|bool + */ + public function getDelegateAuthMethod(\Zend\Http\PhpEnvironment\Request $request) + { + return (null !== $this->emailAuthenticator + && $this->emailAuthenticator->isValidLoginRequest($request)) + ? 'Email' : false; + } + + /** + * Handle the actual login with the ILS. + * + * @param string $username User name + * @param string $password Password + * @param string $loginMethod Login method + * + * @throws AuthException + * @return \VuFind\Db\Row\User Processed User object. + */ + protected function handleLogin($username, $password, $loginMethod) + { + if ($username == '' || ('password' === $loginMethod && $password == '')) { + throw new AuthException('authentication_error_blank'); + } + + // Connect to catalog: + try { + $patron = $this->getCatalog()->patronLogin($username, $password); + } catch (AuthException $e) { + // Pass Auth exceptions through + throw $e; + } catch (\Exception $e) { + throw new AuthException('authentication_error_technical'); + } + + // Did the patron successfully log in? + if ('email' === $loginMethod) { + if (null === $this->emailAuthenticator) { + throw new \Exception('Email authenticator not set'); + } + if ($patron) { + $class = get_class($this); + if ($p = strrpos($class, '\\')) { + $class = substr($class, $p + 1); + } + $this->emailAuthenticator->sendAuthenticationLink( + $patron['email'], + $patron, + ['auth_method' => $class] + ); + } + // Don't reveal the result + throw new \VuFind\Exception\AuthInProgress('email_login_link_sent'); + } + if ($patron) { + return $this->processILSUser($patron); + } + + // If we got this far, we have a problem: + throw new AuthException('authentication_error_invalid'); + } + /** * Update the database using details from the ILS, then return the User object. * @@ -241,10 +315,11 @@ class ILS extends AbstractBase $user->password = ''; // Update user information based on ILS data: - $fields = ['firstname', 'lastname', 'email', 'major', 'college']; + $fields = ['firstname', 'lastname', 'major', 'college']; foreach ($fields as $field) { $user->$field = $info[$field] ?? ' '; } + $user->updateEmail($info['email'] ?? ''); // Update the user in the database, then return it to the caller: $user->saveCredentials( diff --git a/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php b/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php index 60473282fd58789a1065214ebc5cbd761f341815..0a1f0232dcf0123eed8ca930003a524be6850ca0 100644 --- a/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php +++ b/module/VuFind/src/VuFind/Auth/ILSAuthenticator.php @@ -54,6 +54,13 @@ class ILSAuthenticator */ protected $catalog; + /** + * Email authenticator + * + * @var EmailAuthenticator + */ + protected $emailAuthenticator; + /** * Cache for ILS account information (keyed by username) * @@ -64,13 +71,16 @@ class ILSAuthenticator /** * Constructor * - * @param Manager $auth Auth manager - * @param ILSConnection $catalog ILS connection + * @param Manager $auth Auth manager + * @param ILSConnection $catalog ILS connection + * @param EmailAuthenticator $emailAuth Email authenticator */ - public function __construct(Manager $auth, ILSConnection $catalog) - { + public function __construct(Manager $auth, ILSConnection $catalog, + EmailAuthenticator $emailAuth = null + ) { $this->auth = $auth; $this->catalog = $catalog; + $this->emailAuthenticator = $emailAuth; } /** @@ -146,15 +156,77 @@ class ILSAuthenticator { $result = $this->catalog->patronLogin($username, $password); if ($result) { - $user = $this->auth->isLoggedIn(); - if ($user) { - $user->saveCredentials($username, $password); - $this->auth->updateSession($user); - // cache for future use - $this->ilsAccount[$username] = $result; - } + $this->updateUser($username, $password, $result); return $result; } return false; } + + /** + * Send email authentication link + * + * @param string $email Email address + * @param string $route Route for the login link + * + * @return void + */ + public function sendEmailLoginLink($email, $route) + { + if (null === $this->emailAuthenticator) { + throw new \Exception('Email authenticator not set'); + } + + $patron = $this->catalog->patronLogin($email, ''); + if ($patron) { + $this->emailAuthenticator->sendAuthenticationLink( + $patron['email'], + $patron, + ['auth_method' => 'ILS'], + $route + ); + } + } + + /** + * Process email login + * + * @param string $hash Login hash + * + * @return array|bool + * @throws ILSException + */ + public function processEmailLoginHash($hash) + { + if (null === $this->emailAuthenticator) { + throw new \Exception('Email authenticator not set'); + } + + try { + $patron = $this->emailAuthenticator->authenticate($hash); + } catch (\Vufind\Exception\Auth $e) { + return false; + } + $this->updateUser($patron['cat_username'], '', $patron); + return $patron; + } + + /** + * Update current user account with the patron information + * + * @param string $catUsername Catalog username + * @param string $catPassword Catalog password + * @param array $patron Patron + * + * @return void + */ + protected function updateUser($catUsername, $catPassword, $patron) + { + $user = $this->auth->isLoggedIn(); + if ($user) { + $user->saveCredentials($catUsername, $catPassword); + $this->auth->updateSession($user); + // cache for future use + $this->ilsAccount[$catUsername] = $patron; + } + } } diff --git a/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php b/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php index 68c7f1faa355b99c5fbede7cea2f9c64cb57cdd0..92cbaddd7f45e379a2cd9e8e33e99e969c9f3290 100644 --- a/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php +++ b/module/VuFind/src/VuFind/Auth/ILSAuthenticatorFactory.php @@ -68,14 +68,15 @@ class ILSAuthenticatorFactory implements FactoryInterface // actually utilized. $callback = function (& $wrapped, $proxy) use ($container, $requestedName) { // Generate wrapped object: - $auth = $container->get('VuFind\Auth\Manager'); - $catalog = $container->get('VuFind\ILS\Connection'); - $wrapped = new $requestedName($auth, $catalog); + $auth = $container->get(\VuFind\Auth\Manager::class); + $catalog = $container->get(\VuFind\ILS\Connection::class); + $emailAuth = $container->get(\VuFind\Auth\EmailAuthenticator::class); + $wrapped = new $requestedName($auth, $catalog, $emailAuth); // Indicate that initialization is complete to avoid reinitialization: $proxy->setProxyInitializer(null); }; - $cfg = $container->get('ProxyManager\Configuration'); + $cfg = $container->get(\ProxyManager\Configuration::class); $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($cfg); return $factory->createProxy($requestedName, $callback); } diff --git a/module/VuFind/src/VuFind/Auth/ILSFactory.php b/module/VuFind/src/VuFind/Auth/ILSFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2d1d59469f576ae9ee73bcd1f8ca6b4fedaf25d0 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/ILSFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for ILS authentication module (and others with equivalent constructors). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for ILS authentication module (and others with equivalent constructors). + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class IlsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\ILS\Connection::class), + $container->get(ILSAuthenticator::class), + $container->get(EmailAuthenticator::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Auth/Manager.php b/module/VuFind/src/VuFind/Auth/Manager.php index 131a5fd761e983559fec9a69d2895ca6c3f86d93..2c8a21f8c47bf1f4011707c8564749d9d811438d 100644 --- a/module/VuFind/src/VuFind/Auth/Manager.php +++ b/module/VuFind/src/VuFind/Auth/Manager.php @@ -143,8 +143,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface // Initialize active authentication setting (defaulting to Database // if no setting passed in): - $method = isset($config->Authentication->method) - ? $config->Authentication->method : 'Database'; + $method = $config->Authentication->method ?? 'Database'; $this->legalAuthOptions = [$method]; // mark it as legal $this->setAuthMethod($method); // load it } @@ -206,29 +205,35 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface */ public function supportsRecovery($authMethod = null) { - if ($this->getAuth($authMethod)->supportsPasswordRecovery()) { - return isset($this->config->Authentication->recover_password) - && $this->config->Authentication->recover_password; - } - return false; + return ($this->config->Authentication->recover_password ?? false) + && $this->getAuth($authMethod)->supportsPasswordRecovery(); + } + + /** + * Is email changing currently allowed? + * + * @param string $authMethod optional; check this auth method rather than + * the one in config file + * + * @return bool + */ + public function supportsEmailChange($authMethod = null) + { + return $this->config->Authentication->change_email ?? false; } /** * Is new passwords currently allowed? * * @param string $authMethod optional; check this auth method rather than - * the one in config file + * the one in config file * * @return bool */ public function supportsPasswordChange($authMethod = null) { - if (isset($this->config->Authentication->change_password) - && $this->config->Authentication->change_password - ) { - return $this->getAuth($authMethod)->supportsPasswordChange(); - } - return false; + return ($this->config->Authentication->change_password ?? false) + && $this->getAuth($authMethod)->supportsPasswordChange(); } /** @@ -357,9 +362,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface public function loginEnabled() { // Assume login is enabled unless explicitly turned off: - return isset($this->config->Authentication->hideLogin) - ? !$this->config->Authentication->hideLogin - : true; + return !($this->config->Authentication->hideLogin ?? false); } /** @@ -507,8 +510,7 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface */ public function inPrivacyMode() { - return isset($this->config->Authentication->privacy) - && $this->config->Authentication->privacy; + return $this->config->Authentication->privacy ?? false; } /** @@ -562,6 +564,30 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface return $user; } + /** + * Update a user's email from the request. + * + * @param UserRow $user Object representing user being updated. + * @param string $email New email address to set (must be pre-validated!). + * + * @throws AuthException + * @return void + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function updateEmail(UserRow $user, $email) + { + // Depending on verification setting, either do a direct update or else + // put the new address into a pending state. + if ($this->config->Authentication->verify_email ?? false) { + $user->pending_email = $email; + } else { + $user->updateEmail($email, true); + } + $user->save(); + $this->updateSession($user); + } + /** * Try to log in the user using current query parameters; return User object * on success, throws exception on failure. @@ -570,6 +596,8 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface * account credentials. * * @throws AuthException + * @throws \VuFind\Exception\PasswordSecurity + * @throws \VuFind\Exception\AuthInProgress * @return UserRow Object representing logged-in user. */ public function login($request) @@ -578,8 +606,16 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface // for example): $this->getAuth()->preLoginCheck($request); + // Check if the current auth method wants to delegate the request to another + // method: + if ($delegate = $this->getAuth()->getDelegateAuthMethod($request)) { + $this->setAuthMethod($delegate, true); + } + // Validate CSRF for form-based authentication methods: - if (!$this->getAuth()->getSessionInitiator(null)) { + if (!$this->getAuth()->getSessionInitiator(null) + && $this->getAuth()->needsCsrfCheck($request) + ) { if (!$this->csrf->isValid($request->getPost()->get('csrf'))) { $this->getAuth()->resetState(); throw new AuthException('authentication_error_technical'); @@ -619,15 +655,22 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface /** * Setter * - * @param string $method The auth class to proxy + * @param string $method The auth class to proxy + * @param bool $forceLegal Whether to force the new method legal * * @return void */ - public function setAuthMethod($method) + public function setAuthMethod($method, $forceLegal = false) { // Change the setting: $this->activeAuth = $method; + if ($forceLegal) { + if (!in_array($method, $this->legalAuthOptions)) { + $this->legalAuthOptions[] = $method; + } + } + // If this method supports switching to a different method and we haven't // already initialized it, add those options to the whitelist. If the object // is already initialized, that means we've already gone through this step @@ -661,6 +704,22 @@ class Manager implements \ZfcRbac\Identity\IdentityProviderInterface return $this->getAuth()->validateCredentials($request); } + /** + * What login method does the ILS use (password, email, vufind) + * + * @param string $target Login target (MultiILS only) + * + * @return array|false + */ + public function getILSLoginMethod($target = '') + { + $auth = $this->getAuth(); + if (is_callable([$auth, 'getILSLoginMethod'])) { + return $auth->getILSLoginMethod($target); + } + return false; + } + /** * Update common user attributes on login * diff --git a/module/VuFind/src/VuFind/Auth/ManagerFactory.php b/module/VuFind/src/VuFind/Auth/ManagerFactory.php index 9b1436cb86e60f086a3cdf162faa24740e1d6f2f..6e6f90a09f380eb3c43b56958cb10368dd160f28 100644 --- a/module/VuFind/src/VuFind/Auth/ManagerFactory.php +++ b/module/VuFind/src/VuFind/Auth/ManagerFactory.php @@ -62,11 +62,12 @@ class ManagerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } // Set up configuration: - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); try { // Check if the catalog wants to hide the login link, and override // the configuration if necessary. - $catalog = $container->get('VuFind\ILS\Connection'); + $catalog = $container->get(\VuFind\ILS\Connection::class); if ($catalog->loginIsHidden()) { $config = new \Zend\Config\Config($config->toArray(), true); $config->Authentication->hideLogin = true; @@ -80,11 +81,12 @@ class ManagerFactory implements FactoryInterface } // Load remaining dependencies: - $userTable = $container->get('VuFind\Db\Table\PluginManager')->get('user'); - $sessionManager = $container->get('Zend\Session\SessionManager'); - $pm = $container->get('VuFind\Auth\PluginManager'); - $cookies = $container->get('VuFind\Cookie\CookieManager'); - $csrf = $container->get('VuFind\Validator\Csrf'); + $userTable = $container->get(\VuFind\Db\Table\PluginManager::class) + ->get('user'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); + $pm = $container->get(\VuFind\Auth\PluginManager::class); + $cookies = $container->get(\VuFind\Cookie\CookieManager::class); + $csrf = $container->get(\VuFind\Validator\Csrf::class); // Build the object and make sure account credentials haven't expired: $manager = new $requestedName( diff --git a/module/VuFind/src/VuFind/Auth/MultiAuthFactory.php b/module/VuFind/src/VuFind/Auth/MultiAuthFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..45886fe49947c9b2ae9691e4c47a7e26e748d4b3 --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/MultiAuthFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for MultiAuth authentication module. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for MultiAuth authentication module. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MultiAuthFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $auth = new $requestedName(); + $auth->setPluginManager($container->get(PluginManager::class)); + return $auth; + } +} diff --git a/module/VuFind/src/VuFind/Auth/MultiILS.php b/module/VuFind/src/VuFind/Auth/MultiILS.php index e4cd1822bfc4937051f5062311ccdbbec27d02ca..73bc43dfa14b05d542c7cdce8d27f431c8e9b9de 100644 --- a/module/VuFind/src/VuFind/Auth/MultiILS.php +++ b/module/VuFind/src/VuFind/Auth/MultiILS.php @@ -57,35 +57,17 @@ class MultiILS extends ILS */ public function authenticate($request) { - $target = trim($request->getPost()->get('target')); $username = trim($request->getPost()->get('username')); $password = trim($request->getPost()->get('password')); - if ($username == '' || $password == '') { - throw new AuthException('authentication_error_blank'); - } + $target = trim($request->getPost()->get('target')); + $loginMethod = $this->getILSLoginMethod($target); // We should have target either separately or already embedded into username if ($target) { $username = "$target.$username"; } - // Connect to catalog: - try { - $patron = $this->getCatalog()->patronLogin($username, $password); - } catch (AuthException $e) { - // Pass Auth exceptions through - throw $e; - } catch (\Exception $e) { - throw new AuthException('authentication_error_technical'); - } - - // Did the patron successfully log in? - if ($patron) { - return $this->processILSUser($patron); - } - - // If we got this far, we have a problem: - throw new AuthException('authentication_error_invalid'); + return $this->handleLogin($username, $password, $loginMethod); } /** diff --git a/module/VuFind/src/VuFind/Auth/PasswordAccess.php b/module/VuFind/src/VuFind/Auth/PasswordAccess.php new file mode 100644 index 0000000000000000000000000000000000000000..4b54f02f124a8ba80755abd1f55ed64a8581417a --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/PasswordAccess.php @@ -0,0 +1,85 @@ +<?php +/** + * Password Access authentication class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Chris Hallberg <challber@villanova.edu> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Auth; + +use VuFind\Db\Row\User; +use VuFind\Exception\Auth as AuthException; + +/** + * Password Access authentication class + * + * @category VuFind + * @package Authentication + * @author Chris Hallberg <challber@villanova.edu> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class PasswordAccess extends AbstractBase +{ + /** + * Get configuration (load automatically if not previously set). Throw an + * exception if the configuration is invalid. + * + * @throws AuthException + * @return \Zend\Config\Config + */ + public function getConfig() + { + // Validate configuration if not already validated: + if (!$this->configValidated) { + $this->validateConfig(); + $this->configValidated = true; + } + + return $this->config; + } + + /** + * Attempt to authenticate the current user. Throws exception if login fails. + * + * @param \Zend\Http\PhpEnvironment\Request $request Request object containing + * account credentials. + * + * @throws AuthException + * @return User Object representing logged-in user. + */ + public function authenticate($request) + { + $config = $this->getConfig()->toArray(); + $req_password = trim($request->getPost()->get('password')); + + if (!in_array($req_password, $config['PasswordAccess']['access_user'])) { + throw new AuthException('authentication_error_invalid'); + } + + $userMap = array_flip($config['PasswordAccess']['access_user']); + return $this->getUserTable()->getByUsername($userMap[$req_password]); + } +} diff --git a/module/VuFind/src/VuFind/Auth/PluginManager.php b/module/VuFind/src/VuFind/Auth/PluginManager.php index f2ba6ecc9ebae9959623d3fa096215181b7e15c0..4d29dc5346ed2711f566a25e836ea8c3c807b292 100644 --- a/module/VuFind/src/VuFind/Auth/PluginManager.php +++ b/module/VuFind/src/VuFind/Auth/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Auth; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Auth handler plugin manager * @@ -44,20 +46,21 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'almadatabase' => 'VuFind\Auth\AlmaDatabase', - 'cas' => 'VuFind\Auth\CAS', - 'choiceauth' => 'VuFind\Auth\ChoiceAuth', - 'database' => 'VuFind\Auth\Database', - 'facebook' => 'VuFind\Auth\Facebook', - 'ils' => 'VuFind\Auth\ILS', - 'ldap' => 'VuFind\Auth\LDAP', - 'multiauth' => 'VuFind\Auth\MultiAuth', - 'multiils' => 'VuFind\Auth\MultiILS', - 'shibboleth' => 'VuFind\Auth\Shibboleth', - 'sip2' => 'VuFind\Auth\SIP2', + 'almadatabase' => AlmaDatabase::class, + 'cas' => CAS::class, + 'choiceauth' => ChoiceAuth::class, + 'database' => Database::class, + 'email' => Email::class, + 'facebook' => Facebook::class, + 'ils' => ILS::class, + 'ldap' => LDAP::class, + 'multiauth' => MultiAuth::class, + 'multiils' => MultiILS::class, + 'shibboleth' => Shibboleth::class, + 'sip2' => SIP2::class, // for legacy 1.x compatibility - 'db' => 'VuFind\Auth\Database', - 'sip' => 'VuFind\Auth\SIP2', + 'db' => Database::class, + 'sip' => SIP2::class, ]; /** @@ -66,17 +69,18 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Auth\AlmaDatabase' => 'VuFind\Auth\Factory::getAlmaDatabase', - 'VuFind\Auth\CAS' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Auth\ChoiceAuth' => 'VuFind\Auth\Factory::getChoiceAuth', - 'VuFind\Auth\Database' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Auth\Facebook' => 'VuFind\Auth\Factory::getFacebook', - 'VuFind\Auth\ILS' => 'VuFind\Auth\Factory::getILS', - 'VuFind\Auth\LDAP' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Auth\MultiAuth' => 'VuFind\Auth\Factory::getMultiAuth', - 'VuFind\Auth\MultiILS' => 'VuFind\Auth\Factory::getMultiILS', - 'VuFind\Auth\Shibboleth' => 'VuFind\Auth\Factory::getShibboleth', - 'VuFind\Auth\SIP2' => 'Zend\ServiceManager\Factory\InvokableFactory', + AlmaDatabase::class => ILSFactory::class, + CAS::class => InvokableFactory::class, + ChoiceAuth::class => ChoiceAuthFactory::class, + Database::class => InvokableFactory::class, + Email::class => EmailFactory::class, + Facebook::class => FacebookFactory::class, + ILS::class => ILSFactory::class, + LDAP::class => InvokableFactory::class, + MultiAuth::class => MultiAuthFactory::class, + MultiILS::class => ILSFactory::class, + Shibboleth::class => ShibbolethFactory::class, + SIP2::class => InvokableFactory::class, ]; /** @@ -91,7 +95,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Auth\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -103,6 +107,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Auth\AbstractBase'; + return AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Auth/Shibboleth.php b/module/VuFind/src/VuFind/Auth/Shibboleth.php index 2e490709de48d59fe127d9b6a25e226ebed556a2..63d5f33e607b190bb0baabf0a1f20137fdab69fc 100644 --- a/module/VuFind/src/VuFind/Auth/Shibboleth.php +++ b/module/VuFind/src/VuFind/Auth/Shibboleth.php @@ -142,7 +142,9 @@ class Shibboleth extends AbstractBase foreach ($attribsToCheck as $attribute) { if (isset($shib->$attribute)) { $value = $request->getServer()->get($shib->$attribute); - if ($attribute != 'cat_password') { + if ($attribute == 'email') { + $user->updateEmail($value); + } elseif ($attribute != 'cat_password') { $user->$attribute = ($value === null) ? '' : $value; } else { $catPassword = $value; diff --git a/module/VuFind/src/VuFind/Auth/ShibbolethFactory.php b/module/VuFind/src/VuFind/Auth/ShibbolethFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..60b4d412a69f8bb7f656455db7348ad0767be32e --- /dev/null +++ b/module/VuFind/src/VuFind/Auth/ShibbolethFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Shibboleth authentication module. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Auth; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Shibboleth authentication module. + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ShibbolethFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\Zend\Session\SessionManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php b/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php index 1dbe0ef91e05212fe46217ee73b17e19d7b72dbc..bbc6f65c4137d37653d2c58aee50573212106072 100644 --- a/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php +++ b/module/VuFind/src/VuFind/Autocomplete/EdsFactory.php @@ -62,7 +62,7 @@ class EdsFactory implements \Zend\ServiceManager\Factory\FactoryInterface array $options = null ) { return new $requestedName( - $container->get('VuFind\Search\BackendManager')->get('EDS') + $container->get(\VuFind\Search\BackendManager::class)->get('EDS') ); } } diff --git a/module/VuFind/src/VuFind/Autocomplete/PluginManager.php b/module/VuFind/src/VuFind/Autocomplete/PluginManager.php index 748c4ba3d56051717ca3ae3d0a99e77f0ecb275e..7b991ee0b241e3f15f4d4f73ea4911456a7a2da0 100644 --- a/module/VuFind/src/VuFind/Autocomplete/PluginManager.php +++ b/module/VuFind/src/VuFind/Autocomplete/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Autocomplete; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Autocomplete handler plugin manager * @@ -44,16 +46,16 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'none' => 'VuFind\Autocomplete\None', - 'eds' => 'VuFind\Autocomplete\Eds', - 'oclcidentities' => 'VuFind\Autocomplete\OCLCIdentities', - 'search2' => 'VuFind\Autocomplete\Search2', - 'search2cn' => 'VuFind\Autocomplete\Search2CN', - 'solr' => 'VuFind\Autocomplete\Solr', - 'solrauth' => 'VuFind\Autocomplete\SolrAuth', - 'solrcn' => 'VuFind\Autocomplete\SolrCN', - 'solrreserves' => 'VuFind\Autocomplete\SolrReserves', - 'tag' => 'VuFind\Autocomplete\Tag', + 'none' => None::class, + 'eds' => Eds::class, + 'oclcidentities' => OCLCIdentities::class, + 'search2' => Search2::class, + 'search2cn' => Search2CN::class, + 'solr' => Solr::class, + 'solrauth' => SolrAuth::class, + 'solrcn' => SolrCN::class, + 'solrreserves' => SolrReserves::class, + 'tag' => Tag::class, // for legacy 1.x compatibility 'noautocomplete' => 'None', 'oclcidentitiesautocomplete' => 'OCLCIdentities', @@ -70,17 +72,16 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Autocomplete\None' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Autocomplete\Eds' => 'VuFind\Autocomplete\EdsFactory', - 'VuFind\Autocomplete\OCLCIdentities' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Autocomplete\Search2' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\Search2CN' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\Solr' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\SolrAuth' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\SolrCN' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\SolrReserves' => 'VuFind\Autocomplete\SolrFactory', - 'VuFind\Autocomplete\Tag' => 'Zend\ServiceManager\Factory\InvokableFactory', + None::class => InvokableFactory::class, + Eds::class => EdsFactory::class, + OCLCIdentities::class => InvokableFactory::class, + Search2::class => SolrFactory::class, + Search2CN::class => SolrFactory::class, + Solr::class => SolrFactory::class, + SolrAuth::class => SolrFactory::class, + SolrCN::class => SolrFactory::class, + SolrReserves::class => SolrFactory::class, + Tag::class => InvokableFactory::class, ]; /** @@ -95,7 +96,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Autocomplete\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -107,6 +108,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Autocomplete\AutocompleteInterface'; + return AutocompleteInterface::class; } } diff --git a/module/VuFind/src/VuFind/Autocomplete/Solr.php b/module/VuFind/src/VuFind/Autocomplete/Solr.php index 577047797f8adcfc7cb3de60df04b392e3b46a12..e525c974f377f5ad2e9eb32015aa7a3aebd52685 100644 --- a/module/VuFind/src/VuFind/Autocomplete/Solr.php +++ b/module/VuFind/src/VuFind/Autocomplete/Solr.php @@ -172,7 +172,7 @@ class Solr implements AutocompleteInterface protected function mungeQuery($query) { // Modify the query so it makes a nice, truncated autocomplete query: - $forbidden = [':', '(', ')', '*', '+', '"']; + $forbidden = [':', '(', ')', '*', '+', '"', "'"]; $query = str_replace($forbidden, " ", $query); if (substr($query, -1) != " ") { $query .= "*"; diff --git a/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php b/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php index cb6d44c1b762d45ca788cde06b8d7a2d9f58e677..fea358c9fd746102215cd9ecfeb8f80ddf1adc72 100644 --- a/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php +++ b/module/VuFind/src/VuFind/Autocomplete/SolrFactory.php @@ -62,7 +62,7 @@ class SolrFactory implements \Zend\ServiceManager\Factory\FactoryInterface array $options = null ) { return new $requestedName( - $container->get('VuFind\Search\Results\PluginManager') + $container->get(\VuFind\Search\Results\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php b/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php index 98e85ed0c852d97a3b07c998d2cc4b30f901e88f..8c467489a7df125d33be12b8f8b23176461cc9e5 100644 --- a/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php +++ b/module/VuFind/src/VuFind/Autocomplete/SuggesterFactory.php @@ -60,9 +60,9 @@ class SuggesterFactory implements \Zend\ServiceManager\Factory\FactoryInterface array $options = null ) { return new $requestedName( - $container->get('VuFind\Autocomplete\PluginManager'), - $container->get('VuFind\Config\PluginManager'), - $container->get('VuFind\Search\Options\PluginManager') + $container->get(\VuFind\Autocomplete\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Search\Options\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/Bootstrapper.php b/module/VuFind/src/VuFind/Bootstrapper.php index 32b15d513ffaedd5aca8b675a4d8cc4ef30ad124..d2fb48d7497c7a84b544771bd10d211cf52151d9 100644 --- a/module/VuFind/src/VuFind/Bootstrapper.php +++ b/module/VuFind/src/VuFind/Bootstrapper.php @@ -42,6 +42,8 @@ use Zend\Router\Http\RouteMatch; */ class Bootstrapper { + use \VuFind\I18n\Translator\LanguageInitializerTrait; + /** * Main VuFind configuration * @@ -100,30 +102,25 @@ class Bootstrapper // Create the configuration manager: $app = $this->event->getApplication(); $sm = $app->getServiceManager(); - $this->config = $sm->get('VuFind\Config\PluginManager')->get('config'); + $this->config = $sm->get(\VuFind\Config\PluginManager::class)->get('config'); } /** - * Initialize dynamic debug mode (debug initiated by a ?debug=true parameter). + * Set up cookie to flag test mode. * * @return void */ - protected function initDynamicDebug() + protected function initTestMode() { - // Query parameters do not apply in console mode: - if (Console::isConsole()) { - return; - } - - $app = $this->event->getApplication(); - $sm = $app->getServiceManager(); - $debugOverride = $sm->get('Request')->getQuery()->get('debug'); - if ($debugOverride) { - $auth = $sm->get('ZfcRbac\Service\AuthorizationService'); - if ($auth->isGranted('access.DebugMode')) { - $logger = $sm->get('VuFind\Log\Logger'); - $logger->addDebugWriter($debugOverride); - } + // If we're in test mode (as determined by the config.ini property installed + // by the build.xml startup process), set a cookie so the front-end code can + // act accordingly. (This is needed to work around a problem where opening + // print dialogs during testing stalls the automated test process). + if ($this->config->System->runningTestSuite ?? false) { + $app = $this->event->getApplication(); + $sm = $app->getServiceManager(); + $cm = $sm->get(\VuFind\Cookie\CookieManager::class); + $cm->set('VuFindTestSuiteRunning', '1', 0, false); } } @@ -134,10 +131,9 @@ class Bootstrapper */ protected function initSystemStatus() { - // If the system is unavailable, forward to a different place: - if (isset($this->config->System->available) - && !$this->config->System->available - ) { + // If the system is unavailable and we're not in the console, forward to the + // unavailable page. + if (!Console::isConsole() && !($this->config->System->available ?? true)) { $callback = function ($e) { $routeMatch = new RouteMatch( ['controller' => 'Error', 'action' => 'Unavailable'], 1 @@ -274,30 +270,6 @@ class Bootstrapper return false; } - /** - * Support method for initLanguage() -- look up all text domains. - * - * @return array - */ - protected function getTextDomains() - { - $base = APPLICATION_PATH; - $local = LOCAL_OVERRIDE_DIR; - $languagePathParts = ["$base/languages"]; - if (!empty($local)) { - $languagePathParts[] = "$local/languages"; - } - $languagePathParts[] = "$base/themes/*/languages"; - - $domains = []; - foreach ($languagePathParts as $current) { - $places = glob($current . '/*', GLOB_ONLYDIR | GLOB_NOSORT); - $domains = array_merge($domains, array_map('basename', $places)); - } - - return array_unique($domains); - } - /** * Set up language handling. * @@ -321,7 +293,7 @@ class Bootstrapper if (($language = $request->getPost()->get('mylang', false)) || ($language = $request->getQuery()->get('lng', false)) ) { - $cookieManager = $sm->get('VuFind\Cookie\CookieManager'); + $cookieManager = $sm->get(\VuFind\Cookie\CookieManager::class); $cookieManager->set('language', $language); } elseif (!empty($request->getCookie()->language)) { $language = $request->getCookie()->language; @@ -335,17 +307,9 @@ class Bootstrapper $language = $config->Site->language; } try { - $translator = $sm->get('Zend\Mvc\I18n\Translator'); - $translator->setLocale($language) - ->addTranslationFile('ExtendedIni', null, 'default', $language); - foreach ($this->getTextDomains() as $domain) { - // Set up text domains using the domain name as the filename; - // this will help the ExtendedIni loader dynamically locate - // the appropriate files. - $translator->addTranslationFile( - 'ExtendedIni', $domain, $domain, $language - ); - } + $translator = $sm->get(\Zend\Mvc\I18n\Translator::class); + $translator->setLocale($language); + $this->addLanguageToTranslator($translator, $language); } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $e) { if (!extension_loaded('intl')) { throw new \Exception( @@ -354,6 +318,14 @@ class Bootstrapper ); } } + + // Store last selected language in user account, if applicable: + if (($user = $sm->get(\VuFind\Auth\Manager::class)->isLoggedIn()) + && $user->last_language != $language + ) { + $user->updateLastLanguage($language); + } + // Send key values to view: $viewModel = $sm->get('ViewManager')->getViewModel(); $viewModel->setVariable('userLang', $language); @@ -375,16 +347,6 @@ class Bootstrapper */ protected function initTheme() { - // Themes not needed in console mode: - if (Console::isConsole()) { - return; - } - - // Attach template injection configuration to the route event: - $this->events->attach( - 'route', ['VuFindTheme\Initializer', 'configureTemplateInjection'] - ); - // Attach remaining theme configuration to the dispatch event at high // priority (TODO: use priority constant once defined by framework): $config = $this->config->Site; @@ -430,7 +392,7 @@ class Bootstrapper protected function initSearch() { $sm = $this->event->getApplication()->getServiceManager(); - $bm = $sm->get('VuFind\Search\BackendManager'); + $bm = $sm->get(\VuFind\Search\BackendManager::class); $events = $sm->get('SharedEventManager'); $events->attach('VuFindSearch', 'resolve', [$bm, 'onResolve']); } @@ -444,8 +406,8 @@ class Bootstrapper { $callback = function ($event) { $sm = $event->getApplication()->getServiceManager(); - if ($sm->has('VuFind\Log\Logger')) { - $log = $sm->get('VuFind\Log\Logger'); + if ($sm->has(\VuFind\Log\Logger::class)) { + $log = $sm->get(\VuFind\Log\Logger::class); if (is_callable([$log, 'logException'])) { $exception = $event->getParam('exception'); // Console request does not include server, diff --git a/module/VuFind/src/VuFind/Cache/Manager.php b/module/VuFind/src/VuFind/Cache/Manager.php index 80dcde821661270083146da67ded7af77ac568bc..e455992190e7a185f759567ac8c7104bb93825ab 100644 --- a/module/VuFind/src/VuFind/Cache/Manager.php +++ b/module/VuFind/src/VuFind/Cache/Manager.php @@ -123,7 +123,7 @@ class Manager * * @param string $name Name of the requested cache. * @param string|null $namespace Optional namespace to use. Defaults to the - * value of {@see $name}. + * value of $name. * * @return StorageInterface * @throws \Exception diff --git a/module/VuFind/src/VuFind/Cache/ManagerFactory.php b/module/VuFind/src/VuFind/Cache/ManagerFactory.php index 317ca426c20bbdfeb3105e6ecdc524b136694010..b1c384bcb8a27a0e5894854d53ab3b7cb4407065 100644 --- a/module/VuFind/src/VuFind/Cache/ManagerFactory.php +++ b/module/VuFind/src/VuFind/Cache/ManagerFactory.php @@ -62,8 +62,8 @@ class ManagerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config'), - $container->get('VuFind\Config\PluginManager')->get('searches') + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\Config\PluginManager::class)->get('searches') ); } } diff --git a/module/VuFind/src/VuFind/Cart.php b/module/VuFind/src/VuFind/Cart.php index a9e4bf4b13a6164ac20e024fc132652dd5de4aa2..51ab9cf25235e51d709abda9e429110f6071cd11 100644 --- a/module/VuFind/src/VuFind/Cart.php +++ b/module/VuFind/src/VuFind/Cart.php @@ -84,7 +84,7 @@ class Cart */ protected $cookieManager; - const CART_COOKIE = 'vufind_cart'; + const CART_COOKIE = 'vufind_cart'; const CART_COOKIE_SOURCES = 'vufind_cart_src'; const CART_COOKIE_DELIM = "\t"; diff --git a/module/VuFind/src/VuFind/CartFactory.php b/module/VuFind/src/VuFind/CartFactory.php index a4c76e307ae4ab33a108e35b34395f828295e311..a686cb8ce55001d0db1ca809c809496159248225 100644 --- a/module/VuFind/src/VuFind/CartFactory.php +++ b/module/VuFind/src/VuFind/CartFactory.php @@ -61,7 +61,8 @@ class CartFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $active = isset($config->Site->showBookBag) ? (bool)$config->Site->showBookBag : false; $size = isset($config->Site->bookBagMaxSize) @@ -69,8 +70,8 @@ class CartFactory implements FactoryInterface $activeInSearch = isset($config->Site->bookbagTogglesInSearch) ? $config->Site->bookbagTogglesInSearch : true; return new $requestedName( - $container->get('VuFind\Record\Loader'), - $container->get('VuFind\Cookie\CookieManager'), + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Cookie\CookieManager::class), $size, $active, $activeInSearch ); } diff --git a/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php index b3d942f941d6842aab0ea352edf96ee079414733..9920417b5c1d16850d862da7094366dc0682149d 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php +++ b/module/VuFind/src/VuFind/ChannelProvider/AbstractILSChannelProviderFactory.php @@ -62,8 +62,8 @@ class AbstractILSChannelProviderFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory!'); } return new $requestedName( - $container->get('VuFindSearch\Service'), - $container->get('VuFind\ILS\Connection') + $container->get(\VuFindSearch\Service::class), + $container->get(\VuFind\ILS\Connection::class) ); } } diff --git a/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowseFactory.php b/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7963010591c4df607eeb1bc6e16291890bd21fe3 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/AlphaBrowseFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for AlphaBrowse channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for AlphaBrowse channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AlphaBrowseFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get(\VuFindSearch\Service::class), + $container->get(\VuFind\Search\BackendManager::class)->get('Solr'), + $container->get('ControllerPluginManager')->get('url'), + $container->get(\VuFind\Record\Router::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php index b969c763b8bff487e2b5844af4bada7d3efa6741..d471c905f5c5a54fc1c4d77a49124621d9ebfea0 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php @@ -62,11 +62,11 @@ class ChannelLoaderFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory!'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('channels'), - $container->get('VuFind\Cache\Manager'), - $container->get('VuFind\ChannelProvider\PluginManager'), - $container->get('VuFind\Search\SearchRunner'), - $container->get('VuFind\Record\Loader') + $container->get(\VuFind\Config\PluginManager::class)->get('channels'), + $container->get(\VuFind\Cache\Manager::class), + $container->get(\VuFind\ChannelProvider\PluginManager::class), + $container->get(\VuFind\Search\SearchRunner::class), + $container->get(\VuFind\Record\Loader::class) ); } } diff --git a/module/VuFind/src/VuFind/ChannelProvider/FacetsFactory.php b/module/VuFind/src/VuFind/ChannelProvider/FacetsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d3a9acb58b8b53621bdcc28cb1b73cccf25a7c73 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/FacetsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for Facets channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Facets channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FacetsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get(\VuFind\Search\Results\PluginManager::class), + $container->get('ControllerPluginManager')->get('url') + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/Factory.php b/module/VuFind/src/VuFind/ChannelProvider/Factory.php deleted file mode 100644 index b4014736630a7df8472ece79032bf70944ceb2ac..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/ChannelProvider/Factory.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -/** - * Factory for ChannelProvider plugins. - * - * PHP version 7 - * - * Copyright (C) Villanova University 2016. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Channels - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\ChannelProvider; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for ChannelProvider plugins. - * - * @category VuFind - * @package Channels - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the AlphaBrowse channel provider. - * - * @param ServiceManager $sm Service manager. - * - * @return AlphaBrowse - */ - public static function getAlphaBrowse(ServiceManager $sm) - { - return new AlphaBrowse( - $sm->get('VuFindSearch\Service'), - $sm->get('VuFind\Search\BackendManager') - ->get('Solr'), - $sm->get('ControllerPluginManager')->get('url'), - $sm->get('VuFind\Record\Router') - ); - } - - /** - * Construct the Facets channel provider. - * - * @param ServiceManager $sm Service manager. - * - * @return Facets - */ - public static function getFacets(ServiceManager $sm) - { - return new Facets( - $sm->get('VuFind\Search\Results\PluginManager'), - $sm->get('ControllerPluginManager')->get('url') - ); - } - - /** - * Construct the ListItems channel provider. - * - * @param ServiceManager $sm Service manager. - * - * @return ListItems - */ - public static function getListItems(ServiceManager $sm) - { - return new ListItems( - $sm->get('VuFind\Db\Table\PluginManager')->get('UserList'), - $sm->get('ControllerPluginManager')->get('url'), - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Construct the Random channel provider. - * - * @param ServiceManager $sm Service manager. - * - * @return Random - */ - public static function getRandom(ServiceManager $sm) - { - return new Random( - $sm->get('VuFindSearch\Service'), - $sm->get('VuFind\Search\Params\PluginManager') - ); - } - - /** - * Construct the SimilarItems channel provider. - * - * @param ServiceManager $sm Service manager. - * - * @return SimilarItems - */ - public static function getSimilarItems(ServiceManager $sm) - { - return new SimilarItems( - $sm->get('VuFindSearch\Service'), - $sm->get('ControllerPluginManager')->get('url'), - $sm->get('VuFind\Record\Router') - ); - } -} diff --git a/module/VuFind/src/VuFind/ChannelProvider/ListItemsFactory.php b/module/VuFind/src/VuFind/ChannelProvider/ListItemsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..31484b6352a965babb62ed5343062079ffed0afc --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/ListItemsFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for ListItems channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for ListItems channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ListItemsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get(\VuFind\Db\Table\PluginManager::class)->get('UserList'), + $container->get('ControllerPluginManager')->get('url'), + $container->get(\VuFind\Search\Results\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php b/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php index 603b7c1254d6e0c9459302c4c9c55244867951b1..8909b6777f4f9ba8e87abcea22de6d093b3eb56c 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php +++ b/module/VuFind/src/VuFind/ChannelProvider/PluginManager.php @@ -44,14 +44,14 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'alphabrowse' => 'VuFind\ChannelProvider\AlphaBrowse', - 'facets' => 'VuFind\ChannelProvider\Facets', - 'listitems' => 'VuFind\ChannelProvider\ListItems', - 'newilsitems' => 'VuFind\ChannelProvider\NewILSItems', - 'random' => 'VuFind\ChannelProvider\Random', - 'recentlyreturned' => 'VuFind\ChannelProvider\RecentlyReturned', - 'similaritems' => 'VuFind\ChannelProvider\SimilarItems', - 'trendingilsitems' => 'VuFind\ChannelProvider\TrendingILSItems', + 'alphabrowse' => AlphaBrowse::class, + 'facets' => Facets::class, + 'listitems' => ListItems::class, + 'newilsitems' => NewILSItems::class, + 'random' => Random::class, + 'recentlyreturned' => RecentlyReturned::class, + 'similaritems' => SimilarItems::class, + 'trendingilsitems' => TrendingILSItems::class, ]; /** @@ -60,22 +60,14 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\ChannelProvider\AlphaBrowse' => - 'VuFind\ChannelProvider\Factory::getAlphaBrowse', - 'VuFind\ChannelProvider\Facets' => - 'VuFind\ChannelProvider\Factory::getFacets', - 'VuFind\ChannelProvider\ListItems' => - 'VuFind\ChannelProvider\Factory::getListItems', - 'VuFind\ChannelProvider\NewILSItems' => - 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', - 'VuFind\ChannelProvider\Random' => - 'VuFind\ChannelProvider\Factory::getRandom', - 'VuFind\ChannelProvider\RecentlyReturned' => - 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', - 'VuFind\ChannelProvider\SimilarItems' => - 'VuFind\ChannelProvider\Factory::getSimilarItems', - 'VuFind\ChannelProvider\TrendingILSItems' => - 'VuFind\ChannelProvider\AbstractILSChannelProviderFactory', + AlphaBrowse::class => AlphaBrowseFactory::class, + Facets::class => FacetsFactory::class, + ListItems::class => ListItemsFactory::class, + NewILSItems::class => AbstractILSChannelProviderFactory::class, + Random::class => RandomFactory::class, + RecentlyReturned::class => AbstractILSChannelProviderFactory::class, + SimilarItems::class => SimilarItemsFactory::class, + TrendingILSItems::class => AbstractILSChannelProviderFactory::class, ]; /** @@ -90,7 +82,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addInitializer('VuFind\ChannelProvider\RouterInitializer'); + $this->addInitializer(RouterInitializer::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -102,6 +94,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\ChannelProvider\ChannelProviderInterface'; + return ChannelProviderInterface::class; } } diff --git a/module/VuFind/src/VuFind/ChannelProvider/RandomFactory.php b/module/VuFind/src/VuFind/ChannelProvider/RandomFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..00e6e73d226808a38e1663fc8ba6a6a61543ef35 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/RandomFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for Random channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Random channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RandomFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get(\VuFindSearch\Service::class), + $container->get(\VuFind\Search\Params\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php b/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php index 264a9e059b554ffcd1e9150ac9052521d1cfc2b1..5c1704a1663d4ae146fcae2a2fb6f34a4dc2d4b9 100644 --- a/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php +++ b/module/VuFind/src/VuFind/ChannelProvider/RouterInitializer.php @@ -52,8 +52,10 @@ class RouterInitializer implements InitializerInterface public function __invoke(ContainerInterface $container, $instance) { if ($instance instanceof AbstractChannelProvider) { - $instance->setCoverRouter($container->get('VuFind\Cover\Router')); - $instance->setRecordRouter($container->get('VuFind\Record\Router')); + $instance->setCoverRouter($container->get(\VuFind\Cover\Router::class)); + $instance->setRecordRouter( + $container->get(\VuFind\Record\Router::class) + ); } return $instance; } diff --git a/module/VuFind/src/VuFind/ChannelProvider/SimilarItemsFactory.php b/module/VuFind/src/VuFind/ChannelProvider/SimilarItemsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..af6017de1872233297d4bcdb13f261c63df9a4b0 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/SimilarItemsFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for SimilarItems channel provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for SimilarItems channel provider. + * + * @category VuFind + * @package Channels + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SimilarItemsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get(\VuFindSearch\Service::class), + $container->get('ControllerPluginManager')->get('url'), + $container->get(\VuFind\Record\Router::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php b/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php index 85c82ba345e39a3841ffade4d7483cca822c6583..a32ca572092bf6b90e3bb3370b303a1856e672ae 100644 --- a/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php +++ b/module/VuFind/src/VuFind/Config/AccountCapabilitiesFactory.php @@ -62,8 +62,8 @@ class AccountCapabilitiesFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config'), - $container->get('VuFind\Auth\Manager') + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\Auth\Manager::class) ); } } diff --git a/module/VuFind/src/VuFind/Config/PluginManager.php b/module/VuFind/src/VuFind/Config/PluginManager.php index e91f268a4813022ddf055732917b1c3f283548b9..6ade9cacce88344ed1bc35313305440c8fea0044 100644 --- a/module/VuFind/src/VuFind/Config/PluginManager.php +++ b/module/VuFind/src/VuFind/Config/PluginManager.php @@ -52,7 +52,7 @@ class PluginManager extends Base public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Config\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } diff --git a/module/VuFind/src/VuFind/Config/Upgrade.php b/module/VuFind/src/VuFind/Config/Upgrade.php index d8545d9f03249c51fba35ebfd2da7bef5ab280c4..294337ac54b25c6a48d801eaf0e4e6244df198dc 100644 --- a/module/VuFind/src/VuFind/Config/Upgrade.php +++ b/module/VuFind/src/VuFind/Config/Upgrade.php @@ -292,11 +292,14 @@ class Upgrade { // Configuration files to load. Note that config.ini must always be loaded // first so that getOldConfigPath can work properly! - $configs = [ - 'config.ini', 'authority.ini', 'facets.ini', 'geofeatures.ini', - 'reserves.ini', 'searches.ini', 'Summon.ini', 'WorldCat.ini', 'sms.ini', - 'permissions.ini', 'Collection.ini', 'Primo.ini' - ]; + $configs = ['config.ini']; + foreach (glob($this->rawDir . '/*.ini') as $ini) { + $parts = explode('/', str_replace('\\', '/', $ini)); + $filename = array_pop($parts); + if ($filename !== 'config.ini') { + $configs[] = $filename; + } + } foreach ($configs as $config) { // Special case for config.ini, since we may need to overlay extra // settings: diff --git a/module/VuFind/src/VuFind/Config/YamlReaderFactory.php b/module/VuFind/src/VuFind/Config/YamlReaderFactory.php index aa7c306e533dbf50370abba9402feb92f1073dd1..0e60f795be9a0b7181731984929f9a1b4e752481 100644 --- a/module/VuFind/src/VuFind/Config/YamlReaderFactory.php +++ b/module/VuFind/src/VuFind/Config/YamlReaderFactory.php @@ -61,6 +61,6 @@ class YamlReaderFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Cache\Manager')); + return new $requestedName($container->get(\VuFind\Cache\Manager::class)); } } diff --git a/module/VuFind/src/VuFind/Connection/RelaisFactory.php b/module/VuFind/src/VuFind/Connection/RelaisFactory.php index 13101eac9598d354078810475f3e380ccc307e68..03633ac9d938ae0258a28e01001eb50314404a30 100644 --- a/module/VuFind/src/VuFind/Connection/RelaisFactory.php +++ b/module/VuFind/src/VuFind/Connection/RelaisFactory.php @@ -61,9 +61,11 @@ class RelaisFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $url = $config->Relais->authenticateurl ?? null; - $client = $container->get('VuFindHttp\HttpService')->createClient($url); + $client = $container->get(\VuFindHttp\HttpService::class) + ->createClient($url); $client->setOptions(['timeout' => $config->Relais->timeout ?? 500]); return new $requestedName($client, $config->Relais ?? null); } diff --git a/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php b/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php index b38f8f6a435f6c8ddebfef72f45bf544eafa25c0..dab9367fa5aa184a8758a6d9a2b2e9f1d3277ed2 100644 --- a/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php +++ b/module/VuFind/src/VuFind/Connection/WorldCatUtilsFactory.php @@ -61,8 +61,9 @@ class WorldCatUtilsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); - $client = $container->get('VuFindHttp\HttpService')->createClient(); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $client = $container->get(\VuFindHttp\HttpService::class)->createClient(); $ip = $container->get('Request')->getServer()->get('SERVER_ADDR'); return new $requestedName( isset($config->WorldCat) ? $config->WorldCat : null, diff --git a/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php b/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php index b094fba62777723cce02c13539eae969838b870e..ef30257554b14b1ec29f3222936a028305ba24c5 100644 --- a/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php +++ b/module/VuFind/src/VuFind/Content/AbstractAmazonFactory.php @@ -60,12 +60,13 @@ class AbstractAmazonFactory implements \Zend\ServiceManager\Factory\FactoryInter if ($options !== null) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $associate = isset($config->Content->amazonassociate) ? $config->Content->amazonassociate : null; $secret = isset($config->Content->amazonsecret) ? $config->Content->amazonsecret : null; - $label = $container->get('Zend\Mvc\I18n\Translator')->translate( + $label = $container->get(\Zend\Mvc\I18n\Translator::class)->translate( 'Supplied by Amazon' ); return new $requestedName($associate, $secret, $label); diff --git a/module/VuFind/src/VuFind/Content/AbstractCover.php b/module/VuFind/src/VuFind/Content/AbstractCover.php index 39a6e7b26b714ce0b1b057698e635f6928c2ceda..4df69cabe75cf1a1902320bd3b100661733344a8 100644 --- a/module/VuFind/src/VuFind/Content/AbstractCover.php +++ b/module/VuFind/src/VuFind/Content/AbstractCover.php @@ -52,6 +52,13 @@ abstract class AbstractCover */ protected $supportsIssn = false; + /** + * Does this plugin support ISMNs? + * + * @var bool + */ + protected $supportsIsmn = false; + /** * Does this plugin support OCLC numbers? * @@ -66,6 +73,13 @@ abstract class AbstractCover */ protected $supportsUpc = false; + /** + * Does this plugin support national bibliographies number? + * + * @var bool + */ + protected $supportsNbn = false; + /** * Are we allowed to cache images from this source? * @@ -95,8 +109,10 @@ abstract class AbstractCover return ($this->supportsIsbn && isset($ids['isbn'])) || ($this->supportsIssn && isset($ids['issn'])) + || ($this->supportsIsmn && isset($ids['ismn'])) || ($this->supportsOclc && isset($ids['oclc'])) - || ($this->supportsUpc && isset($ids['upc'])); + || ($this->supportsUpc && isset($ids['upc'])) + || ($this->supportsNbn && isset($ids['nbn'])); } /** diff --git a/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php b/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php index 3abb04f083cf4629994f8d4a53accf9540179a9a..d90b6857a1e990105153c006ad7008df24c4eb06 100644 --- a/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php +++ b/module/VuFind/src/VuFind/Content/AbstractSyndeticsFactory.php @@ -61,7 +61,8 @@ class AbstractSyndeticsFactory implements FactoryInterface if ($options !== null) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); // Special case: if the class name ends in Plus, we need to strip off // the "Plus" and instead configure the base Syndetics class into "plus" diff --git a/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php b/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php index 75bee998b92f0a648559c2d24b6692a9686f370e..cb6177fdc6afef1d11ff159d6ca750483ad8865d 100644 --- a/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/AuthorNotes/PluginManager.php @@ -44,9 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'demo' => 'VuFind\Content\AuthorNotes\Demo', - 'syndetics' => 'VuFind\Content\AuthorNotes\Syndetics', - 'syndeticsplus' => 'VuFind\Content\AuthorNotes\SyndeticsPlus', + 'demo' => Demo::class, + 'syndetics' => Syndetics::class, + 'syndeticsplus' => SyndeticsPlus::class, ]; /** @@ -55,12 +55,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\AuthorNotes\Demo' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\AuthorNotes\Syndetics' => - 'VuFind\Content\AbstractSyndeticsFactory', - 'VuFind\Content\AuthorNotes\SyndeticsPlus' => - 'VuFind\Content\AbstractSyndeticsFactory', + Demo::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Syndetics::class => \VuFind\Content\AbstractSyndeticsFactory::class, + SyndeticsPlus::class => \VuFind\Content\AbstractSyndeticsFactory::class, ]; /** @@ -71,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractBase'; + return \VuFind\Content\AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Content/Covers/AmazonFactory.php b/module/VuFind/src/VuFind/Content/Covers/AmazonFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0e5d44d081dc19e6d71df039fc33b62cc6be3b58 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/AmazonFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Amazon cover loader factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; + +/** + * Amazon cover loader factory + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class AmazonFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $associate = $config->Content->amazonassociate ?? null; + $secret = $config->Content->amazonsecret ?? null; + return new $requestedName($associate, $secret); + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/BooksiteFactory.php b/module/VuFind/src/VuFind/Content/Covers/BooksiteFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..50a63e93fb4c5572f2c8225da26cc313c8f3e6b1 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/BooksiteFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Booksite cover loader factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; + +/** + * Booksite cover loader factory + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class BooksiteFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $url = $config->Booksite->url ?? 'https://api.booksite.com'; + if (!isset($config->Booksite->key)) { + throw new \Exception("Booksite 'key' not set in VuFind config"); + } + return new $requestedName($url, $config->Booksite->key); + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/BrowZineFactory.php b/module/VuFind/src/VuFind/Content/Covers/BrowZineFactory.php index 8545902e0642d7c91e90af6f8f6a2952dec81be3..fb96b4c3d2e4cc9b322eba686ba96b765f629195 100644 --- a/module/VuFind/src/VuFind/Content/Covers/BrowZineFactory.php +++ b/module/VuFind/src/VuFind/Content/Covers/BrowZineFactory.php @@ -62,7 +62,8 @@ class BrowZineFactory implements \Zend\ServiceManager\Factory\FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $backend = $container->get('VuFind\Search\BackendManager')->get('BrowZine'); + $backend = $container->get(\VuFind\Search\BackendManager::class) + ->get('BrowZine'); return new $requestedName($backend->getConnector()); } } diff --git a/module/VuFind/src/VuFind/Content/Covers/BuchhandelFactory.php b/module/VuFind/src/VuFind/Content/Covers/BuchhandelFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4ff8a6c6ef459b0bb57961b6ef26efd3a53c99b9 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/BuchhandelFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * Buchhandel cover loader factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; + +/** + * Buchhandel cover loader factory + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class BuchhandelFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $url = isset($config->Buchhandel->url) + ? trim($config->Buchhandel->url, '/') . '/' + : 'https://api.vlb.de/api/v1/cover/'; + if (!isset($config->Buchhandel->token)) { + throw new \Exception("Buchhandel.de 'token' not set in VuFind config"); + } + return new $requestedName($url, $config->Buchhandel->token); + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php b/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php index 276e492013f2ae0addb027a7322be77bd2acb8fb..8af568a0031f3b085c249c5e822dfbf0d92c2278 100644 --- a/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php +++ b/module/VuFind/src/VuFind/Content/Covers/ContentCafe.php @@ -60,8 +60,7 @@ class ContentCafe extends \VuFind\Content\AbstractCover public function __construct(\Zend\Config\Config $config) { $this->password = $config->pw; - $this->baseURL = isset($config->url) - ? $config->url : 'http://contentcafe2.btol.com'; + $this->baseURL = $config->url ?? 'http://contentcafe2.btol.com'; $this->supportsUpc = $this->supportsIsbn = $this->cacheAllowed = true; } diff --git a/module/VuFind/src/VuFind/Content/Covers/ContentCafeFactory.php b/module/VuFind/src/VuFind/Content/Covers/ContentCafeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..381c60ee77fb1f37b10d094926f1bfde614701c7 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/ContentCafeFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * ContentCafe cover loader factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; + +/** + * ContentCafe cover loader factory + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class ContentCafeFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $finalConfig = $config->Contentcafe ?? new \Zend\Config\Config([]); + return new $requestedName($finalConfig); + } +} diff --git a/module/VuFind/src/VuFind/Content/Covers/Factory.php b/module/VuFind/src/VuFind/Content/Covers/Factory.php deleted file mode 100644 index 780403779c9eed39ef0705104371392c5e7cd29e..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Content/Covers/Factory.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php -/** - * Factory for instantiating content loaders - * - * PHP version 7 - * - * Copyright (C) Villanova University 2009. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Content\Covers; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for instantiating content loaders - * - * @category VuFind - * @package Content - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Create Amazon loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getAmazon(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $associate = isset($config->Content->amazonassociate) - ? $config->Content->amazonassociate : null; - $secret = isset($config->Content->amazonsecret) - ? $config->Content->amazonsecret : null; - return new Amazon($associate, $secret); - } - - /** - * Create Booksite loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getBooksite(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $url = isset($config->Booksite->url) - ? $config->Booksite->url : 'https://api.booksite.com'; - if (!isset($config->Booksite->key)) { - throw new \Exception("Booksite 'key' not set in VuFind config"); - } - return new Booksite($url, $config->Booksite->key); - } - - /** - * Create Buchhandel.de loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getBuchhandel(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $url = isset($config->Buchhandel->url) - ? trim($config->Buchhandel->url, '/') . '/' - : 'https://api.vlb.de/api/v1/cover/'; - if (!isset($config->Buchhandel->token)) { - throw new \Exception("Buchhandel.de 'token' not set in VuFind config"); - } - return new Buchhandel($url, $config->Buchhandel->token); - } - - /** - * Create a ContentCafe loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getContentCafe(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $finalConfig = isset($config->Contentcafe) - ? $config->Contentcafe : new \Zend\Config\Config([]); - return new ContentCafe($finalConfig); - } - - /** - * Create a Syndetics loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getSyndetics(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - return new Syndetics( - isset($config->Syndetics->use_ssl) && $config->Syndetics->use_ssl - ); - } -} diff --git a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php index c6095735a307c9085984435e660faa43a3c69ddf..48dd561da51d16fd6919c2ec7d60211b74012540 100644 --- a/module/VuFind/src/VuFind/Content/Covers/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Covers/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Content\Covers; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Covers content loader plugin manager * @@ -44,17 +46,17 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'amazon' => 'VuFind\Content\Covers\Amazon', - 'booksite' => 'VuFind\Content\Covers\Booksite', - 'buchhandel' => 'VuFind\Content\Covers\Buchhandel', - 'browzine' => 'VuFind\Content\Covers\BrowZine', - 'contentcafe' => 'VuFind\Content\Covers\ContentCafe', - 'google' => 'VuFind\Content\Covers\Google', - 'librarything' => 'VuFind\Content\Covers\LibraryThing', - 'localfile' => 'VuFind\Content\Covers\LocalFile', - 'openlibrary' => 'VuFind\Content\Covers\OpenLibrary', - 'summon' => 'VuFind\Content\Covers\Summon', - 'syndetics' => 'VuFind\Content\Covers\Syndetics', + 'amazon' => Amazon::class, + 'booksite' => Booksite::class, + 'buchhandel' => Buchhandel::class, + 'browzine' => BrowZine::class, + 'contentcafe' => ContentCafe::class, + 'google' => Google::class, + 'librarything' => LibraryThing::class, + 'localfile' => LocalFile::class, + 'openlibrary' => OpenLibrary::class, + 'summon' => Summon::class, + 'syndetics' => Syndetics::class, ]; /** @@ -63,26 +65,17 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\Covers\Amazon' => 'VuFind\Content\Covers\Factory::getAmazon', - 'VuFind\Content\Covers\Booksite' => - 'VuFind\Content\Covers\Factory::getBooksite', - 'VuFind\Content\Covers\BrowZine' => 'VuFind\Content\Covers\BrowZineFactory', - 'VuFind\Content\Covers\Buchhandel' => - 'VuFind\Content\Covers\Factory::getBuchhandel', - 'VuFind\Content\Covers\ContentCafe' => - 'VuFind\Content\Covers\Factory::getContentCafe', - 'VuFind\Content\Covers\Google' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Covers\LibraryThing' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Covers\LocalFile' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Covers\OpenLibrary' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Covers\Summon' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Covers\Syndetics' => - 'VuFind\Content\Covers\Factory::getSyndetics', + Amazon::class => AmazonFactory::class, + Booksite::class => BooksiteFactory::class, + BrowZine::class => BrowZineFactory::class, + Buchhandel::class => BuchhandelFactory::class, + ContentCafe::class => ContentCafeFactory::class, + Google::class => InvokableFactory::class, + LibraryThing::class => InvokableFactory::class, + LocalFile::class => InvokableFactory::class, + OpenLibrary::class => InvokableFactory::class, + Summon::class => InvokableFactory::class, + Syndetics::class => SyndeticsFactory::class, ]; /** @@ -93,6 +86,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractCover'; + return \VuFind\Content\AbstractCover::class; } } diff --git a/module/VuFind/src/VuFind/Content/Covers/SyndeticsFactory.php b/module/VuFind/src/VuFind/Content/Covers/SyndeticsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..13f9a996667152a9124aa73e6f6e304508d99048 --- /dev/null +++ b/module/VuFind/src/VuFind/Content/Covers/SyndeticsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Syndetics cover loader factory + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\Content\Covers; + +use Interop\Container\ContainerInterface; + +/** + * Syndetics cover loader factory + * + * @category VuFind + * @package Content + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class SyndeticsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + return new $requestedName($config->Syndetics->use_ssl ?? false); + } +} diff --git a/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php b/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php index fd5b3da8e6818d22095c654021ccfe68a8437e62..e88e85dfc626b016ed77d84c61c870a00876373f 100644 --- a/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Excerpts/PluginManager.php @@ -44,9 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'demo' => 'VuFind\Content\Excerpts\Demo', - 'syndetics' => 'VuFind\Content\Excerpts\Syndetics', - 'syndeticsplus' => 'VuFind\Content\Excerpts\SyndeticsPlus', + 'demo' => Demo::class, + 'syndetics' => Syndetics::class, + 'syndeticsplus' => SyndeticsPlus::class, ]; /** @@ -55,12 +55,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\Excerpts\Demo' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Excerpts\Syndetics' => - 'VuFind\Content\AbstractSyndeticsFactory', - 'VuFind\Content\Excerpts\SyndeticsPlus' => - 'VuFind\Content\AbstractSyndeticsFactory', + Demo::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Syndetics::class => \VuFind\Content\AbstractSyndeticsFactory::class, + SyndeticsPlus::class => \VuFind\Content\AbstractSyndeticsFactory::class, ]; /** @@ -71,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractBase'; + return \VuFind\Content\AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Content/Factory.php b/module/VuFind/src/VuFind/Content/Factory.php index 8e2bc7b44027fe27c339104431170cd6b8779313..05b5502e07e981fe10df30aad377c6b690f668d4 100644 --- a/module/VuFind/src/VuFind/Content/Factory.php +++ b/module/VuFind/src/VuFind/Content/Factory.php @@ -27,7 +27,7 @@ */ namespace VuFind\Content; -use Zend\ServiceManager\ServiceManager; +use Interop\Container\ContainerInterface; /** * Factory for instantiating content loaders @@ -40,85 +40,65 @@ use Zend\ServiceManager\ServiceManager; * * @codeCoverageIgnore */ -class Factory +class Factory implements \Zend\ServiceManager\Factory\FactoryInterface { /** - * Create Author Notes loader + * Get the configuration setting name to get content provider settings. * - * @param ServiceManager $sm Service manager + * @param string $name Requested service name * - * @return mixed + * @return string */ - public static function getAuthorNotes(ServiceManager $sm) + protected function getConfigSettingName($name) { - $loader = $sm->get('VuFind\Content\AuthorNotes\PluginManager'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $providers = isset($config->Content->authorNotes) - ? $config->Content->authorNotes : ''; - return new Loader($loader, $providers); + // Account for one special exception: + $lcName = strtolower($name); + return $lcName === 'authornotes' ? 'authorNotes' : $lcName; } /** - * Create Excerpts loader + * Get the plugin manager service name to build a content provider service. * - * @param ServiceManager $sm Service manager + * @param string $name Requested service name * - * @return mixed + * @return string */ - public static function getExcerpts(ServiceManager $sm) + protected function getPluginManagerServiceName($name) { - $loader = $sm->get('VuFind\Content\Excerpts\PluginManager'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $providers = isset($config->Content->excerpts) - ? $config->Content->excerpts : ''; - return new Loader($loader, $providers); + $lcName = strtolower($name); + // Account for two special legacy exceptions: + $exceptions = ['authornotes' => 'AuthorNotes', 'toc' => 'TOC']; + $formattedName = $exceptions[$lcName] ?? ucfirst($lcName); + return 'VuFind\Content\\' . $formattedName . '\PluginManager'; } /** - * Create Reviews loader + * Create an object * - * @param ServiceManager $sm Service manager + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @return mixed - */ - public static function getReviews(ServiceManager $sm) - { - $loader = $sm->get('VuFind\Content\Reviews\PluginManager'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $providers = isset($config->Content->reviews) - ? $config->Content->reviews : ''; - return new Loader($loader, $providers); - } - - /** - * Create Summaries loader + * @return object * - * @param ServiceManager $sm Service manager + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs * - * @return mixed + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public static function getSummaries(ServiceManager $sm) - { - $loader = $sm->get('VuFind\Content\Summaries\PluginManager'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $providers = isset($config->Content->summaries) - ? $config->Content->summaries : ''; - return new Loader($loader, $providers); - } - - /** - * Create TOC loader - * - * @param ServiceManager $sm Service manager - * - * @return mixed - */ - public static function getTOC(ServiceManager $sm) - { - $loader = $sm->get('VuFind\Content\TOC\PluginManager'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $providers = isset($config->Content->toc) - ? $config->Content->toc : ''; - return new Loader($loader, $providers); + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $pm = $container->get($this->getPluginManagerServiceName($requestedName)); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $setting = $this->getConfigSettingName($requestedName); + $providers = $config->Content->$setting ?? ''; + return new Loader($pm, $providers); } } diff --git a/module/VuFind/src/VuFind/Content/PluginManager.php b/module/VuFind/src/VuFind/Content/PluginManager.php index 94a2e457ee053640ed46a2c2f0e8226b82590f92..359f61f4d0356fc41be8c45dff541d5cb4bd5ef0 100644 --- a/module/VuFind/src/VuFind/Content/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/PluginManager.php @@ -44,11 +44,11 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'authornotes' => 'VuFind\Content\Factory::getAuthorNotes', - 'excerpts' => 'VuFind\Content\Factory::getExcerpts', - 'reviews' => 'VuFind\Content\Factory::getReviews', - 'summaries' => 'VuFind\Content\Factory::getSummaries', - 'toc' => 'VuFind\Content\Factory::getTOC', + 'authornotes' => Factory::class, + 'excerpts' => Factory::class, + 'reviews' => Factory::class, + 'summaries' => Factory::class, + 'toc' => Factory::class, ]; /** @@ -59,6 +59,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\Loader'; + return \VuFind\Content\Loader::class; } } diff --git a/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php b/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php index c2363e1846be0f1c42578eaf293e4094e12892c7..237c100dee497254528e041bc66df3e333516322 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php +++ b/module/VuFind/src/VuFind/Content/Reviews/BooksiteFactory.php @@ -60,7 +60,8 @@ class BooksiteFactory implements \Zend\ServiceManager\Factory\FactoryInterface if ($options !== null) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $url = isset($config->Booksite->url) ? $config->Booksite->url : 'https://api.booksite.com'; if (!isset($config->Booksite->key)) { diff --git a/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php b/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php index 614f8e66abe210fd7b2cbc966ade9b94cc8b83de..bf7d5a2f22f3335c5e9b234ae5f7142655e5021a 100644 --- a/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Reviews/PluginManager.php @@ -44,13 +44,13 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'amazon' => 'VuFind\Content\Reviews\Amazon', - 'amazoneditorial' => 'VuFind\Content\Reviews\AmazonEditorial', - 'booksite' => 'VuFind\Content\Reviews\Booksite', - 'demo' => 'VuFind\Content\Reviews\Demo', - 'guardian' => 'VuFind\Content\Reviews\Guardian', - 'syndetics' => 'VuFind\Content\Reviews\Syndetics', - 'syndeticsplus' => 'VuFind\Content\Reviews\SyndeticsPlus', + 'amazon' => Amazon::class, + 'amazoneditorial' => AmazonEditorial::class, + 'booksite' => Booksite::class, + 'demo' => Demo::class, + 'guardian' => Guardian::class, + 'syndetics' => Syndetics::class, + 'syndeticsplus' => SyndeticsPlus::class, ]; /** @@ -59,19 +59,13 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\Reviews\Amazon' => 'VuFind\Content\AbstractAmazonFactory', - 'VuFind\Content\Reviews\AmazonEditorial' => - 'VuFind\Content\AbstractAmazonFactory', - 'VuFind\Content\Reviews\Booksite' => - 'VuFind\Content\Reviews\BooksiteFactory', - 'VuFind\Content\Reviews\Demo' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Reviews\Guardian' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Reviews\Syndetics' => - 'VuFind\Content\AbstractSyndeticsFactory', - 'VuFind\Content\Reviews\SyndeticsPlus' => - 'VuFind\Content\AbstractSyndeticsFactory', + Amazon::class => \VuFind\Content\AbstractAmazonFactory::class, + AmazonEditorial::class => \VuFind\Content\AbstractAmazonFactory::class, + Booksite::class => BooksiteFactory::class, + Demo::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Guardian::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Syndetics::class => \VuFind\Content\AbstractSyndeticsFactory::class, + SyndeticsPlus::class => \VuFind\Content\AbstractSyndeticsFactory::class, ]; /** @@ -82,6 +76,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractBase'; + return \VuFind\Content\AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php b/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php index 8a1a8ec4414ca3ca8c679480f29747c2397bd6b2..2950becdcb4e4de0a6d8a9e2a050744e7e243223 100644 --- a/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/Summaries/PluginManager.php @@ -44,9 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'demo' => 'VuFind\Content\Summaries\Demo', - 'syndetics' => 'VuFind\Content\Summaries\Syndetics', - 'syndeticsplus' => 'VuFind\Content\Summaries\SyndeticsPlus', + 'demo' => Demo::class, + 'syndetics' => Syndetics::class, + 'syndeticsplus' => SyndeticsPlus::class, ]; /** @@ -55,12 +55,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\Summaries\Demo' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\Summaries\Syndetics' => - 'VuFind\Content\AbstractSyndeticsFactory', - 'VuFind\Content\Summaries\SyndeticsPlus' => - 'VuFind\Content\AbstractSyndeticsFactory', + Demo::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Syndetics::class => \VuFind\Content\AbstractSyndeticsFactory::class, + SyndeticsPlus::class => \VuFind\Content\AbstractSyndeticsFactory::class, ]; /** @@ -71,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractBase'; + return \VuFind\Content\AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Content/TOC/PluginManager.php b/module/VuFind/src/VuFind/Content/TOC/PluginManager.php index 233a79e5080c88c757eb02bf08cb534870fbb5aa..636c98ee78325fe489dbc750d4a6150aa7742fc2 100644 --- a/module/VuFind/src/VuFind/Content/TOC/PluginManager.php +++ b/module/VuFind/src/VuFind/Content/TOC/PluginManager.php @@ -44,9 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'demo' => 'VuFind\Content\TOC\Demo', - 'syndetics' => 'VuFind\Content\TOC\Syndetics', - 'syndeticsplus' => 'VuFind\Content\TOC\SyndeticsPlus', + 'demo' => Demo::class, + 'syndetics' => Syndetics::class, + 'syndeticsplus' => SyndeticsPlus::class, ]; /** @@ -55,10 +55,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Content\TOC\Demo' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Content\TOC\Syndetics' => 'VuFind\Content\AbstractSyndeticsFactory', - 'VuFind\Content\TOC\SyndeticsPlus' => - 'VuFind\Content\AbstractSyndeticsFactory', + Demo::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + Syndetics::class => \VuFind\Content\AbstractSyndeticsFactory::class, + SyndeticsPlus::class => \VuFind\Content\AbstractSyndeticsFactory::class, ]; /** @@ -69,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Content\AbstractBase'; + return \VuFind\Content\AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php index 4ad84522ef75833254d1a8a9bc713d7201740568..9ce5f51b0f0e1b8956314d4ff121fb28438b8163 100644 --- a/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php +++ b/module/VuFind/src/VuFind/ContentBlock/BlockLoaderFactory.php @@ -62,9 +62,9 @@ class BlockLoaderFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Search\Options\PluginManager'), - $container->get('VuFind\Config\PluginManager'), - $container->get('VuFind\ContentBlock\PluginManager') + $container->get(\VuFind\Search\Options\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\ContentBlock\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php index 39cb267cc2daf1f8c91df892b829ecaf7bceb93c..2d102fd85bddaee27217addc3a17a4ff99077cf5 100644 --- a/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php +++ b/module/VuFind/src/VuFind/ContentBlock/ChannelsFactory.php @@ -63,7 +63,7 @@ class ChannelsFactory implements FactoryInterface } return new $requestedName( $container->get('Request'), - $container->get('VuFind\ChannelProvider\ChannelLoader') + $container->get(\VuFind\ChannelProvider\ChannelLoader::class) ); } } diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetList.php b/module/VuFind/src/VuFind/ContentBlock/FacetList.php index f8673e001623cb4e467d392d8975f9aee690eddd..a691c9512c3dae7155bfff0952fe2dfba94ac788 100644 --- a/module/VuFind/src/VuFind/ContentBlock/FacetList.php +++ b/module/VuFind/src/VuFind/ContentBlock/FacetList.php @@ -105,9 +105,17 @@ class FacetList implements ContentBlockInterface */ protected function getHierarchicalFacetSortSettings($facetConfig) { - return isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions) + $baseConfig + = isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions) ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray() : []; + $homepageConfig + = isset($facetConfig->HomePage_Settings->hierarchicalFacetSortOptions) + ? $facetConfig->HomePage_Settings->hierarchicalFacetSortOptions + ->toArray() + : []; + + return array_merge($baseConfig, $homepageConfig); } /** diff --git a/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php index 573fd4dc9f65cba4053ce39d254c24e731cbd02f..1fa54226fe0c26ef242624210cc1a20fefa648c3 100644 --- a/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php +++ b/module/VuFind/src/VuFind/ContentBlock/FacetListFactory.php @@ -61,8 +61,8 @@ class FacetListFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $fcpm = $container->get('VuFind\Search\FacetCache\PluginManager'); - $cm = $container->get('VuFind\Config\PluginManager'); + $fcpm = $container->get(\VuFind\Search\FacetCache\PluginManager::class); + $cm = $container->get(\VuFind\Config\PluginManager::class); return new $requestedName($fcpm, $cm); } } diff --git a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php index 8a99e18eecf1b2bcb637ed2d74a67ac78f4217e0..4d16ea5a6b9f5a9624da5d9e61d248fcd4e2821e 100644 --- a/module/VuFind/src/VuFind/ContentBlock/PluginManager.php +++ b/module/VuFind/src/VuFind/ContentBlock/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\ContentBlock; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Content block plugin manager * @@ -44,9 +46,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'channels' => 'VuFind\ContentBlock\Channels', - 'facetlist' => 'VuFind\ContentBlock\FacetList', - 'ilsstatusmonitor' => 'VuFind\ContentBlock\IlsStatusMonitor', + 'channels' => Channels::class, + 'facetlist' => FacetList::class, + 'ilsstatusmonitor' => IlsStatusMonitor::class, ]; /** @@ -55,10 +57,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\ContentBlock\Channels' => 'VuFind\ContentBlock\ChannelsFactory', - 'VuFind\ContentBlock\FacetList' => 'VuFind\ContentBlock\FacetListFactory', - 'VuFind\ContentBlock\IlsStatusMonitor' => - 'Zend\ServiceManager\Factory\InvokableFactory', + Channels::class => ChannelsFactory::class, + FacetList::class => FacetListFactory::class, + IlsStatusMonitor::class => InvokableFactory::class, ]; /** @@ -88,6 +89,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\ContentBlock\ContentBlockInterface'; + return ContentBlockInterface::class; } } diff --git a/module/VuFind/src/VuFind/Controller/AbstractBase.php b/module/VuFind/src/VuFind/Controller/AbstractBase.php index 1b617ab1c69c9886a5ecbc5c7a4205aa78af6c56..be564d5ab8978f82153109058d1cb30616cbd95b 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractBase.php +++ b/module/VuFind/src/VuFind/Controller/AbstractBase.php @@ -28,7 +28,9 @@ */ namespace VuFind\Controller; +use VuFind\Exception\Auth as AuthException; use VuFind\Exception\ILS as ILSException; +use VuFind\Http\PhpEnvironment\Request as HttpRequest; use Zend\Mvc\Controller\AbstractActionController; use Zend\Mvc\MvcEvent; use Zend\ServiceManager\ServiceLocatorInterface; @@ -51,11 +53,12 @@ class AbstractBase extends AbstractActionController { /** * Permission that must be granted to access this module (false for no - * restriction) + * restriction, null to use configured default (which is usually the same + * as false)). * * @var string|bool */ - protected $accessPermission = false; + protected $accessPermission = null; /** * Behavior when access is denied (used unless overridden through @@ -105,6 +108,43 @@ class AbstractBase extends AbstractActionController } } + /** + * Getter for access permission. + * + * @return string|bool + */ + public function getAccessPermission() + { + return $this->accessPermission; + } + + /** + * Getter for access permission. + * + * @param string $ap Permission to require for access to the controller (false + * for no requirement) + * + * @return void + */ + public function setAccessPermission($ap) + { + $this->accessPermission = empty($ap) ? false : $ap; + } + + /** + * Get request object + * + * @return HttpRequest + */ + public function getRequest() + { + if (!$this->request) { + $this->request = new HttpRequest(); + } + + return $this->request; + } + /** * Register the default events for this controller * @@ -132,10 +172,9 @@ class AbstractBase extends AbstractActionController */ protected function createViewModel($params = null) { - $layout = $this->params() - ->fromPost('layout', $this->params()->fromQuery('layout', false)); - if ('lightbox' === $layout) { + if ($this->inLightbox()) { $this->layout()->setTemplate('layout/lightbox'); + $params['inLightbox'] = true; } return new ViewModel($params); } @@ -218,7 +257,7 @@ class AbstractBase extends AbstractActionController */ protected function getAuthManager() { - return $this->serviceLocator->get('VuFind\Auth\Manager'); + return $this->serviceLocator->get(\VuFind\Auth\Manager::class); } /** @@ -231,7 +270,7 @@ class AbstractBase extends AbstractActionController protected function getAuthorizationService() { return $this->serviceLocator - ->get('ZfcRbac\Service\AuthorizationService'); + ->get(\ZfcRbac\Service\AuthorizationService::class); } /** @@ -241,7 +280,7 @@ class AbstractBase extends AbstractActionController */ protected function getILSAuthenticator() { - return $this->serviceLocator->get('VuFind\Auth\ILSAuthenticator'); + return $this->serviceLocator->get(\VuFind\Auth\ILSAuthenticator::class); } /** @@ -331,15 +370,33 @@ class AbstractBase extends AbstractActionController $username = "$target.$username"; } try { - $patron = $ilsAuth->newCatalogLogin($username, $password); - - // If login failed, store a warning message: - if (!$patron) { - $this->flashMessenger()->addErrorMessage('Invalid Patron Login'); + if ('email' === $this->getILSLoginMethod($target)) { + $routeMatch = $this->getEvent()->getRouteMatch(); + $routeName = $routeMatch ? $routeMatch->getMatchedRouteName() + : 'myresearch-profile'; + $ilsAuth->sendEmailLoginLink($username, $routeName); + $this->flashMessenger() + ->addSuccessMessage('email_login_link_sent'); + } else { + $patron = $ilsAuth->newCatalogLogin($username, $password); + + // If login failed, store a warning message: + if (!$patron) { + $this->flashMessenger() + ->addErrorMessage('Invalid Patron Login'); + } } } catch (ILSException $e) { $this->flashMessenger()->addErrorMessage('ils_connection_failed'); } + } elseif ('ILS' === $this->params()->fromQuery('auth_method', false) + && ($hash = $this->params()->fromQuery('hash', false)) + ) { + try { + $patron = $ilsAuth->processEmailLoginHash($hash); + } catch (AuthException $e) { + $this->flashMessenger()->addErrorMessage($e->getMessage()); + } } else { try { // If no credentials were provided, try the stored values: @@ -368,7 +425,8 @@ class AbstractBase extends AbstractActionController */ public function getConfig($id = 'config') { - return $this->serviceLocator->get('VuFind\Config\PluginManager')->get($id); + return $this->serviceLocator->get(\VuFind\Config\PluginManager::class) + ->get($id); } /** @@ -378,7 +436,7 @@ class AbstractBase extends AbstractActionController */ public function getILS() { - return $this->serviceLocator->get('VuFind\ILS\Connection'); + return $this->serviceLocator->get(\VuFind\ILS\Connection::class); } /** @@ -388,7 +446,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordLoader() { - return $this->serviceLocator->get('VuFind\Record\Loader'); + return $this->serviceLocator->get(\VuFind\Record\Loader::class); } /** @@ -398,7 +456,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordCache() { - return $this->serviceLocator->get('VuFind\Record\Cache'); + return $this->serviceLocator->get(\VuFind\Record\Cache::class); } /** @@ -408,7 +466,7 @@ class AbstractBase extends AbstractActionController */ public function getRecordRouter() { - return $this->serviceLocator->get('VuFind\Record\Router'); + return $this->serviceLocator->get(\VuFind\Record\Router::class); } /** @@ -420,7 +478,7 @@ class AbstractBase extends AbstractActionController */ public function getTable($table) { - return $this->serviceLocator->get('VuFind\Db\Table\PluginManager') + return $this->serviceLocator->get(\VuFind\Db\Table\PluginManager::class) ->get($table); } @@ -531,7 +589,7 @@ class AbstractBase extends AbstractActionController */ protected function disableSessionWrites() { - $this->serviceLocator->get('VuFind\Session\Settings')->disableWrite(); + $this->serviceLocator->get(\VuFind\Session\Settings::class)->disableWrite(); } /** @@ -541,7 +599,7 @@ class AbstractBase extends AbstractActionController */ public function getSearchMemory() { - return $this->serviceLocator->get('VuFind\Search\Memory'); + return $this->serviceLocator->get(\VuFind\Search\Memory::class); } /** @@ -551,7 +609,8 @@ class AbstractBase extends AbstractActionController */ protected function commentsEnabled() { - $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); + $check = $this->serviceLocator + ->get(\VuFind\Config\AccountCapabilities::class); return $check->getCommentSetting() !== 'disabled'; } @@ -562,7 +621,8 @@ class AbstractBase extends AbstractActionController */ protected function listsEnabled() { - $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); + $check = $this->serviceLocator + ->get(\VuFind\Config\AccountCapabilities::class); return $check->getListSetting() !== 'disabled'; } @@ -573,7 +633,8 @@ class AbstractBase extends AbstractActionController */ protected function tagsEnabled() { - $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); + $check = $this->serviceLocator + ->get(\VuFind\Config\AccountCapabilities::class); return $check->getTagSetting() !== 'disabled'; } @@ -669,11 +730,64 @@ class AbstractBase extends AbstractActionController /** * Get the tab configuration for this controller. * + * @return \VuFind\RecordTab\TabManager + */ + protected function getRecordTabManager() + { + return $this->serviceLocator->get(\VuFind\RecordTab\TabManager::class); + } + + /** + * Are we currently in a lightbox context? + * + * @return bool + */ + protected function inLightbox() + { + return + $this->params()->fromPost( + 'layout', $this->params()->fromQuery('layout', false) + ) === 'lightbox' + || 'layout/lightbox' == $this->layout()->getTemplate(); + } + + /** + * What login method does the ILS use (password, email, vufind) + * + * @param string $target Login target (MultiILS only) + * + * @return string + */ + protected function getILSLoginMethod($target = '') + { + $config = $this->getILS()->checkFunction( + 'patronLogin', ['patron' => ['cat_username' => "$target.login"]] + ); + return $config['loginMethod'] ?? 'password'; + } + + /** + * Get settings required for displaying the catalog login form + * * @return array */ - protected function getRecordTabConfig() + protected function getILSLoginSettings() { - $cfg = $this->serviceLocator->get('Config'); - return $cfg['vufind']['recorddriver_tabs']; + $targets = null; + $defaultTarget = null; + $loginMethod = null; + $loginMethods = []; + // Connect to the ILS and check if multiple target support is available: + $catalog = $this->getILS(); + if ($catalog->checkCapability('getLoginDrivers')) { + $targets = $catalog->getLoginDrivers(); + $defaultTarget = $catalog->getDefaultLoginDriver(); + foreach ($targets as $t) { + $loginMethods[$t] = $this->getILSLoginMethod($t); + } + } else { + $loginMethod = $this->getILSLoginMethod(); + } + return compact('targets', 'defaultTarget', 'loginMethod', 'loginMethods'); } } diff --git a/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php b/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php index 6f3d621cc8a42f64a02a5cc252718a906dde3aa6..f16c84b5708fd901823ff6aa1e924436333d894a 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php +++ b/module/VuFind/src/VuFind/Controller/AbstractBaseFactory.php @@ -41,6 +41,46 @@ use Zend\ServiceManager\Factory\FactoryInterface; */ class AbstractBaseFactory implements FactoryInterface { + /** + * Apply permission settings to the controller. + * + * @param ContainerInterface $container Service manager + * @param AbstractBase $controller Controller to configure + * + * @return AbstractBase + */ + protected function applyPermissions($container, $controller) + { + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('permissionBehavior'); + $permissions = $config->global->controllerAccess ?? []; + + if (!empty($permissions)) { + // Iterate through parent classes until we find the most specific + // class access permission defined (if any): + $class = get_class($controller); + do { + if (isset($permissions[$class])) { + $controller->setAccessPermission($permissions[$class]); + break; + } + $class = get_parent_class($class); + } while ($class); + + // If the controller's current permission is null (as opposed to false + // or a string), that means it has no internally configured default, and + // setAccessPermission was not called above; thus, we should apply the + // default value: + if (isset($permissions['*']) + && $controller->getAccessPermission() === null + ) { + $controller->setAccessPermission($permissions['*']); + } + } + + return $controller; + } + /** * Create an object * @@ -61,6 +101,6 @@ class AbstractBaseFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container); + return $this->applyPermissions($container, new $requestedName($container)); } } diff --git a/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php b/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php index 3da3fd398e51bf0d217d12fcbd5eeb0d1632e46f..f9efa5a7e22287a3147f7964093ea717cc06d1cf 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php +++ b/module/VuFind/src/VuFind/Controller/AbstractBaseWithConfigFactory.php @@ -28,7 +28,6 @@ namespace VuFind\Controller; use Interop\Container\ContainerInterface; -use Zend\ServiceManager\Factory\FactoryInterface; /** * Generic controller factory (with config injection). @@ -39,7 +38,7 @@ use Zend\ServiceManager\Factory\FactoryInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class AbstractBaseWithConfigFactory implements FactoryInterface +class AbstractBaseWithConfigFactory extends AbstractBaseFactory { /** * Create an object @@ -61,7 +60,10 @@ class AbstractBaseWithConfigFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); - return new $requestedName($container, $config); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + return $this->applyPermissions( + $container, new $requestedName($container, $config) + ); } } diff --git a/module/VuFind/src/VuFind/Controller/AbstractRecord.php b/module/VuFind/src/VuFind/Controller/AbstractRecord.php index 80fe11cd20a725bcf2baf85fc203b51a101d6c38..13fb8e1e33e1434358a23c68becff132fe61c772 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractRecord.php +++ b/module/VuFind/src/VuFind/Controller/AbstractRecord.php @@ -198,7 +198,7 @@ class AbstractRecord extends AbstractBase // Save tags, if any: if ($tags = $this->params()->fromPost('tag')) { - $tagParser = $this->serviceLocator->get('VuFind\Tags'); + $tagParser = $this->serviceLocator->get(\VuFind\Tags::class); $driver->addTags($user, $tagParser->parse($tags)); $this->flashMessenger() ->addMessage(['msg' => 'add_tag_success'], 'success'); @@ -252,6 +252,30 @@ class AbstractRecord extends AbstractBase */ public function homeAction() { + // If collections are active, we may need to check if the driver is actually + // a collection; if so, we should redirect to the collection controller. + $checkRoute = $this->params()->fromPost('checkRoute') + ?? $this->params()->fromQuery('checkRoute') + ?? false; + $config = $this->getConfig(); + if ($checkRoute && $config->Collections->collections ?? false) { + $routeConfig = isset($config->Collections->route) + ? $config->Collections->route->toArray() : []; + $collectionRoutes + = array_merge(['record' => 'collection'], $routeConfig); + $routeName = $this->event->getRouteMatch()->getMatchedRouteName() ?? ''; + if ($collectionRoute = ($collectionRoutes[$routeName] ?? null)) { + $driver = $this->loadRecord(); + if (true === $driver->tryMethod('isCollection')) { + $params = $this->params()->fromQuery() + + $this->params()->fromRoute(); + $collectionUrl = $this->url() + ->fromRoute($collectionRoute, $params); + return $this->redirect()->toUrl($collectionUrl); + } + } + } + return $this->showTab( $this->params()->fromRoute('tab', $this->getDefaultTab()) ); @@ -287,11 +311,11 @@ class AbstractRecord extends AbstractBase // Perform the save operation: $driver = $this->loadRecord(); $post = $this->getRequest()->getPost()->toArray(); - $tagParser = $this->serviceLocator->get('VuFind\Tags'); + $tagParser = $this->serviceLocator->get(\VuFind\Tags::class); $post['mytags'] = $tagParser->parse($post['mytags'] ?? ''); $favorites = $this->serviceLocator - ->get('VuFind\Favorites\FavoritesService'); + ->get(\VuFind\Favorites\FavoritesService::class); $results = $favorites->save($post, $user, $driver); // Display a success status message: @@ -415,7 +439,7 @@ class AbstractRecord extends AbstractBase $driver = $this->loadRecord(); // Create view - $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); + $mailer = $this->serviceLocator->get(\VuFind\Mailer\Mailer::class); $view = $this->createEmailViewModel( null, $mailer->getDefaultRecordSubject($driver) ); @@ -452,7 +476,8 @@ class AbstractRecord extends AbstractBase */ protected function smsEnabled() { - $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); + $check = $this->serviceLocator + ->get(\VuFind\Config\AccountCapabilities::class); return $check->getSmsSetting() !== 'disabled'; } @@ -472,7 +497,7 @@ class AbstractRecord extends AbstractBase $driver = $this->loadRecord(); // Load the SMS carrier list: - $sms = $this->serviceLocator->get('VuFind\SMS\SMSInterface'); + $sms = $this->serviceLocator->get(\VuFind\SMS\SMSInterface::class); $view = $this->createViewModel(); $view->carriers = $sms->getCarriers(); $view->validation = $sms->getValidationType(); @@ -527,7 +552,7 @@ class AbstractRecord extends AbstractBase $format = $this->params()->fromQuery('style'); // Display export menu if missing/invalid option - $export = $this->serviceLocator->get('VuFind\Export'); + $export = $this->serviceLocator->get(\VuFind\Export::class); if (empty($format) || !$export->recordSupportsFormat($driver, $format)) { if (!empty($format)) { $this->flashMessenger() @@ -637,18 +662,6 @@ class AbstractRecord extends AbstractBase return $this->redirect()->toUrl($target . $params); } - /** - * Alias to getRecordTabConfig for backward compatibility. - * - * @deprecated use getRecordTabConfig instead - * - * @return array - */ - protected function getTabConfiguration() - { - return $this->getRecordTabConfig(); - } - /** * Support method to load tab information from the RecordTab PluginManager. * @@ -658,16 +671,12 @@ class AbstractRecord extends AbstractBase { $driver = $this->loadRecord(); $request = $this->getRequest(); - $rtpm = $this->serviceLocator->get('VuFind\RecordTab\PluginManager'); - $details = $rtpm->getTabDetailsForRecord( - $driver, $this->getRecordTabConfig(), $request, - $this->fallbackDefaultTab - ); + $manager = $this->getRecordTabManager(); + $details = $manager + ->getTabDetailsForRecord($driver, $request, $this->fallbackDefaultTab); $this->allTabs = $details['tabs']; $this->defaultTab = $details['default'] ? $details['default'] : false; - $this->backgroundTabs = $rtpm->getBackgroundTabNames( - $driver, $this->getRecordTabConfig() - ); + $this->backgroundTabs = $manager->getBackgroundTabNames($driver); } /** diff --git a/module/VuFind/src/VuFind/Controller/AbstractSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSearch.php index 1127317d33f71e71ba709bc5ad44cd3fd65123ab..0c3348d9f03afc1c4a498eee2d19825150e23747 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractSearch.php +++ b/module/VuFind/src/VuFind/Controller/AbstractSearch.php @@ -29,6 +29,7 @@ namespace VuFind\Controller; use VuFind\Search\RecommendListener; use VuFind\Solr\Utils as SolrUtils; +use Zend\Session\SessionManager; use Zend\Stdlib\Parameters; /** @@ -86,7 +87,8 @@ class AbstractSearch extends AbstractBase { $view = $this->createViewModel(); $view->options = $this->serviceLocator - ->get('VuFind\Search\Options\PluginManager')->get($this->searchClassId); + ->get(\VuFind\Search\Options\PluginManager::class) + ->get($this->searchClassId); if ($view->options->getAdvancedSearchAction() === false) { throw new \Exception('Advanced search not supported.'); } @@ -102,7 +104,7 @@ class AbstractSearch extends AbstractBase // to properly populate special controls on the advanced screen. if (!$view->saved && count($view->options->getDefaultFilters()) > 0) { $view->saved = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager') + ->get(\VuFind\Search\Results\PluginManager::class) ->get($this->searchClassId); $view->saved->getParams()->initFromRequest( new \Zend\StdLib\Parameters([]) @@ -214,7 +216,8 @@ class AbstractSearch extends AbstractBase return null; } - $rManager = $this->serviceLocator->get('VuFind\Recommend\PluginManager'); + $rManager = $this->serviceLocator + ->get(\VuFind\Recommend\PluginManager::class); // Special case: override recommend settings through parameter (used by // combined search) @@ -249,7 +252,7 @@ class AbstractSearch extends AbstractBase */ public function homeAction() { - $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader') + $blocks = $this->serviceLocator->get(\VuFind\ContentBlock\BlockLoader::class) ->getFromSearchClassId($this->searchClassId); return $this->createViewModel(compact('blocks')); } @@ -269,7 +272,7 @@ class AbstractSearch extends AbstractBase return $this->redirectToSavedSearch($savedId); } - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); + $runner = $this->serviceLocator->get(\VuFind\Search\SearchRunner::class); // Send both GET and POST variables to search class: $request = $this->getRequest()->getQuery()->toArray() @@ -307,6 +310,10 @@ class AbstractSearch extends AbstractBase if ($this->resultScrollerActive()) { $this->resultScroller()->init($results); } + + foreach ($results->getErrors() as $error) { + $this->flashMessenger()->addErrorMessage($error); + } } // Special case: If we're in RSS view, we need to render differently: @@ -319,7 +326,7 @@ class AbstractSearch extends AbstractBase } // Search toolbar - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); $view->showBulkOptions = isset($config->Site->showBulkOptions) && $config->Site->showBulkOptions; @@ -339,7 +346,7 @@ class AbstractSearch extends AbstractBase { // Jump to only result, if configured $default = null; - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); if (isset($config->Record->jump_to_single_search_result) && $config->Record->jump_to_single_search_result @@ -377,7 +384,7 @@ class AbstractSearch extends AbstractBase protected function retrieveSearchSecurely($searchId) { $searchTable = $this->getTable('Search'); - $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); + $sessId = $this->serviceLocator->get(SessionManager::class)->getId(); $user = $this->getUser(); $userId = $user ? $user->id : null; return $searchTable->getOwnedRowById($searchId, $sessId, $userId); @@ -393,7 +400,7 @@ class AbstractSearch extends AbstractBase protected function saveSearchToHistory($results) { $user = $this->getUser(); - $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); + $sessId = $this->serviceLocator->get(SessionManager::class)->getId(); $history = $this->getTable('Search'); $history->saveSearch( $this->getResultsManager(), $results, $sessId, @@ -433,9 +440,6 @@ class AbstractSearch extends AbstractBase } } - // Activate facets so we get appropriate descriptions in the filter list: - $savedSearch->getParams()->activateAllFacets('Advanced'); - // Make the object available to the view: return $savedSearch; } @@ -447,7 +451,8 @@ class AbstractSearch extends AbstractBase */ protected function getResultsManager() { - return $this->serviceLocator->get('VuFind\Search\Results\PluginManager'); + return $this->serviceLocator + ->get(\VuFind\Search\Results\PluginManager::class); } /** @@ -469,7 +474,7 @@ class AbstractSearch extends AbstractBase // Check to see if there is an existing range in the search object: if ($savedSearch) { - $filters = $savedSearch->getParams()->getFilters(); + $filters = $savedSearch->getParams()->getRawFilters(); if (isset($filters[$field])) { foreach ($filters[$field] as $current) { if ($range = SolrUtils::parseRange($current)) { @@ -507,7 +512,7 @@ class AbstractSearch extends AbstractBase */ protected function getRangeFieldList($config, $section, $filter) { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get($config); $fields = isset($config->SpecialFacets->$section) ? $config->SpecialFacets->$section->toArray() : []; @@ -661,7 +666,7 @@ class AbstractSearch extends AbstractBase $section = $params[1] ?? 'CheckboxFacets'; // Load config file: - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get($config); // Process checkbox settings in config: @@ -713,14 +718,14 @@ class AbstractSearch extends AbstractBase $facet = $this->params()->fromQuery('facet'); $page = (int)$this->params()->fromQuery('facetpage', 1); $options = $results->getOptions(); - $facetSortOptions = $options->getFacetSortOptions(); + $facetSortOptions = $options->getFacetSortOptions($facet); $sort = $this->params()->fromQuery('facetsort', null); if ($sort === null || !in_array($sort, array_keys($facetSortOptions))) { $sort = empty($facetSortOptions) ? 'count' : current(array_keys($facetSortOptions)); } - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get($options->getFacetsIni()); $limit = isset($config->Results_Settings->lightboxLimit) ? $config->Results_Settings->lightboxLimit @@ -731,7 +736,6 @@ class AbstractSearch extends AbstractBase $this->params()->fromQuery('facetop', 'AND') == 'OR' ); $list = $facets[$facet]['data']['list'] ?? []; - $params->activateAllFacets(); $facetLabel = $params->getFacetLabel($facet); $view = $this->createViewModel( diff --git a/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php index 9aa07b0910b37299dd2a6b14df35aabb0bdf0e15..179cfa2acae20b207ec15d6e0e2bb0671c3253fb 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php +++ b/module/VuFind/src/VuFind/Controller/AbstractSolrSearch.php @@ -50,13 +50,20 @@ class AbstractSolrSearch extends AbstractSearch // Set up facet information: $facets = $this->serviceLocator - ->get('VuFind\Search\FacetCache\PluginManager') + ->get(\VuFind\Search\FacetCache\PluginManager::class) ->get($this->searchClassId) ->getList('Advanced'); $view->hierarchicalFacets = $this->getHierarchicalFacets($view->options->getFacetsIni()); + $view->hierarchicalFacetsSortOptions + = $this->getAdvancedHierarchicalFacetsSortOptions( + $view->options->getFacetsIni() + ); $view->facetList = $this->processAdvancedFacets( - $facets, $view->saved, $view->hierarchicalFacets + $facets, + $view->saved, + $view->hierarchicalFacets, + $view->hierarchicalFacetsSortOptions ); $specialFacets = $this->parseSpecialFacetsSetting( $view->options->getSpecialAdvancedFacets() @@ -116,27 +123,34 @@ class AbstractSolrSearch extends AbstractSearch /** * Process the facets to be used as limits on the Advanced Search screen. * - * @param array $facetList The advanced facet values - * @param object $searchObject Saved search object (false if none) - * @param array $hierarchicalFacets Hierarchical facet list (if any) + * @param array $facetList The advanced facet values + * @param object $searchObject Saved search object + * (false if none) + * @param array $hierarchicalFacets Hierarchical facet list (if any) + * @param array $hierarchicalFacetsSortOptions Hierarchical facet sort options + * (if any) * - * @return array Sorted facets, with selected values flagged. + * @return array Sorted facets, with selected values flagged. */ protected function processAdvancedFacets($facetList, $searchObject = false, - $hierarchicalFacets = [] + $hierarchicalFacets = [], $hierarchicalFacetsSortOptions = [] ) { // Process the facets $facetHelper = null; if (!empty($hierarchicalFacets)) { $facetHelper = $this->serviceLocator - ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + ->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class); } foreach ($facetList as $facet => &$list) { // Hierarchical facets: format display texts and sort facets // to a flat array according to the hierarchy if (in_array($facet, $hierarchicalFacets)) { $tmpList = $list['list']; - $facetHelper->sortFacetList($tmpList, true); + + $sort = $hierarchicalFacetsSortOptions[$facet] + ?? $hierarchicalFacetsSortOptions['*'] ?? 'top'; + + $facetHelper->sortFacetList($tmpList, $sort); $tmpList = $facetHelper->buildFacetArray( $facet, $tmpList @@ -181,4 +195,27 @@ class AbstractSolrSearch extends AbstractSearch ? $facetConfig->SpecialFacets->hierarchical->toArray() : []; } + + /** + * Get an array of hierarchical facet sort options for Advanced search + * + * @param string $config Name of facet configuration file to load. + * + * @return array + */ + protected function getAdvancedHierarchicalFacetsSortOptions($config) + { + $facetConfig = $this->getConfig($config); + $baseConfig + = isset($facetConfig->SpecialFacets->hierarchicalFacetSortOptions) + ? $facetConfig->SpecialFacets->hierarchicalFacetSortOptions->toArray() + : []; + $advancedConfig + = isset($facetConfig->Advanced_Settings->hierarchicalFacetSortOptions) + ? $facetConfig->Advanced_Settings->hierarchicalFacetSortOptions + ->toArray() + : []; + + return array_merge($baseConfig, $advancedConfig); + } } diff --git a/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php b/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php index 4055fa902ac2e800832ecab8763571e7dacb05ab..ab0daac6bccc059ee3dd8ff5836ba1dc5465408d 100644 --- a/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/AjaxControllerFactory.php @@ -61,7 +61,7 @@ class AjaxControllerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $pm = $container->get('VuFind\AjaxHandler\PluginManager'); + $pm = $container->get(\VuFind\AjaxHandler\PluginManager::class); return new $requestedName($pm); } } diff --git a/module/VuFind/src/VuFind/Controller/AlmaController.php b/module/VuFind/src/VuFind/Controller/AlmaController.php index 555d088cbc17d0932c83e7e1b55c2f94a9679a0b..3753d63c18f63a03d8373589ea9212e8ca7f5bbf 100644 --- a/module/VuFind/src/VuFind/Controller/AlmaController.php +++ b/module/VuFind/src/VuFind/Controller/AlmaController.php @@ -238,7 +238,7 @@ class AlmaController extends AbstractBase $user->username = $username; $user->firstname = $firstname; $user->lastname = $lastname; - $user->email = $email; + $user->updateEmail($email); $user->cat_id = $primaryId; $user->cat_username = $username; @@ -372,7 +372,7 @@ class AlmaController extends AbstractBase ] ); // Send the email - $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( + $this->serviceLocator->get(\VuFind\Mailer\Mailer::class)->send( $user->email, $config->Site->email, $this->translate( 'new_user_welcome_subject', diff --git a/module/VuFind/src/VuFind/Controller/AlphabrowseController.php b/module/VuFind/src/VuFind/Controller/AlphabrowseController.php index 4d9229d256d65d2a01433e4c7e35afd3e34782b8..b408b516623455fdcf68f24c319e5c73c0f50dfa 100644 --- a/module/VuFind/src/VuFind/Controller/AlphabrowseController.php +++ b/module/VuFind/src/VuFind/Controller/AlphabrowseController.php @@ -95,7 +95,7 @@ class AlphabrowseController extends AbstractBase ? (int)$config->AlphaBrowse->page_size : 20; // Connect to Solr: - $db = $this->serviceLocator->get('VuFind\Search\BackendManager') + $db = $this->serviceLocator->get(\VuFind\Search\BackendManager::class) ->get('Solr'); // Process incoming parameters: @@ -160,7 +160,7 @@ class AlphabrowseController extends AbstractBase $highlight_row = $rows_before; // special case: match row is < rows_before (i.e. at beginning of list) if ($startRow_adj < $rows_before) { - $highlight_row = $startRow_adj; + $highlight_row = $startRow_adj; } // special case: we've gone past the end // only the rows_before records will have been returned diff --git a/module/VuFind/src/VuFind/Controller/AuthorController.php b/module/VuFind/src/VuFind/Controller/AuthorController.php index e659071771ec71ebbf0c504b2ebb5b17decf373c..33beb4c640a84ef57e91a9c935a4d6ae88d5503d 100644 --- a/module/VuFind/src/VuFind/Controller/AuthorController.php +++ b/module/VuFind/src/VuFind/Controller/AuthorController.php @@ -106,7 +106,7 @@ class AuthorController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/AuthorityController.php b/module/VuFind/src/VuFind/Controller/AuthorityController.php index d560c24ce3e5817c82aa0fa54a5d738131ca579f..d803f88fbfa4d558e5cf85faaabc87f4a2a1ab56 100644 --- a/module/VuFind/src/VuFind/Controller/AuthorityController.php +++ b/module/VuFind/src/VuFind/Controller/AuthorityController.php @@ -76,14 +76,10 @@ class AuthorityController extends AbstractSearch public function recordAction() { $id = $this->params()->fromQuery('id'); - $cfg = $this->serviceLocator->get('Config'); - $tabConfig = $cfg['vufind']['recorddriver_tabs']; - $driver = $this->serviceLocator->get('VuFind\Record\Loader') + $driver = $this->serviceLocator->get(\VuFind\Record\Loader::class) ->load($id, 'SolrAuth'); $request = $this->getRequest(); - $tabs = $this->serviceLocator - ->get('VuFind\RecordTab\PluginManager') - ->getTabsForRecord($driver, $tabConfig, $request); + $tabs = $this->getRecordTabManager()->getTabsForRecord($driver, $request); return $this->createViewModel(['driver' => $driver, 'tabs' => $tabs]); } diff --git a/module/VuFind/src/VuFind/Controller/BrowseController.php b/module/VuFind/src/VuFind/Controller/BrowseController.php index 09a1bb2f14f65175b4bb24ecdca16bf2dd791175..84b5cab233b80208ddfdef2de67c960a2e5664c4 100644 --- a/module/VuFind/src/VuFind/Controller/BrowseController.php +++ b/module/VuFind/src/VuFind/Controller/BrowseController.php @@ -596,7 +596,7 @@ class BrowseController extends AbstractBase $sort = 'count', $query = '[* TO *]' ) { $results = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get('Solr'); $params = $results->getParams(); $params->addFacet($facet); if ($category != null) { diff --git a/module/VuFind/src/VuFind/Controller/CartController.php b/module/VuFind/src/VuFind/Controller/CartController.php index db97500faf4ddea1ab633ab33428f2d2b6dea984..ba1ee787d90c64e55be0e55929abbb78f7397660 100644 --- a/module/VuFind/src/VuFind/Controller/CartController.php +++ b/module/VuFind/src/VuFind/Controller/CartController.php @@ -69,7 +69,7 @@ class CartController extends AbstractBase */ protected function getCart() { - return $this->serviceLocator->get('VuFind\Cart'); + return $this->serviceLocator->get(\VuFind\Cart::class); } /** @@ -270,7 +270,7 @@ class CartController extends AbstractBase // Attempt to send the email and show an appropriate flash message: try { // If we got this far, we're ready to send the email: - $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); + $mailer = $this->serviceLocator->get(\VuFind\Mailer\Mailer::class); $mailer->setMaxRecipients($view->maxRecipients); $cc = $this->params()->fromPost('ccself') && $view->from != $view->to ? $view->from : null; @@ -315,7 +315,7 @@ class CartController extends AbstractBase */ protected function getExport() { - return $this->serviceLocator->get('VuFind\Export'); + return $this->serviceLocator->get(\VuFind\Export::class); } /** diff --git a/module/VuFind/src/VuFind/Controller/CartControllerFactory.php b/module/VuFind/src/VuFind/Controller/CartControllerFactory.php index 776eaea2ec19b072a9232a5a7968dc5172338e8a..5e5a908056d627b45eba6d87db6aeebbbd31e6cb 100644 --- a/module/VuFind/src/VuFind/Controller/CartControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/CartControllerFactory.php @@ -28,7 +28,6 @@ namespace VuFind\Controller; use Interop\Container\ContainerInterface; -use Zend\ServiceManager\Factory\FactoryInterface; /** * Cart controller factory. @@ -39,7 +38,7 @@ use Zend\ServiceManager\Factory\FactoryInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class CartControllerFactory implements FactoryInterface +class CartControllerFactory extends AbstractBaseFactory { /** * Create an object @@ -62,8 +61,11 @@ class CartControllerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } $session = new \Zend\Session\Container( - 'cart_followup', $container->get('Zend\Session\SessionManager') + 'cart_followup', $container->get(\Zend\Session\SessionManager::class) + ); + return $this->applyPermissions( + $container, + new $requestedName($container, $session) ); - return new $requestedName($container, $session); } } diff --git a/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php index 72fb8015b4701bf014c80c70c84d119377424c8b..28e3c9217069161a887681a45b7ca48a117f08ed 100644 --- a/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php @@ -28,7 +28,6 @@ namespace VuFind\Controller; use Interop\Container\ContainerInterface; -use Zend\ServiceManager\Factory\FactoryInterface; /** * Channels controller factory. @@ -39,7 +38,7 @@ use Zend\ServiceManager\Factory\FactoryInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class ChannelsControllerFactory implements FactoryInterface +class ChannelsControllerFactory extends AbstractBaseFactory { /** * Create an object @@ -61,7 +60,7 @@ class ChannelsControllerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $loader = $container->get('VuFind\ChannelProvider\ChannelLoader'); - return new $requestedName($loader); + $loader = $container->get(\VuFind\ChannelProvider\ChannelLoader::class); + return $this->applyPermissions($container, new $requestedName($loader)); } } diff --git a/module/VuFind/src/VuFind/Controller/CollectionController.php b/module/VuFind/src/VuFind/Controller/CollectionController.php index e2848e13e149b44275319528167a5e35a5bb2523..39b32670b6982968ebbdbb5ac2197e84b9ccc2ed 100644 --- a/module/VuFind/src/VuFind/Controller/CollectionController.php +++ b/module/VuFind/src/VuFind/Controller/CollectionController.php @@ -61,12 +61,13 @@ class CollectionController extends AbstractRecord /** * Get the tab configuration for this controller. * - * @return array + * @return \VuFind\RecordTab\TabManager */ - protected function getRecordTabConfig() + protected function getRecordTabManager() { - $cfg = $this->serviceLocator->get('Config'); - return $cfg['vufind']['recorddriver_collection_tabs']; + $manager = parent::getRecordTabManager(); + $manager->setContext('collection'); + return $manager; } /** @@ -99,7 +100,7 @@ class CollectionController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/CollectionsController.php b/module/VuFind/src/VuFind/Controller/CollectionsController.php index 7055802642a230abc93b781594cd1e314b066543..244604bdd0a40c6e5ee299b113364da9176867e2 100644 --- a/module/VuFind/src/VuFind/Controller/CollectionsController.php +++ b/module/VuFind/src/VuFind/Controller/CollectionsController.php @@ -116,7 +116,7 @@ class CollectionsController extends AbstractBase $limit = $this->getBrowseLimit(); // Load Solr data or die trying: - $db = $this->serviceLocator->get('VuFind\Search\BackendManager') + $db = $this->serviceLocator->get(\VuFind\Search\BackendManager::class) ->get('Solr'); $result = $db->alphabeticBrowse($source, $from, $page, $limit); @@ -174,7 +174,7 @@ class CollectionsController extends AbstractBase $browseField = "hierarchy_browse"; $searchObject = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get('Solr'); foreach ($appliedFilters as $filter) { $searchObject->getParams()->addFilter($filter); } @@ -189,7 +189,7 @@ class CollectionsController extends AbstractBase foreach ($result as $rkey => $collection) { list($name, $id) = explode($delimiter, $collection['value'], 2); $result[$rkey]['displayText'] = $name; - $result[$rkey]['value'] = $id; + $result[$rkey]['value'] = $id; } // Sort the $results and get the position of the from string once sorted @@ -337,7 +337,7 @@ class CollectionsController extends AbstractBase { $title = addcslashes($title, '"'); $query = new Query("is_hierarchy_title:\"$title\"", 'AllFields'); - $searchService = $this->serviceLocator->get('VuFindSearch\Service'); + $searchService = $this->serviceLocator->get(\VuFindSearch\Service::class); $result = $searchService->search('Solr', $query, 0, $this->getBrowseLimit()); return $result->getRecords(); } diff --git a/module/VuFind/src/VuFind/Controller/CombinedController.php b/module/VuFind/src/VuFind/Controller/CombinedController.php index 6c8351d5fc94de5c4bae110fdab817c858b95fa6..74bd57eb06cd2e09778eeb5dd666a0eec5410941 100644 --- a/module/VuFind/src/VuFind/Controller/CombinedController.php +++ b/module/VuFind/src/VuFind/Controller/CombinedController.php @@ -27,6 +27,7 @@ */ namespace VuFind\Controller; +use VuFind\Search\SearchRunner; use Zend\ServiceManager\ServiceLocatorInterface; /** @@ -62,7 +63,7 @@ class CombinedController extends AbstractSearch { // We need to load blocks differently in this controller since it // doesn't follow the usual configuration pattern. - $blocks = $this->serviceLocator->get('VuFind\ContentBlock\BlockLoader') + $blocks = $this->serviceLocator->get(\VuFind\ContentBlock\BlockLoader::class) ->getFromConfig('combined'); return $this->createViewModel(compact('blocks')); } @@ -81,7 +82,7 @@ class CombinedController extends AbstractSearch // Validate configuration: $sectionId = $this->params()->fromQuery('id'); - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('combined')->toArray(); $tabConfig = $this->getTabConfig($config); if (!isset($tabConfig[$sectionId])) { @@ -91,23 +92,27 @@ class CombinedController extends AbstractSearch // Retrieve results: $options = $this->serviceLocator - ->get('VuFind\Search\Options\PluginManager'); + ->get(\VuFind\Search\Options\PluginManager::class); $currentOptions = $options->get($searchClassId); list($controller, $action) = explode('-', $currentOptions->getSearchAction()); $settings = $tabConfig[$sectionId]; - $this->adjustQueryForSettings($settings); + $this->adjustQueryForSettings( + $settings, + $currentOptions->getHandlerForLabel($this->params()->fromQuery('type')) + ); $settings['view'] = $this->forwardTo($controller, $action); // Should we suppress content due to emptiness? - if (isset($settings['hide_if_empty']) && $settings['hide_if_empty'] + if (($settings['hide_if_empty'] ?? false) && $settings['view']->results->getResultTotal() == 0 ) { $html = ''; } else { - $cart = $this->serviceLocator->get('VuFind\Cart'); - $general = $this->serviceLocator->get('VuFind\Config\PluginManager') + $cart = $this->serviceLocator->get(\VuFind\Cart::class); + $general = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class) ->get('config'); $viewParams = [ 'searchClassId' => $searchClassId, @@ -115,8 +120,7 @@ class CombinedController extends AbstractSearch 'showCartControls' => $currentOptions->supportsCart() && $cart->isActive(), 'showBulkOptions' => $currentOptions->supportsCart() - && isset($general->Site->showBulkOptions) - && $general->Site->showBulkOptions + && ($general->Site->showBulkOptions ?? false) ]; // Load custom CSS, if necessary: $html = $this->getViewRenderer()->plugin('headLink')->__invoke(); @@ -139,7 +143,7 @@ class CombinedController extends AbstractSearch // Set up current request context: $request = $this->getRequest()->getQuery()->toArray() + $this->getRequest()->getPost()->toArray(); - $results = $this->serviceLocator->get('VuFind\Search\SearchRunner')->run( + $results = $this->serviceLocator->get(SearchRunner::class)->run( $request, 'Combined', $this->getSearchSetupCallback() ); @@ -151,15 +155,19 @@ class CombinedController extends AbstractSearch // Gather combined results: $combinedResults = []; $options = $this->serviceLocator - ->get('VuFind\Search\Options\PluginManager'); - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + ->get(\VuFind\Search\Options\PluginManager::class); + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('combined')->toArray(); $supportsCart = false; $supportsCartOptions = []; + // Save the initial type value, since it may get manipulated below: + $initialType = $this->params()->fromQuery('type'); foreach ($this->getTabConfig($config) as $current => $settings) { list($searchClassId) = explode(':', $current); - $this->adjustQueryForSettings($settings); $currentOptions = $options->get($searchClassId); + $this->adjustQueryForSettings( + $settings, $currentOptions->getHandlerForLabel($initialType) + ); $supportsCartOptions[] = $currentOptions->supportsCart(); if ($currentOptions->supportsCart()) { $supportsCart = true; @@ -173,10 +181,9 @@ class CombinedController extends AbstractSearch $combinedResults[$current]['domId'] = 'combined_' . str_replace(':', '____', $current); - $combinedResults[$current]['view'] - = (!isset($settings['ajax']) || !$settings['ajax']) - ? $this->forwardTo($controller, $action) - : $this->createViewModel(['results' => $results]); + $combinedResults[$current]['view'] = ($settings['ajax'] ?? false) + ? $this->createViewModel(['results' => $results]) + : $this->forwardTo($controller, $action); // Special case: include appropriate "powered by" message: if (strtolower($searchClassId) == 'summon') { @@ -185,13 +192,15 @@ class CombinedController extends AbstractSearch } } + // Restore the initial type value to the query to prevent weird behavior: + $this->getRequest()->getQuery()->type = $initialType; + // Run the search to obtain recommendations: $results->performAndProcessSearch(); - $columns = isset($config['Layout']['columns']) - && intval($config['Layout']['columns']) <= count($combinedResults) - ? intval($config['Layout']['columns']) - : count($combinedResults); + $actualMaxColumns = count($combinedResults); + $columnConfig = intval($config['Layout']['columns'] ?? $actualMaxColumns); + $columns = min($columnConfig, $actualMaxColumns); $placement = $config['Layout']['stack_placement'] ?? 'distributed'; if (!in_array($placement, ['distributed', 'left', 'right'])) { @@ -199,7 +208,7 @@ class CombinedController extends AbstractSearch } // Get default config for showBulkOptions - $settings = $this->serviceLocator->get('VuFind\Config\PluginManager') + $settings = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); // Build view model: @@ -240,7 +249,7 @@ class CombinedController extends AbstractSearch unset($params['activeSearchClassId']); // don't need to pass this forward $route = $this->serviceLocator - ->get('VuFind\Search\Options\PluginManager') + ->get(\VuFind\Search\Options\PluginManager::class) ->get($searchClassId)->getSearchAction(); $base = $this->url()->fromRoute($route); return $this->redirect()->toUrl($base . '?' . http_build_query($params)); @@ -267,11 +276,12 @@ class CombinedController extends AbstractSearch /** * Adjust the query context to reflect the current settings. * - * @param array $settings Settings + * @param array $settings Settings + * @param string $searchType Override for search handler name * * @return void */ - protected function adjustQueryForSettings($settings) + protected function adjustQueryForSettings($settings, $searchType = null) { // Apply limit setting, if any: $query = $this->getRequest()->getQuery(); @@ -296,12 +306,13 @@ class CombinedController extends AbstractSearch // load a record view in the context of combined search. $query->jumpto = false; + // Override the search type: + $query->type = $searchType; + // Always leave noresults active (useful for 0-hit searches) and // side inactive (no room to display) but display or hide top based // on include_recommendations setting. - if (isset($settings['include_recommendations']) - && $settings['include_recommendations'] - ) { + if ($settings['include_recommendations'] ?? false) { $query->noRecommend = 'side'; if (is_array($settings['include_recommendations'])) { $query->recommendOverride @@ -322,6 +333,7 @@ class CombinedController extends AbstractSearch protected function getTabConfig($config) { // Strip out non-tab sections of the configuration: + unset($config['Basic_Searches']); unset($config['HomePage']); unset($config['Layout']); unset($config['RecommendationModules']); diff --git a/module/VuFind/src/VuFind/Controller/ContentController.php b/module/VuFind/src/VuFind/Controller/ContentController.php index 85b9de46d4355b1c853f9bb62ad44267dcb780f3..0c49d0d5ecc89d8602dd104734adfff65553409b 100644 --- a/module/VuFind/src/VuFind/Controller/ContentController.php +++ b/module/VuFind/src/VuFind/Controller/ContentController.php @@ -50,8 +50,8 @@ class ContentController extends AbstractBase public function contentAction() { $page = $this->params()->fromRoute('page'); - $themeInfo = $this->serviceLocator->get('VuFindTheme\ThemeInfo'); - $language = $this->serviceLocator->get('Zend\Mvc\I18n\Translator') + $themeInfo = $this->serviceLocator->get(\VuFindTheme\ThemeInfo::class); + $language = $this->serviceLocator->get(\Zend\Mvc\I18n\Translator::class) ->getLocale(); $defaultLanguage = $this->getConfig()->Site->language; diff --git a/module/VuFind/src/VuFind/Controller/CoverController.php b/module/VuFind/src/VuFind/Controller/CoverController.php index c4d3b6770689de0de2db8989079236d5e41b6a2d..6e8e0aa7f9002b46f3c5c8c52604188257a17043 100644 --- a/module/VuFind/src/VuFind/Controller/CoverController.php +++ b/module/VuFind/src/VuFind/Controller/CoverController.php @@ -99,6 +99,8 @@ class CoverController extends \Zend\Mvc\Controller\AbstractActionController 'upc' => $params()->fromQuery('upc'), 'recordid' => $params()->fromQuery('recordid'), 'source' => $params()->fromQuery('source'), + 'nbn' => $params()->fromQuery('nbn'), + 'ismn' => $params()->fromQuery('ismn'), ]; } diff --git a/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php b/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php index 26de632cd2a86d138aa8747a1ccb5e1438d643bd..6ae4ab36cbcce2f25ffcd2842498d1986375cdf0 100644 --- a/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/CoverControllerFactory.php @@ -62,9 +62,9 @@ class CoverControllerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Cover\Loader'), - $container->get('VuFind\Cover\CachingProxy'), - $container->get('VuFind\Session\Settings') + $container->get(\VuFind\Cover\Loader::class), + $container->get(\VuFind\Cover\CachingProxy::class), + $container->get(\VuFind\Session\Settings::class) ); } } diff --git a/module/VuFind/src/VuFind/Controller/EITController.php b/module/VuFind/src/VuFind/Controller/EITController.php index b38f4dfd7e6ca4418727cbc3185e0943fce94113..b083bf6133d602f3dccfb4b8b3c2aff3e714a8e4 100644 --- a/module/VuFind/src/VuFind/Controller/EITController.php +++ b/module/VuFind/src/VuFind/Controller/EITController.php @@ -59,7 +59,7 @@ class EITController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('EIT'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/EITrecordController.php b/module/VuFind/src/VuFind/Controller/EITrecordController.php index 5bab533561128c0f64aa829b0d6d693b8fc6677f..902d58919dfcf4fdb78b88ea3a0950fdd2cfaf5e 100644 --- a/module/VuFind/src/VuFind/Controller/EITrecordController.php +++ b/module/VuFind/src/VuFind/Controller/EITrecordController.php @@ -66,7 +66,7 @@ class EITrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('EIT'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/EdsController.php b/module/VuFind/src/VuFind/Controller/EdsController.php index cb49ea2d27e28768e43fae6ec0a2f2746fd70604..f8fbbfee14e3d7675031501295f14db0da30f3d4 100644 --- a/module/VuFind/src/VuFind/Controller/EdsController.php +++ b/module/VuFind/src/VuFind/Controller/EdsController.php @@ -59,7 +59,7 @@ class EdsController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('EDS'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/EdsrecordController.php b/module/VuFind/src/VuFind/Controller/EdsrecordController.php index 526588c36ffe92ee2a8640cc080e91d1d3863da0..f78ba6c2d18441ffed94bd3be8c6ad6a36977a79 100644 --- a/module/VuFind/src/VuFind/Controller/EdsrecordController.php +++ b/module/VuFind/src/VuFind/Controller/EdsrecordController.php @@ -82,7 +82,7 @@ class EdsrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('EDS'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/ExternalAuthController.php b/module/VuFind/src/VuFind/Controller/ExternalAuthController.php index 65a646c199eda9e6463e9023c5eedd075cac97cf..0de5b2046ed702dae390b4ab9555768b514c30b3 100644 --- a/module/VuFind/src/VuFind/Controller/ExternalAuthController.php +++ b/module/VuFind/src/VuFind/Controller/ExternalAuthController.php @@ -68,11 +68,11 @@ class ExternalAuthController extends AbstractBase implements LoggerAwareInterfac $user = $this->getUser(); $authService = $this->serviceLocator - ->get('ZfcRbac\Service\AuthorizationService'); + ->get(\ZfcRbac\Service\AuthorizationService::class); if ($authService->isGranted($this->ezproxyRequiredPermission)) { // Access granted, redirect to EZproxy if (empty($config->EZproxy->disable_ticket_auth_logging)) { - $logger = $this->serviceLocator->get('VuFind\Logger'); + $logger = $this->serviceLocator->get(\VuFind\Logger::class); $logger->log( \Zend\Log\Logger::INFO, "EZproxy login to '" . $config->EZproxy->host diff --git a/module/VuFind/src/VuFind/Controller/FeedbackController.php b/module/VuFind/src/VuFind/Controller/FeedbackController.php index 7251b3b5399a2942d56753aceeb688f510a99a43..21880527c2e4de9144ffa03ce800a157e7bda160 100644 --- a/module/VuFind/src/VuFind/Controller/FeedbackController.php +++ b/module/VuFind/src/VuFind/Controller/FeedbackController.php @@ -54,13 +54,17 @@ class FeedbackController extends AbstractBase $user = $this->getUser(); - $form = $this->serviceLocator->get('VuFind\Form\Form'); + $form = $this->serviceLocator->get(\VuFind\Form\Form::class); $form->setFormId($formId); if (!$form->isEnabled()) { throw new \VuFind\Exception\Forbidden("Form '$formId' is disabled"); } + if (!$user && $form->showOnlyForLoggedUsers()) { + return $this->forceLogin(); + } + $view = $this->createViewModel(compact('form', 'formId', 'user')); $view->useRecaptcha = $this->recaptcha()->active('feedback') && $form->useCaptcha(); @@ -94,16 +98,28 @@ class FeedbackController extends AbstractBase $user ? $user->email : null ); - list($recipientName, $recipientEmail) = $form->getRecipient(); + $recipients = $form->getRecipient(); $emailSubject = $form->getEmailSubject($params->fromPost()); - list($success, $errorMsg) = $this->sendEmail( - $recipientName, $recipientEmail, $senderName, $senderEmail, - $replyToName, $replyToEmail, $emailSubject, $emailMessage - ); + $sendSuccess = true; + foreach ($recipients as $recipient) { + list($success, $errorMsg) = $this->sendEmail( + $recipient['name'], $recipient['email'], $senderName, $senderEmail, + $replyToName, $replyToEmail, $emailSubject, $emailMessage + ); - $this->showResponse($view, $form, $success, $errorMsg); + $sendSuccess = $sendSuccess && $success; + if (!$success) { + $this->showResponse( + $view, $form, false, $errorMsg + ); + } + } + + if ($sendSuccess) { + $this->showResponse($view, $form, true); + } return $view; } @@ -148,10 +164,7 @@ class FeedbackController extends AbstractBase $replyToName, $replyToEmail, $emailSubject, $emailMessage ) { try { - $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); - if ($replyToEmail) { - $mailer->setFromAddressOverride(''); - } + $mailer = $this->serviceLocator->get(\VuFind\Mailer\Mailer::class); $mailer->send( new Address($recipientEmail, $recipientName), new Address($senderEmail, $senderName), diff --git a/module/VuFind/src/VuFind/Controller/HierarchyController.php b/module/VuFind/src/VuFind/Controller/HierarchyController.php index c99b5edce387fa58ba689d059061436a2233c7ee..be65a4bbe6502d15e51e533caf969980fb825b6b 100644 --- a/module/VuFind/src/VuFind/Controller/HierarchyController.php +++ b/module/VuFind/src/VuFind/Controller/HierarchyController.php @@ -86,11 +86,13 @@ class HierarchyController extends AbstractBase ? $config->Hierarchy->treeSearchLimit : -1; $resultIDs = []; $hierarchyID = $this->params()->fromQuery('hierarchyID'); + $source = $this->params() + ->fromQuery('hierarchySource', DEFAULT_SEARCH_BACKEND); $lookfor = $this->params()->fromQuery('lookfor', ''); $searchType = $this->params()->fromQuery('type', 'AllFields'); $results = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get($source); $results->getParams()->setBasicSearch($lookfor, $searchType); $results->getParams()->addFilter('hierarchy_top_id:' . $hierarchyID); $facets = $results->getFullFieldFacets(['id'], false, $limit + 1); @@ -120,9 +122,11 @@ class HierarchyController extends AbstractBase $this->disableSessionWrites(); // avoid session write timing bug // Retrieve the record from the index $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\Record\Loader'); + $source = $this->params() + ->fromQuery('hierarchySource', DEFAULT_SEARCH_BACKEND); + $loader = $this->serviceLocator->get(\VuFind\Record\Loader::class); try { - if ($recordDriver = $loader->load($id)) { + if ($recordDriver = $loader->load($id, $source)) { $results = $recordDriver->getHierarchyDriver()->render( $recordDriver, $this->params()->fromQuery('context'), @@ -153,9 +157,11 @@ class HierarchyController extends AbstractBase $this->disableSessionWrites(); // avoid session write timing bug // Retrieve the record from the index $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\Record\Loader'); + $source = $this->params() + ->fromQuery('hierarchySource', DEFAULT_SEARCH_BACKEND); + $loader = $this->serviceLocator->get(\VuFind\Record\Loader::class); try { - if ($recordDriver = $loader->load($id)) { + if ($recordDriver = $loader->load($id, $source)) { $results = $recordDriver->getHierarchyDriver() ->getTreeRenderer($recordDriver)->getJSON( $this->params()->fromQuery('hierarchyID'), @@ -183,9 +189,11 @@ class HierarchyController extends AbstractBase public function getrecordAction() { $id = $this->params()->fromQuery('id'); - $loader = $this->serviceLocator->get('VuFind\Record\Loader'); + $source = $this->params() + ->fromQuery('hierarchySource', DEFAULT_SEARCH_BACKEND); + $loader = $this->serviceLocator->get(\VuFind\Record\Loader::class); try { - $record = $loader->load($id); + $record = $loader->load($id, $source); $result = $this->getViewRenderer()->record($record) ->getCollectionBriefRecord(); } catch (\VuFind\Exception\RecordMissing $e) { diff --git a/module/VuFind/src/VuFind/Controller/HoldsTrait.php b/module/VuFind/src/VuFind/Controller/HoldsTrait.php index e1081430708b15c899510f195233c72dd1b5ed24..b6fa76ef514094f5563b89c73202d682d22aeb31 100644 --- a/module/VuFind/src/VuFind/Controller/HoldsTrait.php +++ b/module/VuFind/src/VuFind/Controller/HoldsTrait.php @@ -117,7 +117,8 @@ trait HoldsTrait $gatheredDetails, $extraHoldFields, $requestGroups ); $validPickup = $validGroup && $this->holds()->validatePickUpInput( - $gatheredDetails['pickUpLocation'], $extraHoldFields, $pickup + $gatheredDetails['pickUpLocation'] ?? null, + $extraHoldFields, $pickup ); if (!$validGroup) { $this->flashMessenger() @@ -165,7 +166,7 @@ trait HoldsTrait $defaultRequired = $this->holds()->getDefaultRequiredDate( $checkHolds, $catalog, $patron, $gatheredDetails ); - $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') + $defaultRequired = $this->serviceLocator->get(\VuFind\Date\Converter::class) ->convertToDisplayDate("U", $defaultRequired); try { $defaultPickup @@ -181,12 +182,15 @@ trait HoldsTrait $defaultRequestGroup = false; } + $config = $this->getConfig(); + $allowHomeLibrary = $config->Account->set_home_library ?? true; $view = $this->createViewModel( [ 'gatheredDetails' => $gatheredDetails, 'pickup' => $pickup, 'defaultPickup' => $defaultPickup, - 'homeLibrary' => $this->getUser()->home_library, + 'homeLibrary' => $allowHomeLibrary + ? $this->getUser()->home_library : '', 'extraHoldFields' => $extraHoldFields, 'defaultRequiredDate' => $defaultRequired, 'requestGroups' => $requestGroups, diff --git a/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php b/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php index b06d691ea1de1bf9e2f68cf52a7ebe8a75f401d9..36ab7f43e8a91f48aebf36bb7aa79bae783838cf 100644 --- a/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php +++ b/module/VuFind/src/VuFind/Controller/ILLRequestsTrait.php @@ -132,7 +132,7 @@ trait ILLRequestsTrait // Find and format the default required date: $defaultRequired = $this->ILLRequests() ->getDefaultRequiredDate($checkRequests); - $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') + $defaultRequired = $this->serviceLocator->get(\VuFind\Date\Converter::class) ->convertToDisplayDate("U", $defaultRequired); // Get pickup libraries @@ -145,12 +145,15 @@ trait ILLRequestsTrait // selected. $pickupLocations = $catalog->getPickUpLocations($patron, $gatheredDetails); + $config = $this->getConfig(); + $allowHomeLibrary = $config->Account->set_home_library ?? true; $view = $this->createViewModel( [ 'gatheredDetails' => $gatheredDetails, 'pickupLibraries' => $pickupLibraries, 'pickupLocations' => $pickupLocations, - 'homeLibrary' => $this->getUser()->home_library, + 'homeLibrary' => $allowHomeLibrary + ? $this->getUser()->home_library : '', 'extraFields' => $extraFields, 'defaultRequiredDate' => $defaultRequired, 'helpText' => $checkRequests['helpText'] ?? null diff --git a/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php b/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php index 40d94a426b43c3144422f4c7c2e7504c94983178..96f80558c56a6486002c12caa0d2e68bdedb05be 100644 --- a/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/IndexControllerFactory.php @@ -61,8 +61,9 @@ class IndexControllerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); - $authManager = $container->get('VuFind\Auth\Manager'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $authManager = $container->get(\VuFind\Auth\Manager::class); return new $requestedName($config, $authManager); } } diff --git a/module/VuFind/src/VuFind/Controller/InstallController.php b/module/VuFind/src/VuFind/Controller/InstallController.php index 0bbd22c2a4a4258a7df9ae174f2582fa07b98907..d45cb21701faaf88336e8b9634677f0078ceb666 100644 --- a/module/VuFind/src/VuFind/Controller/InstallController.php +++ b/module/VuFind/src/VuFind/Controller/InstallController.php @@ -166,7 +166,7 @@ class InstallController extends AbstractBase */ protected function checkCache() { - $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); + $cache = $this->serviceLocator->get(\VuFind\Cache\Manager::class); return [ 'title' => 'Cache', 'status' => !$cache->hasDirectoryCreationError(), @@ -181,7 +181,7 @@ class InstallController extends AbstractBase */ public function fixcacheAction() { - $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); + $cache = $this->serviceLocator->get(\VuFind\Cache\Manager::class); $view = $this->createViewModel(); $view->cacheDir = $cache->getCacheDir(); if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) { @@ -355,7 +355,8 @@ class InstallController extends AbstractBase try { $dbName = ($view->driver == 'pgsql') ? 'template1' : $view->driver; - $db = $this->serviceLocator->get('VuFind\Db\AdapterFactory') + $db = $this->serviceLocator + ->get(\VuFind\Db\AdapterFactory::class) ->getAdapterFromConnectionString("{$connection}/{$dbName}"); } catch (\Exception $e) { $this->flashMessenger() @@ -392,7 +393,7 @@ class InstallController extends AbstractBase $db->query($query, $db::QUERY_MODE_EXECUTE); } $dbFactory = $this->serviceLocator - ->get('VuFind\Db\AdapterFactory'); + ->get(\VuFind\Db\AdapterFactory::class); $db = $dbFactory->getAdapterFromConnectionString( $connection . '/' . $view->dbname ); @@ -453,11 +454,13 @@ class InstallController extends AbstractBase return [$create, $escape, $cuser, $grant]; } // Default: MySQL: + $user = "CREATE USER '{$view->dbuser}'@'{$view->vufindhost}'" + . "IDENTIFIED BY {$escapedPass}"; $grant = "GRANT SELECT,INSERT,UPDATE,DELETE ON " . $view->dbname . ".* TO '{$view->dbuser}'@'{$view->vufindhost}' " - . "IDENTIFIED BY {$escapedPass} WITH GRANT OPTION"; - return [$create, $grant, 'FLUSH PRIVILEGES']; + . "WITH GRANT OPTION"; + return [$create, $user, $grant, 'FLUSH PRIVILEGES']; } /** @@ -472,9 +475,9 @@ class InstallController extends AbstractBase { // Special case: PostgreSQL: if ($view->driver == 'pgsql') { - $grantTables = "GRANT ALL PRIVILEGES ON ALL TABLES IN " + $grantTables = "GRANT ALL PRIVILEGES ON ALL TABLES IN " . "SCHEMA public TO {$view->dbuser} "; - $grantSequences = "GRANT ALL PRIVILEGES ON ALL SEQUENCES" + $grantSequences = "GRANT ALL PRIVILEGES ON ALL SEQUENCES" . " IN SCHEMA public TO {$view->dbuser} "; return [$grantTables, $grantSequences]; } @@ -586,7 +589,7 @@ class InstallController extends AbstractBase protected function testSearchService() { // Try to retrieve an arbitrary ID -- this will fail if Solr is down: - $searchService = $this->serviceLocator->get('VuFindSearch\Service'); + $searchService = $this->serviceLocator->get(\VuFindSearch\Service::class); $searchService->retrieve('Solr', '1'); } @@ -801,7 +804,7 @@ class InstallController extends AbstractBase { // Try to retrieve an SSL URL; if we're misconfigured, it will fail. try { - $this->serviceLocator->get('VuFindHttp\HttpService') + $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->get('https://google.com'); $status = true; } catch (\VuFindHttp\Exception\RuntimeException $e) { diff --git a/module/VuFind/src/VuFind/Controller/LibGuidesController.php b/module/VuFind/src/VuFind/Controller/LibGuidesController.php index a31fa3c6bf383f268449aba4e4d38f6d1f0e4ba6..9230d58ca49258bfe8ade19f7bfe82179c479502 100644 --- a/module/VuFind/src/VuFind/Controller/LibGuidesController.php +++ b/module/VuFind/src/VuFind/Controller/LibGuidesController.php @@ -58,7 +58,7 @@ class LibGuidesController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('LibGuides'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/LibraryCardsController.php b/module/VuFind/src/VuFind/Controller/LibraryCardsController.php index dbc47d66067bc6bc0d92b5aa016f9a8066f914e7..4d249b208a59fb6cf830870cbda0b62b75e3bab0 100644 --- a/module/VuFind/src/VuFind/Controller/LibraryCardsController.php +++ b/module/VuFind/src/VuFind/Controller/LibraryCardsController.php @@ -5,7 +5,7 @@ * PHP version 7 * * Copyright (C) Villanova University 2010. - * Copyright (C) The National Library of Finland 2015. + * Copyright (C) The National Library of Finland 2015-2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -99,6 +99,13 @@ class LibraryCardsController extends AbstractBase return $this->forceLogin(); } + // Process email authentication: + if ($this->params()->fromQuery('auth_method') === 'Email' + && ($hash = $this->params()->fromQuery('hash')) + ) { + return $this->processEmailLink($user, $hash); + } + // Process form submission: if ($this->formWasSubmitted('submit')) { if ($redirect = $this->processEditLibraryCard($user)) { @@ -111,17 +118,13 @@ class LibraryCardsController extends AbstractBase $target = null; $username = $card->cat_username; - $targets = null; - $defaultTarget = null; - // Connect to the ILS and check if multiple target support is available: - $catalog = $this->getILS(); - if ($catalog->checkCapability('getLoginDrivers')) { - $targets = $catalog->getLoginDrivers(); - $defaultTarget = $catalog->getDefaultLoginDriver(); - if (strstr($username, '.')) { - list($target, $username) = explode('.', $username, 2); - } + + $loginSettings = $this->getILSLoginSettings(); + // Split target and username if multiple login targets are available: + if ($loginSettings['targets'] && strstr($username, '.')) { + list($target, $username) = explode('.', $username, 2); } + $cardName = $this->params()->fromPost('card_name', $card->card_name); $username = $this->params()->fromPost('username', $username); $target = $this->params()->fromPost('target', $target); @@ -131,10 +134,12 @@ class LibraryCardsController extends AbstractBase [ 'card' => $card, 'cardName' => $cardName, - 'target' => $target ? $target : $defaultTarget, + 'target' => $target ?: $loginSettings['defaultTarget'], 'username' => $username, - 'targets' => $targets, - 'defaultTarget' => $defaultTarget + 'targets' => $loginSettings['targets'], + 'defaultTarget' => $loginSettings['defaultTarget'], + 'loginMethod' => $loginSettings['loginMethod'], + 'loginMethods' => $loginSettings['loginMethods'], ] ); } @@ -266,13 +271,32 @@ class LibraryCardsController extends AbstractBase $card = $user->getLibraryCard($id == 'NEW' ? null : $id); if ($card->cat_username !== $username || trim($password)) { // Connect to the ILS and check that the credentials are correct: + $loginMethod = $this->getILSLoginMethod($target); $catalog = $this->getILS(); $patron = $catalog->patronLogin($username, $password); - if (!$patron) { + if ('password' === $loginMethod && !$patron) { $this->flashMessenger() ->addMessage('authentication_error_invalid', 'error'); return false; } + if ('email' === $loginMethod) { + if ($patron) { + $info = $patron; + $info['cardID'] = $id; + $info['cardName'] = $cardName; + $emailAuthenticator = $this->serviceLocator + ->get(\VuFind\Auth\EmailAuthenticator::class); + $emailAuthenticator->sendAuthenticationLink( + $info['email'], + $info, + ['auth_method' => 'Email'], + 'editLibraryCard' + ); + } + // Don't reveal the result + $this->flashMessenger()->addSuccessMessage('email_login_link_sent'); + return $this->redirect()->toRoute('librarycards-home'); + } } try { @@ -286,4 +310,33 @@ class LibraryCardsController extends AbstractBase return $this->redirect()->toRoute('librarycards-home'); } + + /** + * Process library card addition via an email link + * + * @param User $user User object + * @param string $hash Hash + * + * @return \Zend\Http\Response Response object + */ + protected function processEmailLink($user, $hash) + { + $emailAuthenticator = $this->serviceLocator + ->get(\VuFind\Auth\EmailAuthenticator::class); + try { + $info = $emailAuthenticator->authenticate($hash); + $user->saveLibraryCard( + 'NEW' === $info['cardID'] ? null : $info['cardID'], + $info['cardName'], + $info['cat_username'], + ' ' + ); + } catch (\VuFind\Exception\Auth $e) { + $this->flashMessenger()->addErrorMessage($e->getMessage()); + } catch (\VuFind\Exception\LibraryCard $e) { + $this->flashMessenger()->addErrorMessage($e->getMessage()); + } + + return $this->redirect()->toRoute('librarycards-home'); + } } diff --git a/module/VuFind/src/VuFind/Controller/MyResearchController.php b/module/VuFind/src/VuFind/Controller/MyResearchController.php index 937a691131c3d147cf03404cfb59a0e30784cdde..3599d2e6b5a82a7b65c943519b9284ae08954d40 100644 --- a/module/VuFind/src/VuFind/Controller/MyResearchController.php +++ b/module/VuFind/src/VuFind/Controller/MyResearchController.php @@ -28,6 +28,8 @@ namespace VuFind\Controller; use VuFind\Exception\Auth as AuthException; +use VuFind\Exception\AuthEmailNotVerified as AuthEmailNotVerifiedException; +use VuFind\Exception\AuthInProgress as AuthInProgressException; use VuFind\Exception\Forbidden as ForbiddenException; use VuFind\Exception\ILS as ILSException; use VuFind\Exception\ListPermission as ListPermissionException; @@ -49,22 +51,24 @@ use Zend\View\Model\ViewModel; class MyResearchController extends AbstractBase { /** - * ILS Pagination Helper + * Permission that must be granted to access this module (false for no + * restriction, null to use configured default (which is usually the same + * as false)). * - * @var PaginationHelper + * For this controller, we default to false rather than null because + * we don't want a default setting to override the controller's accessibility + * and break the login process! + * + * @var string|bool */ - protected $paginationHelper = null; + protected $accessPermission = false; /** - * Are we currently in a lightbox context? + * ILS Pagination Helper * - * @return bool + * @var PaginationHelper */ - protected function inLightbox() - { - return $this->getRequest()->getQuery('layout', 'no') === 'lightbox' - || 'layout/lightbox' == $this->layout()->getTemplate(); - } + protected $paginationHelper = null; /** * Construct an HTTP 205 (refresh) response. Useful for reporting success @@ -89,6 +93,23 @@ class MyResearchController extends AbstractBase protected function processAuthenticationException(AuthException $e) { $msg = $e->getMessage(); + if ($e instanceof AuthInProgressException) { + $this->flashMessenger()->addSuccessMessage($msg); + return; + } + if ($e instanceof AuthEmailNotVerifiedException) { + $this->sendFirstVerificationEmail($e->user); + if ($msg == 'authentication_error_email_not_verified_html') { + $this->getUserVerificationContainer()->user = $e->user->username; + $url = $this->url()->fromRoute('myresearch-emailnotverified') + . '?reverify=true'; + $msg = [ + 'html' => true, + 'msg' => $msg, + 'tokens' => ['%%url%%' => $url], + ]; + } + } // If a Shibboleth-style login has failed and the user just logged // out, we need to override the error message with a more relevant // one: @@ -242,6 +263,9 @@ class MyResearchController extends AbstractBase try { $this->getAuthManager()->create($this->getRequest()); return $this->forwardTo('MyResearch', 'Home'); + } catch (AuthEmailNotVerifiedException $e) { + $this->sendFirstVerificationEmail($e->user); + return $this->redirect()->toRoute('myresearch-emailnotverified'); } catch (AuthException $e) { $this->flashMessenger()->addMessage($e->getMessage(), 'error'); } @@ -303,7 +327,11 @@ class MyResearchController extends AbstractBase : $this->redirect()->toRoute('home'); } $this->clearFollowupUrl(); - $this->setFollowupUrlToReferer(); + // Set followup only if we're not in lightbox since it has the short-circuit + // for reloading current page: + if (!$this->inLightbox()) { + $this->setFollowupUrlToReferer(); + } if ($si = $this->getSessionInitiator()) { return $this->redirect()->toUrl($si); } @@ -363,16 +391,70 @@ class MyResearchController extends AbstractBase protected function setSavedFlagSecurely($searchId, $saved, $userId) { $searchTable = $this->getTable('Search'); - $sessId = $this->serviceLocator->get('Zend\Session\SessionManager')->getId(); + $sessId = $this->serviceLocator + ->get(\Zend\Session\SessionManager::class)->getId(); $row = $searchTable->getOwnedRowById($searchId, $sessId, $userId); if (empty($row)) { throw new ForbiddenException('Access denied.'); } $row->saved = $saved ? 1 : 0; + if (!$saved) { + $row->notification_frequency = 0; + } $row->user_id = $userId; $row->save(); } + /** + * Return a session container for use in user email verification. + * + * @return \Zend\Session\Container + */ + protected function getUserVerificationContainer() + { + return new \Zend\Session\Container( + 'user_verification', + $this->serviceLocator->get(\Zend\Session\SessionManager::class) + ); + } + + /** + * Support method for savesearchAction() -- schedule a search. + * + * @param \VuFind\Db\Row\User $user Logged-in user object + * @param int $schedule Requested schedule setting + * @param int $sid Search ID to schedule + * + * @return mixed + */ + protected function scheduleSearch($user, $schedule, $sid) + { + // Fail if scheduled searches are disabled. + $scheduleOptions = $this->serviceLocator + ->get(\VuFind\Search\History::class) + ->getScheduleOptions(); + if (!isset($scheduleOptions[$schedule])) { + throw new ForbiddenException('Illegal schedule option: ' . $schedule); + } + $search = $this->getTable('Search'); + $baseurl = rtrim($this->getServerUrl('home'), '/'); + $searchCriteria = ['id' => $sid, 'user_id' => $user->id, 'saved' => 1]; + $savedRow = $search->select($searchCriteria)->current(); + // If we didn't find an already-saved row, let's save and retry: + if (!$savedRow) { + $this->setSavedFlagSecurely($sid, true, $user->id); + $savedRow = $search->select($searchCriteria)->current(); + } + if (!($this->getConfig()->Account->force_first_scheduled_email ?? false)) { + // By default, a first scheduled email will be sent because the database + // last notification date will be initialized to a past date. If we don't + // want that to happen, we need to set it to a more appropriate date: + $savedRow->last_notification_sent = date('Y-m-d H:i:s'); + } + $savedRow->setSchedule($schedule, $baseurl); + return $this->redirect()->toRoute('search-history'); + } + /** * Handle 'save/unsave search' request * @@ -381,7 +463,8 @@ class MyResearchController extends AbstractBase public function savesearchAction() { // Fail if saved searches are disabled. - $check = $this->serviceLocator->get('VuFind\Config\AccountCapabilities'); + $check = $this->serviceLocator + ->get(\VuFind\Config\AccountCapabilities::class); if ($check->getSavedSearchSetting() === 'disabled') { throw new ForbiddenException('Saved searches disabled.'); } @@ -391,6 +474,13 @@ class MyResearchController extends AbstractBase return $this->forceLogin(); } + // Check for schedule-related parameters and process them first: + $schedule = $this->params()->fromQuery('schedule', false); + $sid = $this->params()->fromQuery('searchid', false); + if ($schedule !== false && $sid !== false) { + return $this->scheduleSearch($user, $schedule, $sid); + } + // Check for the save / delete parameters and process them appropriately: if (($id = $this->params()->fromQuery('save', false)) !== false) { $this->setSavedFlagSecurely($id, true, $user->id); @@ -427,11 +517,14 @@ class MyResearchController extends AbstractBase // Begin building view object: $view = $this->createViewModel(['user' => $user]); + $config = $this->getConfig(); + $allowHomeLibrary = $config->Account->set_home_library ?? true; + $patron = $this->catalogLogin(); if (is_array($patron)) { - // Process home library parameter (if present): + // Process home library parameter (if present and allowed): $homeLibrary = $this->params()->fromPost('home_library', false); - if (!empty($homeLibrary)) { + if ($allowHomeLibrary && !empty($homeLibrary)) { $user->changeHomeLibrary($homeLibrary); $this->getAuthManager()->updateSession($user); $this->flashMessenger()->addMessage('profile_update', 'success'); @@ -441,21 +534,36 @@ class MyResearchController extends AbstractBase $catalog = $this->getILS(); $this->addAccountBlocksToFlashMessenger($catalog, $patron); $profile = $catalog->getMyProfile($patron); - $profile['home_library'] = $user->home_library; + $profile['home_library'] = $allowHomeLibrary + ? $user->home_library + : ($profile['home_library'] ?? ''); $view->profile = $profile; + $pickup = $defaultPickupLocation = null; try { - $view->pickup = $catalog->getPickUpLocations($patron); - $view->defaultPickupLocation - = $catalog->getDefaultPickUpLocation($patron); + $pickup = $catalog->getPickUpLocations($patron); + $defaultPickupLocation = $catalog->getDefaultPickUpLocation($patron); } catch (\Exception $e) { // Do nothing; if we're unable to load information about pickup // locations, they are not supported and we should ignore them. } + + // Set things up differently depending on whether or not the user is + // allowed to set a home library. + if ($allowHomeLibrary) { + $view->pickup = $pickup; + $view->defaultPickupLocation = $defaultPickupLocation; + } elseif (!empty($pickup)) { + foreach ($pickup as $lib) { + if ($defaultPickupLocation == $lib['locationID']) { + $view->preferredLibraryDisplay = $lib['locationDisplay']; + break; + } + } + } } else { $view->patronLoginView = $patron; } - $config = $this->getConfig(); $view->accountDeletion = !empty($config->Authentication->account_deletion); @@ -492,13 +600,8 @@ class MyResearchController extends AbstractBase */ public function catalogloginAction() { - // Connect to the ILS and check if multiple target support is available: - $targets = null; - $catalog = $this->getILS(); - if ($catalog->checkCapability('getLoginDrivers')) { - $targets = $catalog->getLoginDrivers(); - } - return $this->createViewModel(['targets' => $targets]); + $loginSettings = $this->getILSLoginSettings(); + return $this->createViewModel($loginSettings); } /** @@ -624,9 +727,9 @@ class MyResearchController extends AbstractBase protected function processEditSubmit($user, $driver, $listID) { $lists = $this->params()->fromPost('lists', []); - $tagParser = $this->serviceLocator->get('VuFind\Tags'); + $tagParser = $this->serviceLocator->get(\VuFind\Tags::class); $favorites = $this->serviceLocator - ->get('VuFind\Favorites\FavoritesService'); + ->get(\VuFind\Favorites\FavoritesService::class); $didSomething = false; foreach ($lists as $list) { $tags = $this->params()->fromPost('tags' . $list); @@ -786,7 +889,7 @@ class MyResearchController extends AbstractBase // If we got this far, we just need to display the favorites: try { - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); + $runner = $this->serviceLocator->get(\VuFind\Search\SearchRunner::class); // We want to merge together GET, POST and route parameters to // initialize our search object: @@ -796,7 +899,7 @@ class MyResearchController extends AbstractBase // Set up listener for recommendations: $rManager = $this->serviceLocator - ->get('VuFind\Recommend\PluginManager'); + ->get(\VuFind\Recommend\PluginManager::class); $setupCallback = function ($runner, $params, $searchId) use ($rManager) { $listener = new RecommendListener($rManager, $searchId); $listener->setConfig( @@ -920,6 +1023,33 @@ class MyResearchController extends AbstractBase return $this->createViewModel(['list' => $list, 'newList' => $newList]); } + /** + * Creates a message that the verification email has been sent to the user's + * mail address. + * + * @return mixed + */ + public function emailNotVerifiedAction() + { + if ($this->params()->fromQuery('reverify')) { + $table = $this->getTable('User'); + // Case 1: new user: + $user = $table + ->getByUsername($this->getUserVerificationContainer()->user, false); + // Case 2: pending email change: + if (!$user) { + $user = $this->getUser(); + if (!empty($user->pending_email)) { + $change = true; + } + } + $this->sendVerificationEmail($user, $change ?? false); + } else { + $this->flashMessenger()->addMessage('verification_email_sent', 'info'); + } + return $this->createViewModel(); + } + /** * Creates a confirmation box to delete or not delete the current list * @@ -986,7 +1116,7 @@ class MyResearchController extends AbstractBase { $id = $current['id'] ?? ''; $source = $current['source'] ?? DEFAULT_SEARCH_BACKEND; - $record = $this->serviceLocator->get('VuFind\Record\Loader') + $record = $this->serviceLocator->get(\VuFind\Record\Loader::class) ->load($id, $source, true); $record->setExtraDetail('ils_details', $current); return $record; @@ -1359,7 +1489,8 @@ class MyResearchController extends AbstractBase if (strlen($row['id'] ?? '') > 0) { $source = $row['source'] ?? DEFAULT_SEARCH_BACKEND; $row['driver'] = $this->serviceLocator - ->get('VuFind\Record\Loader')->load($row['id'], $source); + ->get(\VuFind\Record\Loader::class) + ->load($row['id'], $source); if (empty($row['title'])) { $row['title'] = $row['driver']->getShortTitle(); } @@ -1443,7 +1574,7 @@ class MyResearchController extends AbstractBase if (null == $user) { $this->flashMessenger()->addMessage('recovery_user_not_found', 'error'); } else { - // Make sure we've waiting long enough + // Make sure we've waited long enough $hashtime = $this->getHashAge($user->verify_hash); $recoveryInterval = isset($config->Authentication->recover_interval) ? $config->Authentication->recover_interval @@ -1468,7 +1599,7 @@ class MyResearchController extends AbstractBase . $user->verify_hash . '&auth_method=' . $method ] ); - $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( + $this->serviceLocator->get(\VuFind\Mailer\Mailer::class)->send( $user->email, $config->Site->email, $this->translate('recovery_email_subject'), @@ -1483,6 +1614,119 @@ class MyResearchController extends AbstractBase } } + /** + * Send a verify email message for the first time (only if the user does not + * already have a hash). + * + * @param \VuFind\Db\Row\User $user User object we're recovering + * + * @return void (sends email or adds error message) + */ + protected function sendFirstVerificationEmail($user) + { + if (empty($user->verify_hash)) { + return $this->sendVerificationEmail($user); + } + } + + /** + * When a request to change a user's email address has been received, we should + * send a notification to the old email address for the user's information. + * + * @param \VuFind\Db\Row\User $user User object we're recovering + * + * @return void (sends email or adds error message) + */ + protected function sendChangeNotificationEmail($user) + { + $config = $this->getConfig(); + $renderer = $this->getViewRenderer(); + // Custom template for emails (text-only) + $message = $renderer->render( + 'Email/notify-email-change.phtml', + [ + 'library' => $config->Site->title, + 'url' => $this->getServerUrl('home'), + 'email' => $config->Site->email, + ] + ); + // If the user is setting up a new account, use the main email + // address; if they have a pending address change, use that. + $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( + $user->email, + $config->Site->email, + $this->translate('change_notification_email_subject'), + $message + ); + } + + /** + * Send a verify email message. + * + * @param \VuFind\Db\Row\User $user User object we're recovering + * @param bool $change Is the user changing their email (true) + * or setting up a new account (false). + * + * @return void (sends email or adds error message) + */ + protected function sendVerificationEmail($user, $change = false) + { + // If we can't find a user + if (null == $user) { + $this->flashMessenger() + ->addMessage('verification_user_not_found', 'error'); + } else { + // Make sure we've waited long enough + $hashtime = $this->getHashAge($user->verify_hash); + $recoveryInterval = $this->getConfig()->Authentication->recover_interval + ?? 60; + if (time() - $hashtime < $recoveryInterval && !$change) { + $this->flashMessenger() + ->addMessage('verification_too_soon', 'error'); + } else { + // Attempt to send the email + try { + // Create a fresh hash + $user->updateHash(); + $config = $this->getConfig(); + $renderer = $this->getViewRenderer(); + $method = $this->getAuthManager()->getAuthMethod(); + // Custom template for emails (text-only) + $message = $renderer->render( + 'Email/verify-email.phtml', + [ + 'library' => $config->Site->title, + 'url' => $this->getServerUrl('myresearch-verifyemail') + . '?hash=' + . $user->verify_hash . '&auth_method=' . $method + ] + ); + // If the user is setting up a new account, use the main email + // address; if they have a pending address change, use that. + $to = empty($user->pending_email) + ? $user->email : $user->pending_email; + $this->serviceLocator->get('VuFind\Mailer\Mailer')->send( + $to, + $config->Site->email, + $this->translate('verification_email_subject'), + $message + ); + $flashMessage = $change + ? 'verification_email_change_sent' + : 'verification_email_sent'; + $this->flashMessenger()->addMessage($flashMessage, 'info'); + // If this is an email change, send a notification to the old + // email address as well. + if ($change) { + $this->sendChangeNotificationEmail($user); + } + } catch (MailException $e) { + $this->flashMessenger()->addMessage($e->getMessage(), 'error'); + } + } + } + } + /** * Receive a hash and display the new password form if it's valid * @@ -1506,7 +1750,9 @@ class MyResearchController extends AbstractBase $table = $this->getTable('User'); $user = $table->getByVerifyHash($hash); // If the hash is valid, forward user to create new password + // Also treat email address as verified if (null != $user) { + $user->saveEmailVerified(); $this->setUpAuthenticationFromRequest(); $view = $this->createViewModel(); $view->auth_method @@ -1524,6 +1770,47 @@ class MyResearchController extends AbstractBase return $this->forwardTo('MyResearch', 'Login'); } + /** + * Receive a hash and display the new password form if it's valid + * + * @return view + */ + public function verifyEmailAction() + { + // If we have a submitted form + if ($hash = $this->params()->fromQuery('hash')) { + $hashtime = $this->getHashAge($hash); + $config = $this->getConfig(); + // Check if hash is expired + $hashLifetime = isset($config->Authentication->recover_hash_lifetime) + ? $config->Authentication->recover_hash_lifetime + : 1209600; // Two weeks + if (time() - $hashtime > $hashLifetime) { + $this->flashMessenger() + ->addMessage('recovery_expired_hash', 'error'); + return $this->forwardTo('MyResearch', 'Login'); + } else { + $table = $this->getTable('User'); + $user = $table->getByVerifyHash($hash); + // If the hash is valid, store validation in DB and forward to login + if (null != $user) { + // Apply pending email address change, if applicable: + if (!empty($user->pending_email)) { + $user->updateEmail($user->pending_email, true); + $user->pending_email = ''; + } + $user->saveEmailVerified(); + $this->setUpAuthenticationFromRequest(); + + $this->flashMessenger()->addMessage('verification_done', 'info'); + return $this->forwardTo('MyResearch', 'Login'); + } + } + } + $this->flashMessenger()->addMessage('recovery_invalid_hash', 'error'); + return $this->forwardTo('MyResearch', 'Login'); + } + /** * Reset the new password form and return the modified view. When a user has * already been loaded from an existing hash, this resets the hash and updates @@ -1612,11 +1899,84 @@ class MyResearchController extends AbstractBase $user->updateHash(); // Login $this->getAuthManager()->login($this->request); - // Go to favorites + // Return to account home $this->flashMessenger()->addMessage('new_password_success', 'success'); return $this->redirect()->toRoute('myresearch-home'); } + /** + * Handling submission of a new email for a user. + * + * @return view + */ + public function changeEmailAction() + { + // Always check that we are logged in and function is enabled first: + if (!$this->getAuthManager()->isLoggedIn()) { + return $this->forceLogin(); + } + if (!$this->getAuthManager()->supportsEmailChange()) { + $this->flashMessenger()->addMessage('change_email_disabled', 'error'); + return $this->redirect()->toRoute('home'); + } + $view = $this->createViewModel($this->params()->fromPost()); + // Display email + $user = $this->getUser(); + $view->email = $user->email; + // Identification + $view->useRecaptcha = $this->recaptcha()->active('changeEmail'); + // Special case: form was submitted: + if ($this->formWasSubmitted('submit', $view->useRecaptcha)) { + // Do CSRF check + $csrf = $this->serviceLocator->get(\VuFind\Validator\Csrf::class); + if (!$csrf->isValid($this->getRequest()->getPost()->get('csrf'))) { + throw new \VuFind\Exception\BadRequest( + 'error_inconsistent_parameters' + ); + } + // Update email + $validator = new \Zend\Validator\EmailAddress(); + $email = $this->params()->fromPost('email', ''); + try { + if (!$validator->isValid($email)) { + throw new AuthException('Email address is invalid'); + } + $this->getAuthManager()->updateEmail($user, $email); + // If we have a pending change, we need to send a verification email: + if (!empty($user->pending_email)) { + $this->sendVerificationEmail($user, true); + } else { + $this->flashMessenger() + ->addMessage('new_email_success', 'success'); + } + } catch (AuthException $e) { + $this->flashMessenger()->addMessage($e->getMessage(), 'error'); + return $view; + } + // Return to account home + return $this->redirect()->toRoute('myresearch-home'); + } elseif ($this->getConfig()->Authentication->verify_email ?? false) { + $this->flashMessenger() + ->addMessage('change_email_verification_reminder', 'info'); + } + if (!empty($user->pending_email)) { + $url = $this->url()->fromRoute('myresearch-emailnotverified') + . '?reverify=true'; + $this->flashMessenger()->addMessage( + [ + 'html' => true, + 'msg' => 'email_change_pending_html', + 'tokens' => [ + '%%pending%%' => $user->pending_email, + '%%url%%' => $url, + ], + ], + 'info' + ); + } + return $view; + } + /** * Handling submission of a new password for a user. * @@ -1697,7 +2057,7 @@ class MyResearchController extends AbstractBase $view = $this->createViewModel(['accountDeleted' => false]); if ($this->formWasSubmitted('submit')) { - $csrf = $this->serviceLocator->get('VuFind\Validator\Csrf'); + $csrf = $this->serviceLocator->get(\VuFind\Validator\Csrf::class); if (!$csrf->isValid($this->getRequest()->getPost()->get('csrf'))) { throw new \VuFind\Exception\BadRequest( 'error_inconsistent_parameters' @@ -1719,6 +2079,44 @@ class MyResearchController extends AbstractBase return $view; } + /** + * Unsubscribe a scheduled alert for a saved search. + * + * @return mixed + */ + public function unsubscribeAction() + { + $id = $this->params()->fromQuery('id', false); + $key = $this->params()->fromQuery('key', false); + $type = $this->params()->fromQuery('type', 'alert'); + if ($id === false || $key === false) { + throw new \Exception('Missing parameters.'); + } + $view = $this->createViewModel(); + if ($this->params()->fromQuery('confirm', false) == 1) { + if ($type == 'alert') { + $search + = $this->getTable('Search')->select(['id' => $id])->current(); + if (!$search) { + throw new \Exception('Invalid parameters.'); + } + $user = $this->getTable('User')->getById($search->user_id); + $secret = $search->getUnsubscribeSecret( + $this->serviceLocator->get(\VuFind\Crypt\HMAC::class), $user + ); + if ($key !== $secret) { + throw new \Exception('Invalid parameters.'); + } + $search->setSchedule(0); + $view->success = true; + } + } else { + $view->unsubscribeUrl + = $this->getRequest()->getRequestUri() . '&confirm=1'; + } + return $view; + } + /** * Get the ILS pagination helper * diff --git a/module/VuFind/src/VuFind/Controller/OaiController.php b/module/VuFind/src/VuFind/Controller/OaiController.php index 5e53f01b7e08a0e6fd41088a321992d092d6b71d..50d1873fb0e41eee4328f43b5792927baa11283c 100644 --- a/module/VuFind/src/VuFind/Controller/OaiController.php +++ b/module/VuFind/src/VuFind/Controller/OaiController.php @@ -27,6 +27,8 @@ */ namespace VuFind\Controller; +use VuFindApi\Formatter\RecordFormatter; + /** * OAIController Class * @@ -99,17 +101,13 @@ class OaiController extends AbstractBase $this->getRequest()->getQuery()->toArray(), $this->getRequest()->getPost()->toArray() ); - $server = new $serverClass( - $this->serviceLocator->get('VuFind\Search\Results\PluginManager'), - $this->serviceLocator->get('VuFind\Record\Loader'), - $this->serviceLocator->get('VuFind\Db\Table\PluginManager'), - $config, $baseURL, $params - ); + $server = $this->serviceLocator->get($serverClass); + $server->init($config, $baseURL, $params); $server->setRecordLinkHelper( $this->getViewRenderer()->plugin('recordLink') ); $server->setRecordFormatter( - $this->serviceLocator->get('VuFindApi\Formatter\RecordFormatter') + $this->serviceLocator->get(RecordFormatter::class) ); $xml = $server->getResponse(); } catch (\Exception $e) { diff --git a/module/VuFind/src/VuFind/Controller/OverdriveController.php b/module/VuFind/src/VuFind/Controller/OverdriveController.php new file mode 100644 index 0000000000000000000000000000000000000000..edeb4fba9907919c67d01abd3acc788e6ea6d645 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/OverdriveController.php @@ -0,0 +1,303 @@ +<?php +/** + * Overdrive Controller + * + * PHP version 7 + * + * @category VuFind + * @package Controller + * @author Brent Palmer <brent-palmer@ipcl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Controller; + +use VuFind\DigitalContent\OverdriveConnector; +use Zend\Log\LoggerAwareInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Overdrive Controller supports actions for Overdrive Integration + * + * @category VuFind + * @package Controller + * @author Brent Palmer <brent-palmer@ipcl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +class OverdriveController extends AbstractBase implements LoggerAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait { + logError as error; + } + + /** + * Overdrive Connector + * + * @var OverdriveConnector $connector Overdrive Connector + */ + protected $connector; + + /** + * Constructor + * + * @param ServiceLocatorInterface $sm Service locator + */ + public function __construct(ServiceLocatorInterface $sm) + { + $this->setLogger($sm->get('VuFind\Logger')); + $this->connector = $sm->get('VuFind\DigitalContent\OverdriveConnector'); + parent::__construct($sm); + $this->debug("ODRC constructed"); + } + + /** + * My Content Action + * Prepares the view for the Overdrive MyContent template. + * + * @return array|bool|\Zend\View\Model\ViewModel + */ + public function mycontentAction() + { + $this->debug("ODC mycontent action"); + //force login + if (!is_array($patron = $this->catalogLogin())) { + return $patron; + } + $holds = []; + $checkouts = []; + $checkoutsUnavailable = false; + $holdsUnavailable = false; + + //check on this patrons's access to Overdrive + $odAccessResult = $this->connector->getAccess(); + + if (!$odAccessResult->status) { + $this->debug("result:" . print_r($odAccessResult, true)); + $this->flashMessenger()->addErrorMessage( + $this->translate( + $odAccessResult->code, + ["%%message%%" => $odAccessResult->msg] + ) + ); + $checkoutsUnavailable = true; + $holdsUnavailable = true; + } + + if ($odAccessResult->status) { + //get the current Overdrive checkouts + //for this user and add to our array of IDS + $checkoutResults = $this->connector->getCheckouts(true); + if (!$checkoutResults->status) { + $this->flashMessenger()->addMessage( + $checkoutResults->code, 'error' + ); + $checkoutsUnavailable = true; + } else { + foreach ($checkoutResults->data as $checkout) { + $mycheckout['checkout'] = $checkout; + $mycheckout['record'] + = $this->serviceLocator->get('VuFind\Record\Loader') + ->load(strtolower($checkout->reserveId)); + $checkouts[] = $mycheckout; + } + } + //get the current Overdrive holds for this user and add to + // our array of IDS + $holdsResults = $this->connector->getHolds(true); + if (!$holdsResults->status) { + if ($checkoutResults->status) { + $this->flashMessenger()->addMessage( + $holdsResults->code, 'error' + ); + } + $holdsUnavailable = true; + } else { + foreach ($holdsResults->data as $hold) { + $myhold['hold'] = $hold; + $myhold['record'] + = $this->serviceLocator->get('VuFind\Record\Loader') + ->load(strtolower($hold->reserveId)); + $holds[] = $myhold; + } + } + } + //Future: get reading history will be here + //Future: get hold and checkoutlimit using the Patron Info API + + $view = $this->createViewModel( + compact( + 'checkoutsUnavailable', 'holdsUnavailable', + 'checkouts', 'holds' + ) + ); + + $view->setTemplate('myresearch/odmycontent'); + return $view; + } + + /** + * Get Status Action + * Supports the ajax getStatus calls + * + * @return array|bool|\Zend\View\Model\ViewModel + */ + public function getStatusAction() + { + $this->debug("ODC getStatus action"); + $ids = $this->params()->fromPost( + 'id', $this->params()->fromQuery('id', []) + ); + $this->debug("ODRC availability for :" . print_r($ids, true)); + $result = $this->connector->getAvailabilityBulk($ids); + $view = $this->createViewModel(compact('ids', 'result')); + $view->setTemplate('RecordDriver/SolrOverdrive/status-full'); + $this->layout()->setTemplate('layout/lightbox'); + return $view; + } + + /** + * Hold Action + * + * Hold Action handles all of the actions involving + * Overdrive content including checkout, hold, cancel hold etc. + * + * @return array|bool|\Zend\View\Model\ViewModel + * @todo Deal with situation that an unlogged in user requests + * an action but the action is no longer valid since they + * already have the content on hold/checked out or do not have acceess + */ + public function holdAction() + { + $this->debug("ODC Hold action"); + + if (!is_array($patron = $this->catalogLogin())) { + return $patron; + } + $this->debug("patron: " . print_r($patron, true)); + + $od_id = $this->params()->fromQuery('od_id'); + $rec_id = $this->params()->fromQuery('rec_id'); + $action = $this->params()->fromQuery('action'); + + //place hold action comes in through the form + if (null !== $this->params()->fromPost('doAction')) { + $action = $this->params()->fromPost('doAction'); + } + + //place hold action comes in through the form + if (null !== $this->params()->fromPost('getTitleFormat')) { + $format = $this->params()->fromPost('getTitleFormat'); + } + + $format = $this->params()->fromQuery('getTitleFormat'); + + $this->debug("ODRC od_id=$od_id rec_id=$rec_id action=$action"); + //load the Record Driver. Should be a SolrOverdrive driver. + $driver = $this->serviceLocator->get('VuFind\Record\Loader')->load( + $rec_id + ); + + $formats = $driver->getDigitalFormats(); + $title = $driver->getTitle(); + $cover = $driver->getThumbnail('small'); + $listAuthors = $driver->getPrimaryAuthors(); + if (!$action) { + //double check the availability in case it + //has changed since the page was loaded. + $avail = $driver->getOverdriveAvailability(); + if ($avail->copiesAvailable > 0) { + $action = "checkoutConfirm"; + } else { + $action = "holdConfirm"; + } + } + + if ($action == "checkoutConfirm") { + $result = $this->connector->getResultObject(); + //check to make sure they don't already have this checked out + //shouldn't need to refresh. + if ($checkout = $this->connector->getCheckout($od_id, false)) { + $result->status = false; + $result->data->checkout = $checkout; + $result->code = "OD_CODE_ALREADY_CHECKED_OUT"; + } elseif ($hold = $this->connector->getHold($od_id, false)) { + $result->status = false; + $result->data->hold = $hold; + $result->code = "OD_CODE_ALREADY_ON_HOLD"; + } else { + $result->status = true; + } + $actionTitleCode = "od_checkout"; + } elseif ($action == "holdConfirm") { + $result = $this->connector->getResultObject(); + //check to make sure they don't already have this checked out + //check to make sure they don't already have this checked out + //shouldn't need to refresh. + if ($checkout = $this->connector->getCheckout($od_id, false)) { + $result->status = false; + $result->data->checkout = $checkout; + $result->code = "OD_CODE_ALREADY_CHECKED_OUT"; + $this->debug("title already checked out: $od_id"); + } elseif ($hold = $this->connector->getHold($od_id, false)) { + $result->status = false; + $result->data->hold = $hold; + $result->code = "OD_CODE_ALREADY_ON_HOLD"; + $this->debug("title already on hold: $od_id"); + } else { + $result->status = true; + } + $actionTitleCode = "od_hold"; + } elseif ($action == "cancelHoldConfirm") { + $actionTitleCode = "od_cancel_hold"; + } elseif ($action == "returnTitleConfirm") { + $actionTitleCode = "od_early_return"; + } elseif ($action == "getTitleConfirm") { + //get only formats that are available... + $formats = $driver->getAvailableDigitalFormats(); + $actionTitleCode = "od_get_title"; + } elseif ($action == "doCheckout") { + $actionTitleCode = "od_checkout"; + $result = $this->connector->doOverdriveCheckout($od_id); + } elseif ($action == "placeHold") { + $actionTitleCode = "od_hold"; + $email = $this->params()->fromPost('email'); + $result = $this->connector->placeOverDriveHold($od_id, $email); + } elseif ($action == "cancelHold") { + $actionTitleCode = "od_cancel_hold"; + $result = $this->connector->cancelHold($od_id); + } elseif ($action == "returnTitle") { + $actionTitleCode = "od_early_return"; + $result = $this->connector->returnResource($od_id); + } elseif ($action == "getTitle") { + $actionTitleCode = "od_get_title"; + //need to get server name etc. maybe this: getServerUrl(); + $this->debug( + "Get Title action. Getting downloadlink using" . + $this->getServerUrl('overdrive-hold') + ); + $result = $this->connector->getDownloadLink( + $od_id, $format, $this->getServerUrl('overdrive-hold') + ); + if ($result->status) { + //Redirect to resource + $url = $result->data->downloadLink; + $this->debug("redirecting to: $url"); + return $this->redirect()->toUrl($url); + } + } else { + $this->logWarning("overdrive action not defined: $action"); + } + + $view = $this->createViewModel( + compact( + 'od_id', 'rec_id', 'action', + 'result', 'formats', 'cover', 'title', 'actionTitleCode', + 'listAuthors' + ) + ); + + $view->setTemplate('RecordDriver/SolrOverdrive/hold'); + return $view; + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBaseFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBaseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3d17d0e844b1e78475493db94a53292d6f8d8881 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/AbstractRequestBaseFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for controller plugins extending AbstractRequestBase. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for controller plugins extending AbstractRequestBase. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class AbstractRequestBaseFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Crypt\HMAC::class), + $container->get(\Zend\Session\SessionManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php index 1be1b4663c8bb57982ad48bddce2a6271316f748..34610c9991144756dbac2d62e781044bc710b945 100644 --- a/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php +++ b/module/VuFind/src/VuFind/Controller/Plugin/DbUpgrade.php @@ -627,6 +627,26 @@ class DbUpgrade extends AbstractPlugin return $missing; } + /** + * Normalize constraint values. + * + * @param array $constraints Constraints to normalize + * + * @return array + */ + protected function normalizeConstraints($constraints) + { + foreach (['deleteRule', 'updateRule'] as $key) { + // NO ACTION and RESTRICT are equivalent in MySQL, but different + // versions return different values. Here we normalize them to RESTRICT + // for simplicity/consistency. + if ($constraints[$key] == 'NO ACTION') { + $constraints[$key] = 'RESTRICT'; + } + } + return $constraints; + } + /** * Compare expected vs. actual constraint actions and return an array of SQL * clauses required to create the modified constraints. @@ -646,7 +666,7 @@ class DbUpgrade extends AbstractPlugin "Could not find constraint '$name' in actual constraints" ); } - $actualConstr = $actual[$type][$name]; + $actualConstr = $this->normalizeConstraints($actual[$type][$name]); if ($constraint['deleteRule'] !== $actualConstr['deleteRule'] || $constraint['updateRule'] !== $actualConstr['updateRule'] ) { @@ -791,7 +811,9 @@ class DbUpgrade extends AbstractPlugin // If it's not a blob or a text (which don't have explicit sizes in our SQL), // we should see what the character length is, if any: - if ($type != 'blob' && $type != 'text' && $type != 'longtext') { + if ($type != 'blob' && $type != 'text' && $type !== 'mediumtext' + && $type != 'longtext' + ) { $charLen = $column->getCharacterMaximumLength(); if ($charLen) { $type .= '(' . $charLen . ')'; diff --git a/module/VuFind/src/VuFind/Controller/Plugin/Factory.php b/module/VuFind/src/VuFind/Controller/Plugin/Factory.php deleted file mode 100644 index 247a8b5217612b0278d09d1918237ede6630c700..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Controller/Plugin/Factory.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php -/** - * Factory for controller plugins. - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Controller\Plugin; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for controller plugins. - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the Favorites plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return \Zend\Mvc\Controller\Plugin\Favorites - */ - public static function getFavorites(ServiceManager $sm) - { - return new Favorites( - $sm->get('VuFind\Record\Loader'), - $sm->get('VuFind\Record\Cache'), - $sm->get('VuFind\Tags') - ); - } - - /** - * Construct the FlashMessenger plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return \Zend\Mvc\Plugin\FlashMessenger\FlashMessenger - */ - public static function getFlashMessenger(ServiceManager $sm) - { - $plugin = new \Zend\Mvc\Plugin\FlashMessenger\FlashMessenger(); - $sessionManager = $sm->get('Zend\Session\SessionManager'); - $plugin->setSessionManager($sessionManager); - return $plugin; - } - - /** - * Construct the Followup plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Followup - */ - public static function getFollowup(ServiceManager $sm) - { - return new Followup( - new \Zend\Session\Container( - 'Followup', $sm->get('Zend\Session\SessionManager') - ) - ); - } - - /** - * Construct the Holds plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Holds - */ - public static function getHolds(ServiceManager $sm) - { - return new Holds( - $sm->get('VuFind\Crypt\HMAC'), - $sm->get('Zend\Session\SessionManager') - ); - } - - /** - * Construct the ILLRequests plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return ILLRequests - */ - public static function getILLRequests(ServiceManager $sm) - { - return new ILLRequests( - $sm->get('VuFind\Crypt\HMAC'), - $sm->get('Zend\Session\SessionManager') - ); - } - - /** - * Construct the NewItems plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return NewItems - */ - public static function getNewItems(ServiceManager $sm) - { - $search = $sm->get('VuFind\Config\PluginManager')->get('searches'); - $config = isset($search->NewItem) - ? $search->NewItem : new \Zend\Config\Config([]); - return new NewItems($config); - } - - /** - * Construct the Permission plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Permission - */ - public static function getPermission(ServiceManager $sm) - { - $pdm = $sm->get('VuFind\Role\PermissionDeniedManager'); - $pm = $sm->get('VuFind\Role\PermissionManager'); - $auth = $sm->get('VuFind\Auth\Manager'); - return new Permission($pm, $pdm, $auth); - } - - /** - * Construct the Recaptcha plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Recaptcha - */ - public static function getRecaptcha(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - return new Recaptcha($sm->get('VuFind\Service\ReCaptcha'), $config); - } - - /** - * Construct the Reserves plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Reserves - */ - public static function getReserves(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $useIndex = isset($config->Reserves->search_enabled) - && $config->Reserves->search_enabled; - $ss = $useIndex ? $sm->get('VuFindSearch\Service') : null; - return new Reserves($useIndex, $ss); - } - - /** - * Construct the ResultScroller plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return ResultScroller - */ - public static function getResultScroller(ServiceManager $sm) - { - return new ResultScroller( - new \Zend\Session\Container( - 'ResultScroller', - $sm->get('Zend\Session\SessionManager') - ), - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Construct the StorageRetrievalRequests plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return StorageRetrievalRequests - */ - public static function getStorageRetrievalRequests(ServiceManager $sm) - { - return new StorageRetrievalRequests( - $sm->get('VuFind\Crypt\HMAC'), - $sm->get('Zend\Session\SessionManager') - ); - } -} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/FavoritesFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/FavoritesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7152a3371825d0e8f35bc4a914935a05d04e0cca --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/FavoritesFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for Favorites controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Favorites controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class FavoritesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Record\Cache::class), + $container->get(\VuFind\Tags::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/FlashMessengerFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/FlashMessengerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8d2a2d7a8c5b5c87501d1e432642098b52d397e5 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/FlashMessengerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for FlashMessenger controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for FlashMessenger controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class FlashMessengerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $plugin = new $requestedName(); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); + $plugin->setSessionManager($sessionManager); + return $plugin; + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/FollowupFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/FollowupFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ebb43f525a36f6df1df6a9e3d1a9311f62226650 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/FollowupFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for Followup controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Followup controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class FollowupFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + new \Zend\Session\Container( + 'Followup', $container->get(\Zend\Session\SessionManager::class) + ) + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/NewItemsFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/NewItemsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b99ba6afba35db8f2e623540354ea429c3b49b2f --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/NewItemsFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for NewItems controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for NewItems controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class NewItemsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $search + = $container->get(\VuFind\Config\PluginManager::class)->get('searches'); + $config = $search->NewItem ?? new \Zend\Config\Config([]); + return new $requestedName($config); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/PermissionFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/PermissionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..262376a8386dec43e675d35b261f01a867e96a6a --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/PermissionFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for Permission controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Permission controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class PermissionFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $pdm = $container->get(\VuFind\Role\PermissionDeniedManager::class); + $pm = $container->get(\VuFind\Role\PermissionManager::class); + $auth = $container->get(\VuFind\Auth\Manager::class); + return new $requestedName($pm, $pdm, $auth); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/RecaptchaFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/RecaptchaFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..aed8036767838b0c1a5beb0da6818ec6b61d5d25 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/RecaptchaFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for Recaptcha controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Recaptcha controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class RecaptchaFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Service\ReCaptcha::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/ReservesFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/ReservesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..abdd41daf8aec5f5552a6aed6f02a75b701382b7 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/ReservesFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for Reserves controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Reserves controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class ReservesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config + = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + $useIndex = $config->Reserves->search_enabled ?? false; + $ss = $useIndex ? $container->get(\VuFindSearch\Service::class) : null; + return new $requestedName($useIndex, $ss); + } +} diff --git a/module/VuFind/src/VuFind/Controller/Plugin/ResultScrollerFactory.php b/module/VuFind/src/VuFind/Controller/Plugin/ResultScrollerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..417d4bfc8ae7b4e90b846300c41db7be59ef2ae9 --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Plugin/ResultScrollerFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for ResultScroller controller plugin. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Controller\Plugin; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for ResultScroller controller plugin. + * + * @category VuFind + * @package Controller_Plugins + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class ResultScrollerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + new \Zend\Session\Container( + 'ResultScroller', + $container->get(\Zend\Session\SessionManager::class) + ), + $container->get(\VuFind\Search\Results\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/PrimoController.php b/module/VuFind/src/VuFind/Controller/PrimoController.php index f511f36cdf6e58d47758002951769d58245650eb..af967111e147fcfe6c514534c3fcaf844055ffdc 100644 --- a/module/VuFind/src/VuFind/Controller/PrimoController.php +++ b/module/VuFind/src/VuFind/Controller/PrimoController.php @@ -59,7 +59,7 @@ class PrimoController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('Primo'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/PrimorecordController.php b/module/VuFind/src/VuFind/Controller/PrimorecordController.php index c34d535f9aef5564836f6bd5263863efe003019f..b4ed476697ee37d0c0b955e669734070f01c46ab 100644 --- a/module/VuFind/src/VuFind/Controller/PrimorecordController.php +++ b/module/VuFind/src/VuFind/Controller/PrimorecordController.php @@ -63,7 +63,7 @@ class PrimorecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('Primo'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php b/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php index dc3051698f5fd6defc126c1a7e0cd4ff759b98e2..16a196dd64207fb66817018952a5111d6fa6560a 100644 --- a/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/QRCodeControllerFactory.php @@ -62,8 +62,8 @@ class QRCodeControllerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\QRCode\Loader'), - $container->get('VuFind\Session\Settings') + $container->get(\VuFind\QRCode\Loader::class), + $container->get(\VuFind\Session\Settings::class) ); } } diff --git a/module/VuFind/src/VuFind/Controller/RecordController.php b/module/VuFind/src/VuFind/Controller/RecordController.php index a44027b1dd25418d60ba121bddf08ba49670c98e..f7c7ffa5a08433aebab928fef0c55423431936f6 100644 --- a/module/VuFind/src/VuFind/Controller/RecordController.php +++ b/module/VuFind/src/VuFind/Controller/RecordController.php @@ -68,7 +68,7 @@ class RecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/Search2Controller.php b/module/VuFind/src/VuFind/Controller/Search2Controller.php index c7b3907ac76dbd7785069eb6cacb98b2d969e05f..00a3d40935d085f1ea0b6870369cd27ff70a900d 100644 --- a/module/VuFind/src/VuFind/Controller/Search2Controller.php +++ b/module/VuFind/src/VuFind/Controller/Search2Controller.php @@ -59,7 +59,7 @@ class Search2Controller extends AbstractSolrSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config') + $config = $this->serviceLocator->get(\VuFind\Config::class) ->get('Search2'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/Search2collectionController.php b/module/VuFind/src/VuFind/Controller/Search2collectionController.php new file mode 100644 index 0000000000000000000000000000000000000000..506bcdfbcb7afe83065f3f37482030f62432413f --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/Search2collectionController.php @@ -0,0 +1,42 @@ +<?php +/** + * Collection Controller + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Controller; + +/** + * Collection Controller + * + * @category VuFind + * @package Controller + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Search2collectionController extends CollectionController +{ + protected $searchClassId = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Controller/Search2recordController.php b/module/VuFind/src/VuFind/Controller/Search2recordController.php index 479ead7f1edd855059e74b7b9ec93e1b2c840e0b..d6198b5f55f97c0852dafc9790c7ed97d8f1d8b3 100644 --- a/module/VuFind/src/VuFind/Controller/Search2recordController.php +++ b/module/VuFind/src/VuFind/Controller/Search2recordController.php @@ -60,7 +60,7 @@ class Search2recordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config') + $config = $this->serviceLocator->get(\VuFind\Config::class) ->get('Search2'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/SearchController.php b/module/VuFind/src/VuFind/Controller/SearchController.php index 1e81635d7a89eb57cb03e44b0ae8a27d4bb3e75c..b3cf701f282f41027a5a455bc81c3b66c573f92b 100644 --- a/module/VuFind/src/VuFind/Controller/SearchController.php +++ b/module/VuFind/src/VuFind/Controller/SearchController.php @@ -28,6 +28,7 @@ namespace VuFind\Controller; use VuFind\Exception\Mail as MailException; +use VuFind\Search\Factory\UrlQueryHelperFactory; /** * Redirects the user to the appropriate default VuFind action. @@ -51,6 +52,59 @@ class SearchController extends AbstractSolrSearch return $this->facetListAction(); } + /** + * Edit search memory action. + * + * @return mixed + */ + public function editmemoryAction() + { + // Get the user's referer, with the home page as a fallback; we'll + // redirect here after the work is done. + $from = $this->getRequest()->getServer()->get('HTTP_REFERER') + ?? $this->url()->fromRoute('home'); + + // Get parameters: + $searchClassId = $this->params() + ->fromQuery('searchClassId', DEFAULT_SEARCH_BACKEND); + $removeAllFilters = $this->params()->fromQuery('removeAllFilters'); + $removeFacet = $this->params()->fromQuery('removeFacet'); + $removeFilter = $this->params()->fromQuery('removeFilter'); + + // Retrieve and manipulate the parameters: + $searchHelper = $this->getViewRenderer()->plugin('searchMemory'); + $params = $searchHelper->getLastSearchParams($searchClassId); + $factory = $this->serviceLocator->get(UrlQueryHelperFactory::class); + $initialParams = $factory->fromParams($params); + + if ($removeAllFilters) { + $defaultFilters = $params->getOptions()->getDefaultFilters(); + $query = $initialParams->removeAllFilters(); + foreach ($defaultFilters as $filter) { + $query = $query->addFilter($filter); + } + } elseif ($removeFacet) { + $defaults = ['operator' => 'AND', 'field' => '', 'value' => '']; + extract($removeFacet + $defaults); + $query = $initialParams->removeFacet($field, $value, $operator); + } elseif ($removeFilter) { + $query = $initialParams->removeFilter($removeFilter); + } else { + $query = null; + } + + // Remember the altered parameters: + if ($query) { + $base = $this->url() + ->fromRoute($params->getOptions()->getSearchAction()); + $this->getSearchMemory() + ->rememberSearch($base . $query->getParams(false)); + } + + // Send the user back where they came from: + return $this->redirect()->toUrl($from); + } + /** * Email action - Allows the email form to appear. * @@ -60,7 +114,7 @@ class SearchController extends AbstractSolrSearch { // If a URL was explicitly passed in, use that; otherwise, try to // find the HTTP referrer. - $mailer = $this->serviceLocator->get('VuFind\Mailer\Mailer'); + $mailer = $this->serviceLocator->get(\VuFind\Mailer\Mailer::class); $view = $this->createEmailViewModel(null, $mailer->getDefaultLinkSubject()); $mailer->setMaxRecipients($view->maxRecipients); // Set up reCaptcha @@ -126,7 +180,8 @@ class SearchController extends AbstractSolrSearch } $userId = is_object($user) ? $user->id : null; - $searchHistoryHelper = $this->serviceLocator->get('VuFind\Search\History'); + $searchHistoryHelper = $this->serviceLocator + ->get(\VuFind\Search\History::class); if ($this->params()->fromQuery('purge')) { $searchHistoryHelper->purgeSearchHistory($userId); @@ -134,8 +189,19 @@ class SearchController extends AbstractSolrSearch // We don't want to remember the last search after a purge: $this->getSearchMemory()->forgetSearch(); } - $lastSearches = $searchHistoryHelper->getSearchHistory($userId); - return $this->createViewModel($lastSearches); + $viewData = $searchHistoryHelper->getSearchHistory($userId); + // Eliminate schedule settings if scheduled searches are disabled; add + // user email data if scheduled searches are enabled. + $scheduleOptions = $this->serviceLocator + ->get(\VuFind\Search\History::class) + ->getScheduleOptions(); + if (empty($scheduleOptions)) { + unset($viewData['schedule']); + } else { + $viewData['scheduleOptions'] = $scheduleOptions; + $viewData['alertemail'] = is_object($user) ? $user->email : null; + } + return $this->createViewModel($viewData); } /** @@ -280,7 +346,7 @@ class SearchController extends AbstractSolrSearch + $this->getRequest()->getPost()->toArray() ); $view = $this->createViewModel(); - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); + $runner = $this->serviceLocator->get(\VuFind\Search\SearchRunner::class); $view->results = $runner->run( $request, 'SolrReserves', $this->getSearchSetupCallback() ); @@ -415,7 +481,8 @@ class SearchController extends AbstractSolrSearch // Get suggestions and make sure they are an array (we don't want to JSON // encode them into an object): - $suggester = $this->serviceLocator->get('VuFind\Autocomplete\Suggester'); + $suggester = $this->serviceLocator + ->get(\VuFind\Autocomplete\Suggester::class); $suggestions = $suggester->getSuggestions($query, 'type', 'lookfor'); // Send the JSON response: @@ -435,7 +502,7 @@ class SearchController extends AbstractSolrSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('config'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php b/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php index 56c836a91683d210dceb4db0ab60fc188e9eb75a..d244ad85450ff083b16400f16deb9a5a0ccce5db 100644 --- a/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php +++ b/module/VuFind/src/VuFind/Controller/ShibbolethLogoutNotificationController.php @@ -106,7 +106,8 @@ class ShibbolethLogoutNotificationController extends AbstractBase if (empty($row)) { return; } - $sessionManager = $this->serviceLocator->get('Zend\Session\SessionManager'); + $sessionManager = $this->serviceLocator + ->get(\Zend\Session\SessionManager::class); $handler = $sessionManager->getSaveHandler(); $handler->destroy($row['session_id']); } diff --git a/module/finc/src/finc/Recommend/Factory.php b/module/VuFind/src/VuFind/Controller/ShortlinkController.php similarity index 60% rename from module/finc/src/finc/Recommend/Factory.php rename to module/VuFind/src/VuFind/Controller/ShortlinkController.php index 0ac7101fb807c0a16efe8d4ad509444701e86b42..5a1034d85283122cc348db4c2d238b3ad46c93f0 100644 --- a/module/finc/src/finc/Recommend/Factory.php +++ b/module/VuFind/src/VuFind/Controller/ShortlinkController.php @@ -1,10 +1,10 @@ <?php /** - * Recommendation Module Factory Class + * Short link controller * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2014. + * Copyright (C) Villanova University 2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,40 +20,41 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Recommendations + * @package Controller * @author Demian Katz <demian.katz@villanova.edu> - * @author Viola Elsenhans <elsenhans@ub.uni-leipzig.de> - * @author Frank Morgner <morgnerf@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * @link https://vufind.org Main Site */ -namespace finc\Recommend; +namespace VuFind\Controller; -use Zend\ServiceManager\ServiceManager; +use VuFind\UrlShortener\UrlShortenerInterface; /** - * Recommendation Module Factory Class + * Short link controller * * @category VuFind - * @package Recommendations + * @package Controller * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore + * @link https://vufind.org Main Site */ -class Factory extends \Vufind\Recommend\Factory +class ShortlinkController extends AbstractBase { /** - * Factory for AuthorFacets module. - * - * @param ServiceManager $sm Service manager. + * Resolve full version of shortlink & redirect to target. * - * @return EbscoResult + * @return mixed */ - public static function getEbscoResults(ServiceManager $sm) + public function redirectAction() { - return new EbscoResults(); + if ($id = $this->params('id')) { + $resolver = $this->serviceLocator->get(UrlShortenerInterface::class); + if ($url = $resolver->resolve($id)) { + return $this->redirect()->toUrl($url); + } + } + + $this->getResponse()->setStatusCode(404); } /** diff --git a/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php b/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php index 221c6087d53eb50a978ed211464fd0534254f6b0..5d6f75281fd413e8c032fcf4419d8f9695affcbf 100644 --- a/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php +++ b/module/VuFind/src/VuFind/Controller/StorageRetrievalRequestsTrait.php @@ -132,7 +132,7 @@ trait StorageRetrievalRequestsTrait // Find and format the default required date: $defaultRequired = $this->storageRetrievalRequests() ->getDefaultRequiredDate($checkRequests); - $defaultRequired = $this->serviceLocator->get('VuFind\Date\Converter') + $defaultRequired = $this->serviceLocator->get(\VuFind\Date\Converter::class) ->convertToDisplayDate("U", $defaultRequired); try { $defaultPickup @@ -141,12 +141,15 @@ trait StorageRetrievalRequestsTrait $defaultPickup = false; } + $config = $this->getConfig(); + $allowHomeLibrary = $config->Account->set_home_library ?? true; $view = $this->createViewModel( [ 'gatheredDetails' => $gatheredDetails, 'pickup' => $pickup, 'defaultPickup' => $defaultPickup, - 'homeLibrary' => $this->getUser()->home_library, + 'homeLibrary' => $allowHomeLibrary + ? $this->getUser()->home_library : '', 'extraFields' => $extraFields, 'defaultRequiredDate' => $defaultRequired, 'helpText' => $checkRequests['helpText'] ?? null diff --git a/module/VuFind/src/VuFind/Controller/SummonController.php b/module/VuFind/src/VuFind/Controller/SummonController.php index fe12aa78700b54602ae11ece777c9ba10aca38bf..8bb41797e2bf7b6d780eb05d8bec25f5dffc0fa9 100644 --- a/module/VuFind/src/VuFind/Controller/SummonController.php +++ b/module/VuFind/src/VuFind/Controller/SummonController.php @@ -59,7 +59,7 @@ class SummonController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('Summon'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; @@ -106,7 +106,7 @@ class SummonController extends AbstractSearch // Set up facet information: $facets = $this->serviceLocator - ->get('VuFind\Search\FacetCache\PluginManager')->get('Summon') + ->get(\VuFind\Search\FacetCache\PluginManager::class)->get('Summon') ->getList('Advanced'); $view->facetList = $this->processAdvancedFacets($facets, $view->saved); $specialFacets = $this->parseSpecialFacetsSetting( diff --git a/module/VuFind/src/VuFind/Controller/SummonrecordController.php b/module/VuFind/src/VuFind/Controller/SummonrecordController.php index 07fb137d09b15e0b857ec75438fd320306bc292d..47b3f339c546094746f099865a461d14344a1f84 100644 --- a/module/VuFind/src/VuFind/Controller/SummonrecordController.php +++ b/module/VuFind/src/VuFind/Controller/SummonrecordController.php @@ -63,7 +63,7 @@ class SummonrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('Summon'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/UpgradeController.php b/module/VuFind/src/VuFind/Controller/UpgradeController.php index 13c0b96da11ff3546497b6aca2b05bd346b7f2f8..1cac1bb50d2cc088cd294f83e16d8396b3072fd9 100644 --- a/module/VuFind/src/VuFind/Controller/UpgradeController.php +++ b/module/VuFind/src/VuFind/Controller/UpgradeController.php @@ -237,7 +237,7 @@ class UpgradeController extends AbstractBase // subsequent calls. static $adapter = false; if (!$adapter) { - $factory = $this->serviceLocator->get('VuFind\Db\AdapterFactory'); + $factory = $this->serviceLocator->get(\VuFind\Db\AdapterFactory::class); $adapter = $factory->getAdapter( $this->session->dbRootUser, $this->session->dbRootPass ); @@ -302,7 +302,7 @@ class UpgradeController extends AbstractBase protected function fixSearchChecksumsInDatabase() { $manager = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager'); + ->get(\VuFind\Search\Results\PluginManager::class); $search = $this->getTable('search'); $searchWhere = ['checksum' => null, 'saved' => 1]; $searchRows = $search->select($searchWhere); @@ -498,7 +498,8 @@ class UpgradeController extends AbstractBase // If this is a MySQL connection, we can do an automatic upgrade; // if VuFind is using a different database, we have to prompt the // user to check the migrations directory and upgrade manually. - $adapter = $this->serviceLocator->get('Zend\Db\Adapter\Adapter'); + $adapter = $this->serviceLocator + ->get(\Zend\Db\Adapter\Adapter::class); $platform = $adapter->getDriver()->getDatabasePlatformName(); if (strtolower($platform) == 'mysql') { $upgradeResult = $this->upgradeMySQL($adapter); @@ -597,7 +598,7 @@ class UpgradeController extends AbstractBase try { // Query a table known to exist $factory = $this->serviceLocator - ->get('VuFind\Db\AdapterFactory'); + ->get(\VuFind\Db\AdapterFactory::class); $db = $factory->getAdapter($dbrootuser, $pass); $db->query("SELECT * FROM user;"); $this->session->dbRootUser = $dbrootuser; @@ -725,7 +726,7 @@ class UpgradeController extends AbstractBase // Process submit button: if ($this->formWasSubmitted('submit')) { - $converter = $this->serviceLocator->get('VuFind\Date\Converter'); + $converter = $this->serviceLocator->get(\VuFind\Date\Converter::class); foreach ($problems as $problem) { try { $driver = $this->getRecordLoader() @@ -828,7 +829,7 @@ class UpgradeController extends AbstractBase { // If the cache is messed up, nothing is going to work right -- check that // first: - $cache = $this->serviceLocator->get('VuFind\Cache\Manager'); + $cache = $this->serviceLocator->get(\VuFind\Cache\Manager::class); if ($cache->hasDirectoryCreationError()) { return $this->redirect()->toRoute('install-fixcache'); } diff --git a/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php b/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php index 91ee8f310f52c6f99cb177ca74c9dd373ccd3865..572b9d0c345211c57be4a982f50a3cfabe7ff521 100644 --- a/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php +++ b/module/VuFind/src/VuFind/Controller/UpgradeControllerFactory.php @@ -28,7 +28,6 @@ namespace VuFind\Controller; use Interop\Container\ContainerInterface; -use Zend\ServiceManager\Factory\FactoryInterface; /** * Upgrade controller factory. @@ -39,7 +38,7 @@ use Zend\ServiceManager\Factory\FactoryInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class UpgradeControllerFactory implements FactoryInterface +class UpgradeControllerFactory extends AbstractBaseFactory { /** * Create an object @@ -61,10 +60,13 @@ class UpgradeControllerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $cookieManager = $container->get('VuFind\Cookie\CookieManager'); + $cookieManager = $container->get(\VuFind\Cookie\CookieManager::class); $session = new \Zend\Session\Container( - 'upgrade', $container->get('Zend\Session\SessionManager') + 'upgrade', $container->get(\Zend\Session\SessionManager::class) + ); + return $this->applyPermissions( + $container, + new $requestedName($container, $cookieManager, $session) ); - return new $requestedName($container, $cookieManager, $session); } } diff --git a/module/VuFind/src/VuFind/Controller/WorldcatController.php b/module/VuFind/src/VuFind/Controller/WorldcatController.php index 8754974368221baf2c4ff1256c6e926226f12047..9325bf7a51092cd126f7381b4d01e167a4dc28f1 100644 --- a/module/VuFind/src/VuFind/Controller/WorldcatController.php +++ b/module/VuFind/src/VuFind/Controller/WorldcatController.php @@ -58,7 +58,7 @@ class WorldcatController extends AbstractSearch */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('WorldCat'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php b/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php index 99898d3d1a78107f8735d1caa186840c682603d5..2d20805a897fd9c6f996da1701691f873a47300f 100644 --- a/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php +++ b/module/VuFind/src/VuFind/Controller/WorldcatrecordController.php @@ -61,7 +61,7 @@ class WorldcatrecordController extends AbstractRecord */ protected function resultScrollerActive() { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->get('WorldCat'); return isset($config->Record->next_prev_navigation) && $config->Record->next_prev_navigation; diff --git a/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php index e92ea718d403fac8fd3dddca8224cd73c41e9913..b3911fe8df7435c04b34a7dcf3a14d9d623d7e24 100644 --- a/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php +++ b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php @@ -62,7 +62,8 @@ class CookieManagerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $path = '/'; if ($config->Cookies->limit_by_path ?? false) { $path = Console::isConsole() diff --git a/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php b/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php index 068783abfdeaa8c7fb261285787aa5be2f986555..49518c6c0524e7a026476d5e0cbdd4f90f3db7e8 100644 --- a/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php +++ b/module/VuFind/src/VuFind/Cover/CachingProxyFactory.php @@ -61,10 +61,10 @@ class CachingProxyFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $cacheDir = $container->get('VuFind\Cache\Manager') + $cacheDir = $container->get(\VuFind\Cache\Manager::class) ->getCache('cover')->getOptions()->getCacheDir(); - $client = $container->get('VuFindHttp\HttpService')->createClient(); - $config = $container->get('VuFind\Config\PluginManager')->get('config') + $client = $container->get(\VuFindHttp\HttpService::class)->createClient(); + $config = $container->get(\VuFind\Config\PluginManager::class)->get('config') ->toArray(); $whitelist = isset($config['Content']['coverproxyCache']) ? (array)$config['Content']['coverproxyCache'] : []; diff --git a/module/VuFind/src/VuFind/Cover/GeneratorFactory.php b/module/VuFind/src/VuFind/Cover/GeneratorFactory.php index 36938fc0dde5cf5e5acb089d6f3f27b199aa35cd..8bc3547c65590e93e2fc148bfa8e95d662856e2b 100644 --- a/module/VuFind/src/VuFind/Cover/GeneratorFactory.php +++ b/module/VuFind/src/VuFind/Cover/GeneratorFactory.php @@ -62,8 +62,8 @@ class GeneratorFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFindTheme\ThemeInfo'), - $container->get('VuFind\Cover\Layer\PluginManager') + $container->get(\VuFindTheme\ThemeInfo::class), + $container->get(\VuFind\Cover\Layer\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php b/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php index 303ec2450e55fb9859f42eb4b898d351dbc72120..3500bec35b9dea2b3ccdf391670ef23164521a27 100644 --- a/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php +++ b/module/VuFind/src/VuFind/Cover/Layer/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Cover\Layer; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Cover layer plugin manager * @@ -44,10 +46,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'defaulttext' => 'VuFind\Cover\Layer\DefaultText', - 'gridbackground' => 'VuFind\Cover\Layer\GridBackground', - 'initialtext' => 'VuFind\Cover\Layer\InitialText', - 'solidbackground' => 'VuFind\Cover\Layer\SolidBackground', + 'defaulttext' => DefaultText::class, + 'gridbackground' => GridBackground::class, + 'initialtext' => InitialText::class, + 'solidbackground' => SolidBackground::class, ]; /** @@ -56,14 +58,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Cover\Layer\DefaultText' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Cover\Layer\GridBackground' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Cover\Layer\InitialText' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Cover\Layer\SolidBackground' => - 'Zend\ServiceManager\Factory\InvokableFactory', + DefaultText::class => InvokableFactory::class, + GridBackground::class => InvokableFactory::class, + InitialText::class => InvokableFactory::class, + SolidBackground::class => InvokableFactory::class, ]; /** @@ -74,6 +72,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Cover\Layer\LayerInterface'; + return LayerInterface::class; } } diff --git a/module/VuFind/src/VuFind/Cover/Loader.php b/module/VuFind/src/VuFind/Cover/Loader.php index 1ca347798e586f35e9f7f9e78df2d2e1c1194443..1f195356cdd4ae0b33798a42ca15f2762766e029 100644 --- a/module/VuFind/src/VuFind/Cover/Loader.php +++ b/module/VuFind/src/VuFind/Cover/Loader.php @@ -30,6 +30,7 @@ namespace VuFind\Cover; use VuFind\Content\Covers\PluginManager as ApiManager; use VuFindCode\ISBN; +use VuFindCode\ISMN; /** * Book Cover Generator @@ -121,6 +122,20 @@ class Loader extends \VuFind\ImageLoader */ protected $upc = null; + /** + * User National bibliography number parameter + * + * @var array + */ + protected $nbn = null; + + /** + * User ISMN parameter + * + * @var ISMN + */ + protected $ismn = null; + /** * User record id number parameter * @@ -232,6 +247,8 @@ class Loader extends \VuFind\ImageLoader 'upc' => null, 'recordid' => null, 'source' => null, + 'nbn' => null, + 'ismn' => null, ]; } @@ -268,6 +285,7 @@ class Loader extends \VuFind\ImageLoader protected function storeSanitizedSettings($settings) { $this->isbn = new ISBN($settings['isbn']); + $this->ismn = new ISMN($settings['ismn']); if (!empty($settings['issn'])) { $rawissn = preg_replace('/[^0-9X]/', '', strtoupper($settings['issn'])); $this->issn = substr($rawissn, 0, 8); @@ -278,6 +296,7 @@ class Loader extends \VuFind\ImageLoader $this->upc = $settings['upc']; $this->recordid = $settings['recordid']; $this->source = $settings['source']; + $this->nbn = $settings['nbn']; $this->type = preg_replace('/[^a-zA-Z]/', '', $settings['type']); $this->size = $settings['size']; } @@ -289,7 +308,8 @@ class Loader extends \VuFind\ImageLoader * contain any or all of these keys: 'isbn' (ISBN), 'size' (requested size), * 'type' (content type), 'title' (title of book, for dynamic covers), 'author' * (author of book, for dynamic covers), 'callnumber' (unique ID, for dynamic - * covers), 'issn' (ISSN), 'oclc' (OCLC number), 'upc' (UPC number). + * covers), 'issn' (ISSN), 'oclc' (OCLC number), 'upc' (UPC number), + * 'nbn' (national bibliography number), 'ismn' (ISMN). * * @return void */ @@ -347,6 +367,10 @@ class Loader extends \VuFind\ImageLoader return $this->getCachePath($this->size, 'OCLC' . $ids['oclc']); } elseif (isset($ids['upc'])) { return $this->getCachePath($this->size, 'UPC' . $ids['upc']); + } elseif (isset($ids['nbn'])) { + return $this->getCachePath($this->size, 'NBN' . $ids['nbn']); + } elseif (isset($ids['ismn'])) { + return $this->getCachePath($this->size, 'ISMN' . $ids['ismn']->get13()); } elseif (isset($ids['recordid']) && isset($ids['source'])) { return $this->getCachePath( $this->size, @@ -376,6 +400,12 @@ class Loader extends \VuFind\ImageLoader if ($this->upc && strlen($this->upc) > 0) { $ids['upc'] = $this->upc; } + if ($this->nbn && strlen($this->nbn) > 0) { + $ids['nbn'] = $this->nbn; + } + if ($this->ismn && $this->ismn->isValid()) { + $ids['ismn'] = $this->ismn; + } if ($this->recordid && strlen($this->recordid) > 0) { $ids['recordid'] = $this->recordid; } @@ -599,7 +629,7 @@ class Loader extends \VuFind\ImageLoader $imagePath = substr($url, 7); // Display the image: - $this->contentType = mime_content_type($imagePath); + $this->contentType = mime_content_type($imagePath); $this->image = file_get_contents($imagePath); return true; } else { diff --git a/module/VuFind/src/VuFind/Cover/LoaderFactory.php b/module/VuFind/src/VuFind/Cover/LoaderFactory.php index a8bd85075a701009fb6c453c0ac610de9eb7e95d..8629f05448609b3853582e45830beaff7dda0d45 100644 --- a/module/VuFind/src/VuFind/Cover/LoaderFactory.php +++ b/module/VuFind/src/VuFind/Cover/LoaderFactory.php @@ -61,19 +61,22 @@ class LoaderFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $cacheDir = $container->get('VuFind\Cache\Manager') + $cacheDir = $container->get(\VuFind\Cache\Manager::class) ->getCache('cover')->getOptions()->getCacheDir(); - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $loader = new $requestedName( $config, - $container->get('VuFind\Content\Covers\PluginManager'), - $container->get('VuFindTheme\ThemeInfo'), - $container->get('VuFindHttp\HttpService'), + $container->get(\VuFind\Content\Covers\PluginManager::class), + $container->get(\VuFindTheme\ThemeInfo::class), + $container->get(\VuFindHttp\HttpService::class), $cacheDir ); // Add cover generator if enabled: if ($config->Content->makeDynamicCovers ?? false) { - $loader->setCoverGenerator($container->get('VuFind\Cover\Generator')); + $loader->setCoverGenerator( + $container->get(\VuFind\Cover\Generator::class) + ); } return $loader; } diff --git a/module/VuFind/src/VuFind/Cover/RouterFactory.php b/module/VuFind/src/VuFind/Cover/RouterFactory.php index 53d9d8351cc006dfe3dcc307d105f4279dd4d831..431a226cc6ec28f5ee86043379c7ec27d8c44f69 100644 --- a/module/VuFind/src/VuFind/Cover/RouterFactory.php +++ b/module/VuFind/src/VuFind/Cover/RouterFactory.php @@ -61,8 +61,15 @@ class RouterFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $base = $container->get('ControllerPluginManager')->get('url') - ->fromRoute('cover-show'); + // Try to get the base URL from the controller plugin; fail over to + // the view helper if that doesn't work. + try { + $base = $container->get('ControllerPluginManager')->get('url') + ->fromRoute('cover-show'); + } catch (\Exception $e) { + $base = $container->get('ViewRenderer')->plugin('url') + ->__invoke('cover-show'); + } return new $requestedName($base); } } diff --git a/module/VuFind/src/VuFind/Crypt/HMACFactory.php b/module/VuFind/src/VuFind/Crypt/HMACFactory.php index f645ebb9d8934fa49afde3e70c7d0d995a564d1f..198ce0fd7f6672e19d853688a6f03d877c3bd199 100644 --- a/module/VuFind/src/VuFind/Crypt/HMACFactory.php +++ b/module/VuFind/src/VuFind/Crypt/HMACFactory.php @@ -61,7 +61,8 @@ class HMACFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->Security->HMACkey); } } diff --git a/module/VuFind/src/VuFind/Db/AdapterFactory.php b/module/VuFind/src/VuFind/Db/AdapterFactory.php index 71f74665ced916c7f2d1616464327df58a4a91dd..79b7393706a0b90c81a99c5240fda1f2d58bda7f 100644 --- a/module/VuFind/src/VuFind/Db/AdapterFactory.php +++ b/module/VuFind/src/VuFind/Db/AdapterFactory.php @@ -1,6 +1,7 @@ <?php /** - * Database utility class. + * Database utility class. May be used as a service or as a standard + * Zend Framework factory. * * PHP version 7 * @@ -27,10 +28,13 @@ */ namespace VuFind\Db; +use Interop\Container\ContainerInterface; +use Zend\Config\Config; use Zend\Db\Adapter\Adapter; /** - * Database utility class. + * Database utility class. May be used as a service or as a standard + * Zend Framework factory. * * @category VuFind * @package Db @@ -38,23 +42,49 @@ use Zend\Db\Adapter\Adapter; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ -class AdapterFactory +class AdapterFactory implements \Zend\ServiceManager\Factory\FactoryInterface { /** * VuFind configuration * - * @var \Zend\Config\Config + * @var Config */ protected $config; /** * Constructor * - * @param \Zend\Config\Config $config VuFind configuration + * @param Config $config VuFind configuration (provided when used as service; + * omitted when used as factory) */ - public function __construct(\Zend\Config\Config $config) + public function __construct(Config $config = null) { - $this->config = $config; + $this->config = $config ?: new Config([]); + } + + /** + * Create an object (glue code for FactoryInterface compliance) + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory!'); + } + $this->config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + return $this->getAdapter(); } /** @@ -70,6 +100,9 @@ class AdapterFactory public function getAdapter($overrideUser = null, $overridePass = null) { // Parse details from connection string: + if (!isset($this->config->Database->database)) { + throw new \Exception('"database" setting missing'); + } return $this->getAdapterFromConnectionString( $this->config->Database->database, $overrideUser, $overridePass ); diff --git a/module/VuFind/src/VuFind/Db/Row/AuthHash.php b/module/VuFind/src/VuFind/Db/Row/AuthHash.php new file mode 100644 index 0000000000000000000000000000000000000000..265338271e773236abee5013156362ac7dd3a213 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Row/AuthHash.php @@ -0,0 +1,53 @@ +<?php +/** + * Row Definition for auth_hash + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Db\Row; + +/** + * Row Definition for auth_hash + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class AuthHash extends RowGateway +{ + /** + * Constructor + * + * @param \Zend\Db\Adapter\Adapter $adapter Database adapter + */ + public function __construct($adapter) + { + parent::__construct('id', 'auth_hash', $adapter); + } +} diff --git a/module/VuFind/src/VuFind/Db/Row/PluginManager.php b/module/VuFind/src/VuFind/Db/Row/PluginManager.php index c5cc9009a52fdba72a5be7ee5356c386130f0611..02d8d0fb4dbc899d3b49cf15216a159edfe4f593 100644 --- a/module/VuFind/src/VuFind/Db/Row/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Row/PluginManager.php @@ -44,20 +44,21 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'changetracker' => 'VuFind\Db\Row\ChangeTracker', - 'comments' => 'VuFind\Db\Row\Comments', - 'externalsession' => 'VuFind\Db\Row\ExternalSession', - 'oairesumption' => 'VuFind\Db\Row\OaiResumption', - 'record' => 'VuFind\Db\Row\Record', - 'resource' => 'VuFind\Db\Row\Resource', - 'resourcetags' => 'VuFind\Db\Row\ResourceTags', - 'search' => 'VuFind\Db\Row\Search', - 'session' => 'VuFind\Db\Row\Session', - 'tags' => 'VuFind\Db\Row\Tags', - 'user' => 'VuFind\Db\Row\User', - 'usercard' => 'VuFind\Db\Row\UserCard', - 'userlist' => 'VuFind\Db\Row\UserList', - 'userresource' => 'VuFind\Db\Row\UserResource', + 'changetracker' => ChangeTracker::class, + 'comments' => Comments::class, + 'externalsession' => ExternalSession::class, + 'oairesumption' => OaiResumption::class, + 'record' => Record::class, + 'resource' => Resource::class, + 'resourcetags' => ResourceTags::class, + 'search' => Search::class, + 'session' => Session::class, + 'shortlinks' => Shortlinks::class, + 'tags' => Tags::class, + 'user' => User::class, + 'usercard' => UserCard::class, + 'userlist' => UserList::class, + 'userresource' => UserResource::class, ]; /** @@ -66,20 +67,22 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Db\Row\ChangeTracker' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Comments' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\ExternalSession' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\OaiResumption' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Record' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Resource' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\ResourceTags' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Search' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Session' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\Tags' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\User' => 'VuFind\Db\Row\UserFactory', - 'VuFind\Db\Row\UserCard' => 'VuFind\Db\Row\RowGatewayFactory', - 'VuFind\Db\Row\UserList' => 'VuFind\Db\Row\UserListFactory', - 'VuFind\Db\Row\UserResource' => 'VuFind\Db\Row\RowGatewayFactory', + AuthHash::class => RowGatewayFactory::class, + ChangeTracker::class => RowGatewayFactory::class, + Comments::class => RowGatewayFactory::class, + ExternalSession::class => RowGatewayFactory::class, + OaiResumption::class => RowGatewayFactory::class, + Record::class => RowGatewayFactory::class, + Resource::class => RowGatewayFactory::class, + ResourceTags::class => RowGatewayFactory::class, + Search::class => RowGatewayFactory::class, + Session::class => RowGatewayFactory::class, + Shortlinks::class => RowGatewayFactory::class, + Tags::class => RowGatewayFactory::class, + User::class => UserFactory::class, + UserCard::class => RowGatewayFactory::class, + UserList::class => UserListFactory::class, + UserResource::class => RowGatewayFactory::class, ]; /** @@ -90,6 +93,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Db\Row\RowGateway'; + return RowGateway::class; } } diff --git a/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php b/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php index 6cbff5b00bc80851226d6f65c57205f8dbcb97be..17054918635855ed3720a7e486c50509885bdc41 100644 --- a/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php +++ b/module/VuFind/src/VuFind/Db/Row/RowGatewayFactory.php @@ -57,7 +57,7 @@ class RowGatewayFactory implements \Zend\ServiceManager\Factory\FactoryInterface public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $adapter = $container->get('Zend\Db\Adapter\Adapter'); + $adapter = $container->get(\Zend\Db\Adapter\Adapter::class); return new $requestedName($adapter, ...($options !== null ? $options : [])); } } diff --git a/module/VuFind/src/VuFind/Db/Row/Search.php b/module/VuFind/src/VuFind/Db/Row/Search.php index f349b7ffad63864018898fc603e2f4a0dc42d72e..f5d92c40d342831d8c4096887056bbf0c26d6299 100644 --- a/module/VuFind/src/VuFind/Db/Row/Search.php +++ b/module/VuFind/src/VuFind/Db/Row/Search.php @@ -27,6 +27,8 @@ */ namespace VuFind\Db\Row; +use VuFind\Crypt\HMAC; + /** * Row Definition for search * @@ -48,6 +50,22 @@ class Search extends RowGateway parent::__construct('id', 'search', $adapter); } + /** + * Support method to make sure that the search_object field is formatted as a + * string, since PostgreSQL sometimes represents it as a resource. + * + * @return void + */ + protected function normalizeSearchObject() + { + // Note that if we have a resource, we need to grab the contents before + // saving -- this is necessary for PostgreSQL compatibility although MySQL + // returns a plain string + if (is_resource($this->search_object)) { + $this->search_object = stream_get_contents($this->search_object); + } + } + /** * Get the search object from the row * @@ -55,10 +73,9 @@ class Search extends RowGateway */ public function getSearchObject() { - // Resource check for PostgreSQL compatibility: - $raw = is_resource($this->search_object) - ? stream_get_contents($this->search_object) : $this->search_object; - $result = unserialize($raw); + // We need to make sure the search object is a string before unserializing: + $this->normalizeSearchObject(); + $result = unserialize($this->search_object); if (!($result instanceof \VuFind\Search\Minified)) { throw new \Exception('Problem decoding saved search'); } @@ -72,12 +89,58 @@ class Search extends RowGateway */ public function save() { - // Note that if we have a resource, we need to grab the contents before - // saving -- this is necessary for PostgreSQL compatibility although MySQL - // returns a plain string - $this->search_object = is_resource($this->search_object) - ? stream_get_contents($this->search_object) - : $this->search_object; - parent::save(); + // We can't save if the search object is a resource; make sure it's a + // string first: + $this->normalizeSearchObject(); + return parent::save(); + } + + /** + * Set last executed time for scheduled alert. + * + * @param string $time Time. + * + * @return mixed + */ + public function setLastExecuted($time) + { + $this->last_notification_sent = $time; + return $this->save(); + } + + /** + * Set schedule for scheduled alert. + * + * @param int $schedule Schedule. + * @param string $url Site base URL + * + * @return mixed + */ + public function setSchedule($schedule, $url = null) + { + $this->notification_frequency = $schedule; + if ($url) { + $this->notification_base_url = $url; + } + return $this->save(); + } + + /** + * Utility function for generating a token for unsubscribing a + * saved search. + * + * @param VuFind\Crypt\HMAC $hmac HMAC hash generator + * @param object $user User object + * + * @return string token + */ + public function getUnsubscribeSecret(HMAC $hmac, $user) + { + $data = [ + 'id' => $this->id, + 'user_id' => $user->id, + 'created' => $user->created + ]; + return $hmac->generate(array_keys($data), $data); } } diff --git a/module/VuFind/src/VuFind/Db/Row/Shortlinks.php b/module/VuFind/src/VuFind/Db/Row/Shortlinks.php new file mode 100644 index 0000000000000000000000000000000000000000..36345f9876b6d792487cdd44b3fa4ed49eaa5b1b --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Row/Shortlinks.php @@ -0,0 +1,50 @@ +<?php +/** + * Row Definition for shortlinks + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Db\Row; + +/** + * Row Definition for shortlinks + * + * @category VuFind + * @package Db_Row + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Shortlinks extends RowGateway +{ + /** + * Constructor + * + * @param \Zend\Db\Adapter\Adapter $adapter Database adapter + */ + public function __construct($adapter) + { + parent::__construct('id', 'shortlinks', $adapter); + } +} diff --git a/module/VuFind/src/VuFind/Db/Row/User.php b/module/VuFind/src/VuFind/Db/Row/User.php index 2b3205e04782d9a00dd16de08247a68243bde0aa..d9bd42085f4fbe5c5871fa9b24f2ae6284646c0b 100644 --- a/module/VuFind/src/VuFind/Db/Row/User.php +++ b/module/VuFind/src/VuFind/Db/Row/User.php @@ -143,6 +143,23 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, return $result; } + /** + * Save date/time when email address has been verified. + * + * @param string $datetime optional date/time to save. + * + * @return mixed The output of the save method. + */ + public function saveEmailVerified($datetime=null) + { + if ($datetime === null) { + $datetime = date('Y-m-d H:i:s'); + } + + $this->email_verified = $datetime; + return $this->save(); + } + /** * This is a getter for the Catalog Password. It will return a plaintext version * of the password. @@ -227,6 +244,16 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, return $this->save(); } + /** + * Check whether the email address has been verified yet. + * + * @return bool + */ + public function checkEmailVerified() + { + return !empty($this->email_verified); + } + /** * Get a list of all tags generated by the user in favorites lists. Note that * the returned list WILL NOT include tags attached to records that are not @@ -653,6 +680,41 @@ class User extends RowGateway implements \VuFind\Db\Table\DbTableAwareInterface, return $this->save(); } + /** + * Updated saved language + * + * @param string $language New language + * + * @return void + */ + public function updateLastLanguage($language) + { + $this->last_language = $language; + $this->save(); + } + + /** + * Update the user's email address, if appropriate. Note that this does NOT + * automatically save the row; it assumes a subsequent call will be made to + * the save() method. + * + * @param string $email New email address + * @param bool $userProvided Was this email provided by the user (true) or + * an automated lookup (false)? + * + * @return void + */ + public function updateEmail($email, $userProvided = false) + { + // Only change the email if it is a non-empty value and was user provided + // (the user is always right) or the previous email was NOT user provided + // (a value may have changed in an upstream system). + if (!empty($email) && ($userProvided || !$this->user_provided_email)) { + $this->email = $email; + $this->user_provided_email = $userProvided ? 1 : 0; + } + } + /** * Get the list of roles of this identity * diff --git a/module/VuFind/src/VuFind/Db/Row/UserFactory.php b/module/VuFind/src/VuFind/Db/Row/UserFactory.php index eb74dcd025e4f1e29d2bf5c5a5d276b01a456004..4ebcf82037ac809c3b7a1e7740632ccd868499e3 100644 --- a/module/VuFind/src/VuFind/Db/Row/UserFactory.php +++ b/module/VuFind/src/VuFind/Db/Row/UserFactory.php @@ -67,14 +67,15 @@ class UserFactory extends RowGatewayFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $privacy = isset($config->Authentication->privacy) && $config->Authentication->privacy; $rowClass = $privacy ? $this->privateUserClass : $requestedName; $prototype = parent::__invoke($container, $rowClass, $options); $prototype->setConfig($config); if ($privacy) { - $sessionManager = $container->get('Zend\Session\SessionManager'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); $session = new \Zend\Session\Container('Account', $sessionManager); $prototype->setSession($session); } diff --git a/module/VuFind/src/VuFind/Db/Row/UserListFactory.php b/module/VuFind/src/VuFind/Db/Row/UserListFactory.php index d0a8c1f2fd529043e550720e007b94314c0f4412..6965f6c7f104e9b626d3a25d99c4c2a90ec25475 100644 --- a/module/VuFind/src/VuFind/Db/Row/UserListFactory.php +++ b/module/VuFind/src/VuFind/Db/Row/UserListFactory.php @@ -60,7 +60,7 @@ class UserListFactory extends RowGatewayFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $sessionManager = $container->get('Zend\Session\SessionManager'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); $session = new \Zend\Session\Container('List', $sessionManager); return parent::__invoke($container, $requestedName, [$session]); } diff --git a/module/VuFind/src/VuFind/Db/Table/AuthHash.php b/module/VuFind/src/VuFind/Db/Table/AuthHash.php new file mode 100644 index 0000000000000000000000000000000000000000..0918b0aa60ad04190a658dd556ea1fc136da4c05 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/AuthHash.php @@ -0,0 +1,145 @@ +<?php +/** + * Table Definition for auth_hash + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Db\Table; + +use VuFind\Db\Row\RowGateway; +use Zend\Db\Adapter\Adapter; + +/** + * Table Definition for auth_hash + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class AuthHash extends Gateway +{ + use ExpirationTrait; + + const TYPE_EMAIL = 'email'; // EmailAuthenticator + + /** + * Constructor + * + * @param Adapter $adapter Database adapter + * @param PluginManager $tm Table manager + * @param array $cfg Zend Framework configuration + * @param RowGateway $rowObj Row prototype object (null for default) + * @param string $table Name of database table to interface with + */ + public function __construct(Adapter $adapter, PluginManager $tm, $cfg, + RowGateway $rowObj = null, $table = 'auth_hash' + ) { + parent::__construct($adapter, $tm, $cfg, $rowObj, $table); + } + + /** + * Retrieve an object from the database based on hash and type; create a new + * row if no existing match is found. + * + * @param string $hash Hash + * @param string $type Hash type + * @param bool $create Should we create rows that don't already exist? + * + * @return \VuFind\Db\Row\AuthHash + */ + public function getByHashAndType($hash, $type, $create = true) + { + $row = $this->select(['hash' => $hash, 'type' => $type])->current(); + if ($create && empty($row)) { + $row = $this->createRow(); + $row->hash = $hash; + $row->type = $type; + $row->created = date('Y-m-d H:i:s'); + } + return $row; + } + + /** + * Retrieve last object from the database based on session id. + * + * @param string $sessionId Session ID + * + * @return \VuFind\Db\Row\AuthHash + */ + public function getLatestBySessionId($sessionId) + { + $callback = function ($select) use ($sessionId) { + $select->where->equalTo('session_id', $sessionId); + $select->order('created DESC'); + }; + return $this->select($callback)->current(); + } + + /** + * Get a query representing expired sessions (this can be passed + * to select() or delete() for further processing). + * + * @param int $daysOld Age in days of an "expired" session. + * + * @return function + */ + public function getExpiredQuery($daysOld = 2) + { + // Determine the expiration date: + $expireDate = time() - $daysOld * 24 * 60 * 60; + $callback = function ($select) use ($expireDate) { + $select->where->lessThan('created', date('Y-m-d H:i:s', $expireDate)); + }; + return $callback; + } + + /** + * Update the select statement to find records to delete. + * + * @param Select $select Select clause + * @param int $daysOld Age in days of an "expired" record. + * @param int $idFrom Lowest id of rows to delete. + * @param int $idTo Highest id of rows to delete. + * + * @return void + */ + protected function expirationCallback($select, $daysOld, $idFrom = null, + $idTo = null + ) { + $expireDate = time() - $daysOld * 24 * 60 * 60; + $where = $select->where + ->lessThan('created', date('Y-m-d H:i:s', $expireDate)); + if (null !== $idFrom) { + $where->and->greaterThanOrEqualTo('id', $idFrom); + } + if (null !== $idTo) { + $where->and->lessThanOrEqualTo('id', $idTo); + } + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php b/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php index 8c3e790d6fbb56eaa74fb9cc4afbdeeef42aadb7..a83861a3202183c4fcacc8d0c7f4101d0082d18d 100644 --- a/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/CaseSensitiveTagsFactory.php @@ -60,7 +60,8 @@ class CaseSensitiveTagsFactory extends GatewayFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $caseSensitive = isset($config->Social->case_sensitive_tags) && $config->Social->case_sensitive_tags; return parent::__invoke($container, $requestedName, [$caseSensitive]); diff --git a/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php b/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php index c7b3e225cd34ba94d4d3007e9013cda9cbb61014..dc36e2fedb6953811678b0665cbff4cc4fcdd998 100644 --- a/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php +++ b/module/VuFind/src/VuFind/Db/Table/ChangeTracker.php @@ -29,6 +29,7 @@ namespace VuFind\Db\Table; use VuFind\Db\Row\RowGateway; use Zend\Db\Adapter\Adapter; +use Zend\Db\Sql\Expression; /** * Table Definition for change_tracker @@ -78,23 +79,78 @@ class ChangeTracker extends Gateway } /** - * Retrieve a set of deleted rows from the database. + * Build a callback function for use by the retrieveDeleted* methods. * - * @param string $core The Solr core holding the record. - * @param string $from The beginning date of the range to search. - * @param string $until The end date of the range to search. + * @param string $core The Solr core holding the record. + * @param string $from The beginning date of the range to search. + * @param string $until The end date of the range to search. + * @param int $offset Record number to retrieve first. + * @param int $limit Retrieval limit (null for no limit) + * @param array $columns Columns to retrieve (null for all) * - * @return \Zend\Db\ResultSet\AbstractResultSet + * @return \Callable */ - public function retrieveDeleted($core, $from, $until) - { - $callback = function ($select) use ($core, $from, $until) { + public function getRetrieveDeletedCallback($core, $from, $until, $offset = 0, + $limit = null, $columns = null + ) { + $params = compact('core', 'from', 'until', 'offset', 'limit', 'columns'); + return function ($select) use ($params) { + extract($params); + if ($columns !== null) { + $select->columns($columns); + } $select->where ->equalTo('core', $core) ->greaterThanOrEqualTo('deleted', $from) ->lessThanOrEqualTo('deleted', $until); $select->order('deleted'); + if ($offset > 0) { + $select->offset($offset); + } + if ($limit !== null) { + $select->limit($limit); + } }; + } + + /** + * Retrieve a set of deleted rows from the database. + * + * @param string $core The Solr core holding the record. + * @param string $from The beginning date of the range to search. + * @param string $until The end date of the range to search. + * + * @return \Zend\Db\ResultSet\AbstractResultSet + */ + public function retrieveDeletedCount($core, $from, $until) + { + $columns = ['count' => new Expression('COUNT(*)')]; + $callback = $this + ->getRetrieveDeletedCallback($core, $from, $until, 0, null, $columns); + $select = $this->sql->select(); + $callback($select); + $statement = $this->sql->prepareStatementForSqlObject($select); + $result = $statement->execute(); + return ((array)$result->current())['count']; + } + + /** + * Retrieve a set of deleted rows from the database. + * + * @param string $core The Solr core holding the record. + * @param string $from The beginning date of the range to search. + * @param string $until The end date of the range to search. + * @param int $offset Record number to retrieve first. + * @param int $limit Retrieval limit (null for no limit) + * + * @return \Zend\Db\ResultSet\AbstractResultSet + */ + public function retrieveDeleted($core, $from, $until, $offset = 0, + $limit = null + ) { + $callback = $this->getRetrieveDeletedCallback( + $core, $from, $until, $offset, $limit + ); return $this->select($callback); } diff --git a/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php b/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php index 1f124b44efe06b52eaeda0a52ae3fcbed0e3bd26..a483b2ca3e28607ec482a221755f4aa214bf1b0b 100644 --- a/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/GatewayFactory.php @@ -50,7 +50,7 @@ class GatewayFactory implements \Zend\ServiceManager\Factory\FactoryInterface */ protected function getRowPrototype(ContainerInterface $container, $requestedName) { - $rowManager = $container->get('VuFind\Db\Row\PluginManager'); + $rowManager = $container->get(\VuFind\Db\Row\PluginManager::class); // Map Table class to matching Row class. $name = str_replace("\\Table\\", "\\Row\\", $requestedName); return $rowManager->has($name) ? $rowManager->get($name) : null; @@ -73,8 +73,8 @@ class GatewayFactory implements \Zend\ServiceManager\Factory\FactoryInterface public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $adapter = $container->get('Zend\Db\Adapter\Adapter'); - $tm = $container->get('VuFind\Db\Table\PluginManager'); + $adapter = $container->get(\Zend\Db\Adapter\Adapter::class); + $tm = $container->get(\VuFind\Db\Table\PluginManager::class); $config = $container->get('config'); $rowPrototype = $this->getRowPrototype($container, $requestedName); $args = $options ? $options : []; diff --git a/module/VuFind/src/VuFind/Db/Table/PluginManager.php b/module/VuFind/src/VuFind/Db/Table/PluginManager.php index f753d55d43dc9744c5ad0f35bc5570a3f00a325c..761b62288d880f10df715bdf7ec9dfb4ad0f1b19 100644 --- a/module/VuFind/src/VuFind/Db/Table/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Table/PluginManager.php @@ -44,20 +44,21 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'changetracker' => 'VuFind\Db\Table\ChangeTracker', - 'comments' => 'VuFind\Db\Table\Comments', - 'externalsession' => 'VuFind\Db\Table\ExternalSession', - 'oairesumption' => 'VuFind\Db\Table\OaiResumption', - 'record' => 'VuFind\Db\Table\Record', - 'resource' => 'VuFind\Db\Table\Resource', - 'resourcetags' => 'VuFind\Db\Table\ResourceTags', - 'search' => 'VuFind\Db\Table\Search', - 'session' => 'VuFind\Db\Table\Session', - 'tags' => 'VuFind\Db\Table\Tags', - 'user' => 'VuFind\Db\Table\User', - 'usercard' => 'VuFind\Db\Table\UserCard', - 'userlist' => 'VuFind\Db\Table\UserList', - 'userresource' => 'VuFind\Db\Table\UserResource', + 'changetracker' => ChangeTracker::class, + 'comments' => Comments::class, + 'externalsession' => ExternalSession::class, + 'oairesumption' => OaiResumption::class, + 'record' => Record::class, + 'resource' => Resource::class, + 'resourcetags' => ResourceTags::class, + 'search' => Search::class, + 'session' => Session::class, + 'shortlinks' => Shortlinks::class, + 'tags' => Tags::class, + 'user' => User::class, + 'usercard' => UserCard::class, + 'userlist' => UserList::class, + 'userresource' => UserResource::class, ]; /** @@ -66,20 +67,22 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Db\Table\ChangeTracker' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\Comments' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\ExternalSession' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\OaiResumption' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\Record' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\Resource' => 'VuFind\Db\Table\ResourceFactory', - 'VuFind\Db\Table\ResourceTags' => 'VuFind\Db\Table\CaseSensitiveTagsFactory', - 'VuFind\Db\Table\Search' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\Session' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\Tags' => 'VuFind\Db\Table\CaseSensitiveTagsFactory', - 'VuFind\Db\Table\User' => 'VuFind\Db\Table\UserFactory', - 'VuFind\Db\Table\UserCard' => 'VuFind\Db\Table\GatewayFactory', - 'VuFind\Db\Table\UserList' => 'VuFind\Db\Table\UserListFactory', - 'VuFind\Db\Table\UserResource' => 'VuFind\Db\Table\GatewayFactory', + AuthHash::class => GatewayFactory::class, + ChangeTracker::class => GatewayFactory::class, + Comments::class => GatewayFactory::class, + ExternalSession::class => GatewayFactory::class, + OaiResumption::class => GatewayFactory::class, + Record::class => GatewayFactory::class, + Resource::class => ResourceFactory::class, + ResourceTags::class => CaseSensitiveTagsFactory::class, + Search::class => GatewayFactory::class, + Session::class => GatewayFactory::class, + Shortlinks::class => GatewayFactory::class, + Tags::class => CaseSensitiveTagsFactory::class, + User::class => UserFactory::class, + UserCard::class => GatewayFactory::class, + UserList::class => UserListFactory::class, + UserResource::class => GatewayFactory::class, ]; /** @@ -94,7 +97,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Db\Table\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -106,6 +109,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Db\Table\Gateway'; + return Gateway::class; } } diff --git a/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php b/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php index 02e7c52f4c69fa28d9e3fd2bc46ca0f7711a273b..fd0d0ce7f8129395a1b7e8b3554a9896ffa8d54f 100644 --- a/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/ResourceFactory.php @@ -60,8 +60,8 @@ class ResourceFactory extends GatewayFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $converter = $container->get('VuFind\Date\Converter'); - $loader = $container->get('VuFind\Record\Loader'); + $converter = $container->get(\VuFind\Date\Converter::class); + $loader = $container->get(\VuFind\Record\Loader::class); return parent::__invoke($container, $requestedName, [$converter, $loader]); } } diff --git a/module/VuFind/src/VuFind/Db/Table/Search.php b/module/VuFind/src/VuFind/Db/Table/Search.php index bfa6ed443c0baa7b087c8d8e63de2e4a88362087..8fc21a40816afd88e8e9707aa1f774fddd17801b 100644 --- a/module/VuFind/src/VuFind/Db/Table/Search.php +++ b/module/VuFind/src/VuFind/Db/Table/Search.php @@ -212,6 +212,21 @@ class Search extends Gateway return $this->select($callback)->current(); } + /** + * Get scheduled searches. + * + * @return array Array of VuFind\Db\Row\Search objects. + */ + public function getScheduledSearches() + { + $callback = function ($select) { + $select->where->equalTo('saved', 1); + $select->where->greaterThan('notification_frequency', 0); + $select->order('user_id'); + }; + return $this->select($callback); + } + /** * Add a search into the search table (history) * diff --git a/module/VuFind/src/VuFind/Db/Table/Shortlinks.php b/module/VuFind/src/VuFind/Db/Table/Shortlinks.php new file mode 100644 index 0000000000000000000000000000000000000000..e241ad8a8ecf9f4f007f87418d6d72f0987737bb --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Table/Shortlinks.php @@ -0,0 +1,58 @@ +<?php +/** + * Table Definition for shortlinks + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Db\Table; + +use VuFind\Db\Row\RowGateway; +use Zend\Db\Adapter\Adapter; + +/** + * Table Definition for shortlinks + * + * @category VuFind + * @package Db_Table + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Shortlinks extends Gateway +{ + /** + * Constructor + * + * @param Adapter $adapter Database adapter + * @param PluginManager $tm Table manager + * @param array $cfg Zend Framework configuration + * @param RowGateway $rowObj Row prototype object (null for default) + * @param string $table Name of database table to interface with + */ + public function __construct(Adapter $adapter, PluginManager $tm, $cfg, + RowGateway $rowObj = null, $table = 'shortlinks' + ) { + parent::__construct($adapter, $tm, $cfg, $rowObj, $table); + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/User.php b/module/VuFind/src/VuFind/Db/Table/User.php index 17fb1a3c6114d7fd1d0ce5841c56c7ff47e06f97..c5dae8da258ffff918fccf2080c132a5c816cc5b 100644 --- a/module/VuFind/src/VuFind/Db/Table/User.php +++ b/module/VuFind/src/VuFind/Db/Table/User.php @@ -90,9 +90,24 @@ class User extends Gateway $row = $this->createRow(); $row->username = $username; $row->created = date('Y-m-d H:i:s'); + // Failing to initialize this here can cause Zend\Db errors in + // the VuFind\Auth\Shibboleth and VuFind\Auth\ILS integration tests. + $row->user_provided_email = 0; return $row; } + /** + * Retrieve a user object from the database based on ID. + * + * @param string $id ID. + * + * @return UserRow + */ + public function getById($id) + { + return $this->select(['id' => $id])->current(); + } + /** * Retrieve a user object from the database based on catalog ID. * diff --git a/module/VuFind/src/VuFind/Db/Table/UserFactory.php b/module/VuFind/src/VuFind/Db/Table/UserFactory.php index 4cbe25991cab9b5409df449ad27c30ecf9d904b4..9ce70b8f683584d67b2dec8b4ce8e976cfb1bf59 100644 --- a/module/VuFind/src/VuFind/Db/Table/UserFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/UserFactory.php @@ -57,12 +57,13 @@ class UserFactory extends GatewayFactory public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $session = null; if (isset($config->Authentication->privacy) && $config->Authentication->privacy ) { - $sessionManager = $container->get('Zend\Session\SessionManager'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); $session = new \Zend\Session\Container('Account', $sessionManager); } return parent::__invoke($container, $requestedName, [$config, $session]); diff --git a/module/VuFind/src/VuFind/Db/Table/UserListFactory.php b/module/VuFind/src/VuFind/Db/Table/UserListFactory.php index 5fea27b4880b5e84192dc7d410ec4c390c51de9d..ba43dc99ea2f941d65caebfd91b04118ba227ada 100644 --- a/module/VuFind/src/VuFind/Db/Table/UserListFactory.php +++ b/module/VuFind/src/VuFind/Db/Table/UserListFactory.php @@ -60,7 +60,7 @@ class UserListFactory extends GatewayFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $sessionManager = $container->get('Zend\Session\SessionManager'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); $session = new \Zend\Session\Container('List', $sessionManager); return parent::__invoke($container, $requestedName, [$session]); } diff --git a/module/VuFind/src/VuFind/DigitalContent/OverdriveConnector.php b/module/VuFind/src/VuFind/DigitalContent/OverdriveConnector.php new file mode 100644 index 0000000000000000000000000000000000000000..3e4ebfaf8a57f4f950ce3078e2ce4c3cad61ec7b --- /dev/null +++ b/module/VuFind/src/VuFind/DigitalContent/OverdriveConnector.php @@ -0,0 +1,1540 @@ +<?php +/** + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * @category VuFind + * @package DigitalContent + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\DigitalContent; + +use Exception; +use VuFind\Auth\ILSAuthenticator; +use VuFind\Cache\KeyGeneratorTrait; +use Zend\Cache\Storage\StorageInterface; +use Zend\Config\Config; +use Zend\Http\Client; +use Zend\Log\LoggerAwareInterface; +use Zend\Session\Container; +use ZfcRbac\Service\AuthorizationServiceAwareInterface; +use ZfcRbac\Service\AuthorizationServiceAwareTrait; + +/** + * OverdriveConnector + * + * Class responsible for connecting to the Overdrive API + * + * @category VuFind + * @package DigitalContent + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + * @todo provide option for autocheckout by default in config + * allow override for cover display using other covers + * provide option for not requiring email for holds + * provide option for giving users option for every hold + * provide option for asking about autocheckout for every hold + * provide config options for how to handle patrons with no access to OD + */ +class OverdriveConnector implements LoggerAwareInterface, + AuthorizationServiceAwareInterface, \VuFindHttp\HttpServiceAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait { + logError as error; + } + use AuthorizationServiceAwareTrait; + use \VuFindHttp\HttpServiceAwareTrait; + use KeyGeneratorTrait; + + /** + * Session Container + * + * @var Container + */ + protected $sessionContainer; + + /** + * Record Config + * + * Main configurations + * + * @var Config + */ + protected $recordConfig; + + /** + * Record Config + * + * Overdrive configurations + * + * @var Config + */ + protected $mainConfig; + + /** + * ILS Authorization + * + * @var ILSAuthenticator + */ + protected $ilsAuth; + + /** + * HTTP Client + * + * Client for making calls to the API + * + * @var Client + */ + protected $client; + + /** + * Cache for storing ILS data temporarily (e.g. patron blocks) + * + * @var StorageInterface + */ + protected $cache = null; + + /** + * Constructor + * + * @param Config $mainConfig VuFind main conf + * @param Config $recordConfig Record-specific conf file + * @param ILSAuthenticator $ilsAuth ILS Authenticator + * @param Container $sessionContainer container + */ + public function __construct( + Config $mainConfig, + Config $recordConfig, + ILSAuthenticator $ilsAuth, + Container $sessionContainer = null + ) { + $this->mainConfig = $mainConfig; + $this->recordConfig = $recordConfig; + $this->ilsAuth = $ilsAuth; + $this->sessionContainer = $sessionContainer; + } + + /** + * Loads the session container + * + * @return \Zend\Session\Container + */ + protected function getSessionContainer() + { + if (null === $this->sessionContainer || !$this->sessionContainer) { + error_log("NO SESSION CONTAINER"); + } + return $this->sessionContainer; + } + + /** + * Get (Logged-in) User + * + * Returns the currently logged in user or false if the user is not + * + * @return array|boolean an array of user info from the ILSAuthenticator + * or false if user is not logged in. + */ + public function getUser() + { + try { + $user = $this->ilsAuth->storedCatalogLogin(); + } catch (ILSException $e) { + return false; + } + return $user; + } + + /** + * Get Overdrive Access + * + * Whether the patron has access to overdrive actions (hold, + * checkout etc.). + * This is stored and retrieved from the session. + * + * @param bool $refresh Force a check instead of checking cache + * + * @return object + */ + public function getAccess($refresh = false) + { + if (!$user = $this->getUser()) { + return $this->getResultObject(false, "User not logged in."); + } + + $odAccess = $this->getSessionContainer()->odAccess; + if ($refresh || empty($odAccess)) { + if ($this->connectToPatronAPI( + $user["cat_username"], + $user["cat_password"], true + ) + ) { + $this->getSessionContainer()->odAccess + = $this->getResultObject(true); + } else { + $result = $this->getResultObject(); + //there is some problem with the account + $result->code = "od_account_problem"; + $conf = $this->getConfig(); + + if ($conf->noAccessString) { + if (strpos( + $this->getSessionContainer()->odAccessMessage, + $conf->noAccessString + ) !== false + ) { + //this user should not have access to OD + $result->code = "od_account_noaccess"; + } + } + //odAccessMessage is set in the session by the API call above + //maybe it should be saved to a class property instead + $result->msg = $this->getSessionContainer()->odAccessMessage; + $this->getSessionContainer()->odAccess = $result; + } + } else { + $result = $this->getSessionContainer()->odAccess; + } + + return $result; + } + + /** + * Get Availability + * + * Retrieves the availability for a single resource from Overdrive API + * with information like copiesOwned, copiesAvailable, numberOfHolds et. + * + * @param string $overDriveId The Overdrive ID (reserve ID) of the eResource + * + * @return object Standard object with availability info + * + * @link https://developer.overdrive.com/apis/library-availability-new + */ + public function getAvailability($overDriveId) + { + $result = $this->getResultObject(); + if (!$overDriveId) { + $this->logWarning("no overdrive content ID was passed in."); + return $result; + } + + if ($conf = $this->getConfig()) { + $collectionToken = $this->getCollectionToken(); + //hmm. no token. if user is logged in let's check access + if (!$collectionToken && $user = $this->getUser()) { + $accessResult = $this->getAccess(); + if (!$accessResult->status) { + return $accessResult; + } + } + $baseUrl = $conf->discURL; + $availabilityUrl + = "$baseUrl/v2/collections/$collectionToken/products/"; + $availabilityUrl .= "$overDriveId/availability"; + $res = $this->callUrl($availabilityUrl); + + if ($res->errorCode == "NotFound") { + if ($conf->consortiumSupport && !$this->getUser()) { + //consortium support is turned on but user is not logged in; + //if the title is not found it probably means that it's only + //available to some users. + $result->status = true; + $result->code = 'od_code_login_for_avail'; + } else { + $result->status = false; + $this->logWarning("resource not found: $overDriveId"); + } + } else { + $result->status = true; + $result->data = $res; + } + } + + return $result; + } + + /** + * Get Availability (in) Bulk + * + * Gets availability for up to 25 titles at once. This is used by the + * the ajax availability system + * + * @param array $overDriveIds The Overdrive ID (reserve IDs) of the + * eResources + * + * @return array|bool see getAvailability + * + * @todo if more tan 25 passed in, make multiple calls + */ + public function getAvailabilityBulk($overDriveIds = []) + { + $result = $this->getResultObject(); + $loginRequired = false; + if (count($overDriveIds) < 1) { + $this->logWarning("no overdrive content ID was passed in."); + return false; + } + + if ($conf = $this->getConfig()) { + if ($conf->consortiumSupport && !$this->getUser()) { + $loginRequired = true; + } + $collectionToken = $this->getCollectionToken(); + //hmm. no token. if user is logged in let's check access + if (!$collectionToken && $user = $this->getUser()) { + $accessResult = $this->getAccess(); + if (!$accessResult->status) { + return $accessResult; + } + } + $baseUrl = $conf->discURL; + $availabilityPath = "/v2/collections/"; + $availabilityPath .= "$collectionToken/availability?products="; + $availabilityUrl = $baseUrl . $availabilityPath . + implode(",", $overDriveIds); + $res = $this->callUrl($availabilityUrl); + if (!$res) { + $result->code = 'od_code_connection_failed'; + } else { + if ($res->errorCode == "NotFound" || $res->totalItems == 0) { + if ($loginRequired) { + //consortium support is turned on but user is + //not logged in + //if the title is not found it could mean that it's only + //available to some users. + $result->status = true; + $result->code = 'od_code_login_for_avail'; + } else { + $result->status = false; + $this->logWarning("resources not found"); + } + } else { + $result->status = true; + foreach ($res->availability as $item) { + $this->debug("item:" . print_r($item, true)); + $result->data[strtolower($item->reserveId)] = $item; + } + //now look for items not returned + foreach ($overDriveIds as $id) { + if (!isset($result->data[$id])) { + if ($loginRequired) { + $result->data[$id]->code + = 'od_code_login_for_avail'; + } else { + $result->data[$id]->code + = 'od_code_resource_not_found'; + } + } + } + } + } + } + return $result; + } + + /** + * Get Colllection Token + * + * Gets the colleciton token for the Overdrive collection. The collection + * token doesn't change much but according to + * the OD API docs it could change and should be retrieved each session. + * Also, the collection token depends on the user if the user is in a + * consortium. If consortium support is turned on then the user collection + * token will override the library collection token. + * The token itself is returned but it's also saved in the session and + * automatically returned. + * + * @return object|bool A collection token for the library's collection. + */ + public function getCollectionToken() + { + $collectionToken = $this->getCachedData("collectionToken"); + $userCollectionToken = $this->getSessionContainer( + )->userCollectionToken; + $this->debug("collectionToken from cache: $collectionToken"); + $this->debug("userCollectionToken from session: $userCollectionToken"); + $conf = $this->getConfig(); + if ($conf->consortiumSupport && $user = $this->getUser()) { + if (empty($userCollectionToken)) { + $this->debug("getting new user collectionToken"); + $baseUrl = $conf->circURL; + $patronURL = "$baseUrl/v1/patrons/me"; + $res = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $patronURL + ); + if ($res) { + $userCollectionToken = $res->collectionToken; + $this->getSessionContainer()->userCollectionToken + = $userCollectionToken; + } else { + return false; + } + } + return $userCollectionToken; + } + if (empty($collectionToken)) { + $this->debug("getting new collectionToken"); + $baseUrl = $conf->discURL; + $libraryID = $conf->libraryID; + $libraryURL = "$baseUrl/v1/libraries/$libraryID"; + $res = $this->callUrl($libraryURL); + if ($res) { + $collectionToken = $res->collectionToken; + $this->putCachedData("collectionToken", $collectionToken); + } else { + return false; + } + } + return $collectionToken; + } + + /** + * Overdrive Checkout + * Processes a request to checkout a title from Overdrive + * + * @param string $overDriveId The overdrive id for the title + * + * @return object $result Results of the call. + */ + public function doOverdriveCheckout($overDriveId) + { + $result = $this->getResultObject(); + + $this->debug("doOverdriveCheckout: overdriveID: " . $overDriveId); + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $result; + } + if ($config = $this->getConfig()) { + $url = $config->circURL . '/v1/patrons/me/checkouts'; + $params = [ + 'reserveId' => $overDriveId, + ]; + + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url, $params, "POST" + ); + + if (!empty($response)) { + if (isset($response->reserveId)) { + $expires = ""; + if ($dt = new \DateTime($response->expires)) { + $expires = $dt->format( + (string)$config->displayDateFormat + ); + } + $result->status = true; + $result->data->expires = $expires; + $result->data->formats = $response->formats; + //add the checkout to the session cache + $this->getSessionContainer()->checkouts[] = $response; + } else { + $result->msg = $response->message; + } + } else { + $result->code = 'od_code_connection_failed'; + } + } + return $result; + } + + /** + * Places a hold on an item within OverDrive + * + * @param string $overDriveId The overdrive id for the title + * @param string $email The email overdrive should use for notif + * + * @return \stdClass Object with result + */ + public function placeOverDriveHold($overDriveId, $email) + { + $this->debug("placeOverdriveHold"); + $holdResult = $this->getResultObject(); + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $holdResult; + } + + if ($config = $this->getConfig()) { + $autoCheckout = true; + $ignoreHoldEmail = false; + $url = $config->circURL . '/v1/patrons/me/holds'; + $params = [ + 'reserveId' => $overDriveId, + 'emailAddress' => $email, + 'autoCheckout' => $autoCheckout, + 'ignoreHoldEmail' => $ignoreHoldEmail, + ]; + + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], + $url, + $params, + "POST" + ); + + if (!empty($response)) { + if (isset($response->holdListPosition)) { + $holdResult->status = true; + $holdResult->data->holdListPosition + = $response->holdListPosition; + } else { + $holdResult->msg = $response->message; + } + } else { + $holdResult->code = 'od_code_connection_failed'; + } + } + return $holdResult; + } + + /** + * Cancel Hold + * Cancel and existing Overdrive Hold + * + * @param string $overDriveId The overdrive id for the title + * + * @return \stdClass Object with result + */ + public function cancelHold($overDriveId) + { + $holdResult = $this->getResultObject(); + $this->debug("OverdriveConnector: cancelHold"); + //$this->debug(print_r($user,true)); + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $holdResult; + } + if ($config = $this->getConfig()) { + $url = $config->circURL . "/v1/patrons/me/holds/$overDriveId"; + $response = $this->callPatronUrl( + $user["cat_username"], $user["cat_password"], $url, null, + "DELETE" + ); + + //because this is a DELETE Call, we are just looking for a boolean + if ($response) { + $holdResult->status = true; + } else { + $holdResult->msg = $response->message; + } + } + return $holdResult; + } + + /** + * Return Resource + * Return a title early. + * + * @param string $resourceID Overdrive ID of the resource + * + * @return object|bool Object with result + */ + public function returnResource($resourceID) + { + $result = $this->getResultObject(); + //$this->debug(print_r($user,true)); + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $result; + } + if ($config = $this->getConfig()) { + $url = $config->circURL . "/v1/patrons/me/checkouts/$resourceID"; + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url, null, "DELETE" + ); + + //because this is a DELETE Call, we are just looking for a boolean + if ($response) { + $result->status = true; + } else { + $result->msg = $response->message; + } + } + return $result; + } + + /** + * Get Download Link for an Overdrive Resource + * + * @param string $overDriveId Overdrive ID + * @param string $format Overdrive string for this format + * @param string $errorURL A URL to show err if the download doesn't wk + * + * @return object Object with result. If successful, then data will + * have the download URI ($result->downloadLink) + */ + public function getDownloadLink($overDriveId, $format, $errorURL) + { + $this->debug("getDownloadLink: id: $overDriveId, $format"); + $result = $this->getResultObject(); + $downloadLink = false; + //$this->debug(print_r($user,true)); + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $result; + } + $checkout = $this->getCheckout($overDriveId, false); + + //either they are requesting a format that is always avail + //or it is locked in and they are requesting the format that + //is already locked in. + if ($template = $this->getLinkTemplate($checkout, $format)) { + $this->debug("template: " . print_r($template, true)); + $downloadLink = $template->downloadLinkV2->href; + $this->debug("found the link: $downloadLink"); + } elseif (!$checkout->isFormatLockedIn) { + //if we get this far, and the checkout is not locked in, then we should + //lock it in and try again + + $lockinResult = $this->lockinResource($overDriveId, $format); + if ($lockinResult->status) { + $downloadLink + = $lockinResult->data->linkTemplates->downloadLink->href; + $this->debug("(locked in. found the link: $downloadLink"); + } else { + $result->msg = $lockinResult->msg; + } + } else { + //the checkout is locked in but we didn't find the template + //for this format, means that they are requesting the wrong + //format for the locked-in resource. + $result->msg + = "The title appears to be already locked in for a different format"; + $result->status = false; + $this->debug("locked in for another format."); + return $result; + } + + if ($downloadLink) { + $this->debug("dll true"); + $url = str_replace("{errorurl}", $errorURL, $downloadLink); + $url = str_replace("{errorpageurl}", $errorURL, $url); + $url = str_replace("{successurl}", $errorURL, $url); + $this->debug("getting download link using: $url"); + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url, null, "GET" + ); + + if (!empty($response)) { + if (isset($response->links)) { + $result->status = true; + $result->data->downloadLink + = $response->links->contentlink->href; + } else { + $this->debug("problem getting link:" . $response->message); + $result->msg + = "Could not get download link for resourceID " + . "[$overDriveId]: " . $response->message; + } + } else { + $result->code = 'od_code_connection_failed'; + } + } else { + $this->debug("dll false"); + } + return $result; + } + + /** + * Returns the link template for this format + * + * @param object $checkout The checkout object + * @param string $format The name of the format to check + * + * @return bool + */ + protected function getLinkTemplate($checkout, $format) + { + foreach ($checkout->formats as $f) { + if ($f->formatType == $format) { + return $f->linkTemplates; + } + } + return false; + } + + /** + * Lock In Overdrive Resource for a particular format + * + * @param string $overDriveId Overdrive Resource ID + * @param string $format Overdrive string for the format + * + * @return object|bool Result of the call. + */ + public function lockinResource($overDriveId, $format) + { + $this->debug("OverdriveConnector: lockinResource, format: $format"); + $result = $this->getResultObject(); + + if (!$user = $this->getUser()) { + $this->error("user is not logged in", false, true); + return $result; + } + //shouldn't need to refresh. This should be in the cache if it exists + $checkout = $this->getCheckout($overDriveId, false); + if (!$checkout) { + $result->msg + = "Could not find a checkout for this resource ID for + this user."; + $this->debug("title not checked out."); + return $result; + } + //doublecheck this format is an option. + $availableFormats = []; + //$params = array(); + foreach ($checkout->actions->format->fields as $field) { + if ($field->name == 'formatType') { + $availableFormats = $field->options; + } + } + if (!in_array($format, $availableFormats)) { + $result->msg + = "Could not lock in Overdrive resourceID [$overDriveId]:" . + " This format ($format) doesn't appear to be available " . + "for this resource."; + return $result; + } else { + $params = [ + 'reserveId' => $overDriveId, 'formatType' => $format + ]; + } + + if ($config = $this->getConfig()) { + $url = $config->circURL + . "/v1/patrons/me/checkouts/$overDriveId/formats"; + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url, $params, "POST" + ); + + if (!empty($response)) { + if (isset($response->linkTemplates)) { + $result->status = true; + $result->data->linkTemplates = $response->linkTemplates; + $this->debug("title locked in:"); + } else { + $result->msg + = "Could not lock in Overdrive resourceID [$overDriveId]: " + . $response->message; + } + } else { + $result->code = 'od_code_connection_failed'; + } + } + + return $result; + } + + /** + * Get Configuration + * Sets up a local copy of configurations for convenience + * + * @return bool|\stdClass + */ + public function getConfig() + { + $conf = new \stdClass(); + if (!$this->recordConfig) { + $this->error( + "Could not locate the Overdrive Record Driver " + . "configuration." + ); + return false; + } + if ($this->recordConfig->API->productionMode == false) { + $conf->productionMode = false; + $conf->discURL = $this->recordConfig->API->integrationDiscoveryURL; + $conf->circURL = $this->recordConfig->API->integrationCircURL; + $conf->libraryID = $this->recordConfig->API->integrationLibraryID; + $conf->websiteID = $this->recordConfig->API->integrationWebsiteID; + } else { + $conf->productionMode = true; + $conf->discURL = $this->recordConfig->API->productionDiscoveryURL; + $conf->circURL = $this->recordConfig->API->productionCircURL; + $conf->libraryID = $this->recordConfig->API->productionLibraryID; + $conf->websiteID = $this->recordConfig->API->productionWebsiteID; + } + + $conf->clientKey = $this->recordConfig->API->clientKey; + $conf->clientSecret = $this->recordConfig->API->clientSecret; + $conf->tokenURL = $this->recordConfig->API->tokenURL; + $conf->patronTokenURL = $this->recordConfig->API->patronTokenURL; + $conf->idField = $this->recordConfig->Overdrive->overdriveIdMarcField; + $conf->idSubfield + = $this->recordConfig->Overdrive->overdriveIdMarcSubfield; + $conf->ILSname = $this->recordConfig->API->ILSname; + $conf->isMarc = $this->recordConfig->Overdrive->isMarc; + $conf->displayDateFormat = $this->mainConfig->Site->displayDateFormat; + $conf->consortiumSupport + = $this->recordConfig->Overdrive->consortiumSupport; + $conf->showMyContent + = strtolower($this->recordConfig->Overdrive->showMyContent); + $conf->noAccessString = $this->recordConfig->Overdrive->noAccessString; + $admin = $this->recordConfig->Overdrive->showOverdriveAdminMenu ?? false; + $conf->showOverdriveAdminMenu + = (strtolower($admin) === 'false') ? false : $admin; + $conf->tokenCacheLifetime + = $this->recordConfig->API->tokenCacheLifetime; + return $conf; + } + + /** + * Returns an array of Overdrive Formats and translation tokens + * + * @return array + */ + public function getFormatNames() + { + return [ + 'ebook-kindle' => "od_ebook-kindle", + 'ebook-overdrive' => "od_ebook-overdrive", + 'ebook-epub-adobe' => "od_ebook-epub-adobe", + 'ebook-epub-open' => "od_ebook-epub-open", + 'ebook-pdf-adobe' => "od_ebook-pdf-adobe", + 'ebook-pdf-open' => "od_ebook-pdf-open", + 'ebook-mediado' => "od_ebook-mediado", + 'audiobook-overdrive' => "od_audiobook-overdrive", + 'audiobook-mp3' => "od_audiobook-mp3", + 'video-streaming' => "od_video-streaming", + ]; + } + + /** + * Returns a hash of metadata keyed on overdrive reserveID + * + * @param array $overDriveIds Set of Overdrive IDs + * + * @return array results of metadata fetch + * + * @todo if more tan 25 passed in, make multiple calls + */ + public function getMetadata($overDriveIds = []) + { + $metadata = []; + if (!$overDriveIds || count($overDriveIds) < 1) { + $this->logWarning("no overdrive content IDs were passed in."); + return []; + } + if ($conf = $this->getConfig()) { + $productsKey = $this->getCollectionToken(); + $baseUrl = $conf->discURL; + $metadataUrl = "$baseUrl/v1/collections/$productsKey/"; + $metadataUrl .= "bulkmetadata?reserveIds=" . implode( + ",", $overDriveIds + ); + $res = $this->callUrl($metadataUrl); + $md = $res->metadata; + foreach ($md as $item) { + $metadata[$item->id] = $item; + } + } + return $metadata; + } + + /** + * Get Overdrive Checkout + * + * Get the overdrive checkout object for an overdrive title + * for the current user + * + * @param string $overDriveId Overdrive resource id + * @param bool $refresh Whether or not to ignore cache and get latest + * + * @return object|false PHP object that represents the checkout or false + * the checkout is not in the current list of checkouts for the current + * user. + */ + public function getCheckout($overDriveId, $refresh = true) + { + $this->debug("get Overdrive checkout"); + $result = $this->getCheckouts($refresh); + if ($result->status) { + $checkouts = $result->data; + foreach ($checkouts as $checkout) { + if (strtolower($checkout->reserveId) == strtolower( + $overDriveId + ) + ) { + return $checkout; + } + } + return false; + } else { + return false; + } + } + + /** + * Get Overdrive Hold + * + * Get the overdrive hold object for an overdrive title + * for the current user + * + * @param string $overDriveId Overdrive resource id + * @param bool $refresh Whether or not to ignore cache and get latest + * + * @return object|false PHP object that represents the checkout or false + * the checkout is not in the current list of checkouts for the current + * user. + */ + public function getHold($overDriveId, $refresh = true) + { + $this->debug("get Overdrive hold"); + $result = $this->getHolds($refresh); + if ($result->status) { + $holds = $result->data; + foreach ($holds as $hold) { + if (strtolower($hold->reserveId) == strtolower($overDriveId)) { + $this->debug("hold found"); + return $hold; + } + } + return false; + } else { + return false; + } + } + + /** + * Get Overdrive Checkouts (or a user) + * + * @param bool $refresh Whether or not to ignore cache and get latest + * + * @return object Results of the call + */ + public function getCheckouts($refresh = true) + { + //the checkouts are cached in the session, but we can force a refresh + $this->debug("get Overdrive Checkouts"); + // $this->debug(print_r($user,true)); + $result = $this->getResultObject(); + + if (!$user = $this->getUser()) { + $this->error("user is not logged in"); + return $result; + } + + $checkouts = $this->getSessionContainer()->checkouts; + if (!$checkouts || $refresh) { + if ($config = $this->getConfig()) { + $url = $config->circURL . '/v1/patrons/me/checkouts'; + + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url, false + ); + if (!empty($response)) { + $result->status = true; + $result->message = ''; + $result->data = $response->checkouts; + //Convert dates to desired format + foreach ($response->checkouts as $key => $checkout) { + $coExpires = new \DateTime($checkout->expires); + $result->data[$key]->expires = $coExpires->format( + $config->displayDateFormat + ); + $result->data[$key]->isReturnable + = !$checkout->isFormatLockedIn; + } + + $this->getSessionContainer()->checkouts + = $response->checkouts; + } else { + $accessResult = $this->getAccess(); + return $accessResult; + //$result->code = 'od_code_connection_failed'; + } + } + } else { + $this->debug("found Overdrive Checkouts in session"); + $result->status = true; + $result->msg = []; + $result->data = $this->getSessionContainer()->checkouts; + } + + return $result; + } + + /** + * Get Overdrive Holds (or a user) + * + * @param bool $refresh Whether or not to ignore cache and get latest + * + * @return \stdClass Results of the call + */ + public function getHolds($refresh = true) + { + $this->debug("get Overdrive Holds"); + $result = $this->getResultObject(); + if (!$user = $this->getUser()) { + $this->error("user is not logged in"); + return $result; + } + + $holds = $this->getSessionContainer()->holds; + if (!$holds || $refresh) { + if ($config = $this->getConfig()) { + $url = $config->circURL . '/v1/patrons/me/holds'; + + $response = $this->callPatronUrl( + $user["cat_username"], + $user["cat_password"], $url + ); + + $result->status = false; + $result->message = ''; + + if (!empty($response)) { + $result->status = true; + $result->message = 'hold_place_success_html'; + $result->data = $response->holds; + //Check for holds ready for chechout + foreach ($response->holds as $key => $hold) { + if (!$hold->autoCheckout + && $hold->holdListPosition == 1 + ) { + $result->data[$key]->holdReadyForCheckout = true; + //format the expires date. + $holdExpires = new \DateTime($hold->holdExpires); + $result->data[$key]->holdExpires + = $holdExpires->format( + (string)$config->displayDateFormat + ); + } + $holdPlacedDate = new \DateTime($hold->holdPlacedDate); + $result->data[$key]->holdPlacedDate + = $holdPlacedDate->format( + (string)$config->displayDateFormat + ); + } + $this->getSessionContainer()->holds = $response->holds; + } else { + $result->code = 'od_code_connection_failed'; + } + } + } else { + $this->debug("found Overdrive Holds in cache"); + $result->status = true; + $result->message = []; + $result->data = $this->getSessionContainer()->holds; + } + return $result; + } + + /** + * Call a URL on the API + * + * @param string $url The url to call + * @param array $headers Headers to set for the request. + * if null, then the auth headers are used. + * @param bool $checkToken Whether to check and get a new token + * @param string $requestType The request type (GET, POST etc) + * + * @return object|bool The json response from the API call + * converted to an object. If the call fails at the + * HTTP level then the error is logged and false is returned. + */ + protected function callUrl( + $url, $headers = null, $checkToken = true, $requestType = "GET" + ) { + $this->debug("chktoken: $checkToken"); + if (!$checkToken || $this->connectToAPI()) { + $tokenData = $this->getSessionContainer()->tokenData; + $this->debug("url for OD API Call: $url"); + try { + $client = $this->getHttpClient($url); + } catch (Exception $e) { + $this->error( + "error while setting up the client: " . $e->getMessage() + ); + return false; + } + if ($headers === null) { + $headers = [ + "Authorization: {$tokenData->token_type} " . + "{$tokenData->access_token}", "User-Agent: VuFind" + ]; + } + $client->setHeaders($headers); + $client->setMethod($requestType); + $client->setUri($url); + try { + //throw new Exception('testException'); + $response = $client->send(); + } catch (Exception $ex) { + $this->error( + "Exception during request: " . + $ex->getMessage() + ); + return false; + } + + if ($response->isServerError()) { + $this->error( + "Overdrive HTTP Error: " . + $response->getStatusCode() + ); + $this->debug("Request: " . $client->getRequest()); + $this->debug("Response: " . $client->getResponse()); + return false; + } + + $body = $response->getBody(); + $returnVal = json_decode($body); + $this->debug( + "Return from OD API Call: " . print_r($returnVal, true) + ); + if ($returnVal != null) { + if (isset($returnVal->errorCode)) { + //In some cases, this should be returned perhaps... + $this->error("Overdrive Error: " . $returnVal->errorCode); + return $returnVal; + } else { + return $returnVal; + } + } else { + $this->error( + "Overdrive Error: Nothing returned from API call." + ); + $this->debug( + "Body return from OD API Call: " . print_r($body, true) + ); + } + } + return false; + } + + /** + * Connect to API + * + * @param bool $forceNewConnection Force a new connection (get a new token) + * + * @return string token for the session or false + * if the token request failed + */ + protected function connectToAPI($forceNewConnection = false) + { + $this->debug("connecting to API"); + $conf = $this->getConfig(); + $tokenData = $this->getSessionContainer()->tokenData; + $this->debug("API Token from session: " . print_r($tokenData, true)); + if ($forceNewConnection || $tokenData == null + || !isset($tokenData->access_token) + || time() >= $tokenData->expirationTime + ) { + $authHeader = base64_encode( + $conf->clientKey . ":" . $conf->clientSecret + ); + $headers = [ + 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8', + "Authorization: Basic $authHeader" + ]; + + try { + $client = $this->getHttpClient(); + } catch (Exception $e) { + $this->error( + "error while setting up the client: " . $e->getMessage() + ); + return false; + } + $client->setHeaders($headers); + $client->setMethod("POST"); + $client->setRawBody("grant_type=client_credentials"); + $response = $client->setUri($conf->tokenURL)->send(); + + if ($response->isServerError()) { + $this->error( + "Overdrive HTTP Error: " . + $response->getStatusCode() + ); + $this->debug("Request: " . $client->getRequest()); + return false; + } + + $body = $response->getBody(); + $tokenData = json_decode($body); + $this->debug( + "TokenData returned from OD API Call: " . print_r( + $tokenData, true + ) + ); + if ($tokenData != null) { + if (isset($tokenData->errorCode)) { + //In some cases, this should be returned perhaps... + $this->error("Overdrive Error: " . $tokenData->errorCode); + return false; + } else { + $tokenData->expirationTime = time() + + $tokenData->expires_in; + $this->getSessionContainer()->tokenData = $tokenData; + return $tokenData; + } + } else { + $this->error( + "Overdrive Error: Nothing returned from API call." + ); + $this->debug( + "Body return from OD API Call: " . print_r($body, true) + ); + } + } + return $tokenData; + } + + /** + * Call a Patron URL on the API + * + * The patron URL is used for the circulation API's and requires a patron + * specific token. + * + * @param string $patronBarcode Patrons barcode + * @param string $patronPin Patrons password + * @param string $url The url to call + * @param array $params parameters to call + * @param string $requestType HTTP request type (default=GET) + * + * @return object|bool The json response from the API call + * converted to an object. If the call fails at the + * HTTP level then the error is logged and false is returned. + */ + protected function callPatronUrl( + $patronBarcode, $patronPin, $url, $params = null, $requestType = "GET" + ) { + $this->debug("calling patronURL: $url"); + if ($this->connectToPatronAPI($patronBarcode, $patronPin, false)) { + $patronTokenData = $this->getSessionContainer()->patronTokenData; + $authorizationData = $patronTokenData->token_type . + ' ' . $patronTokenData->access_token; + $headers = [ + "Authorization: $authorizationData", + "User-Agent: VuFind", + "Content-Type: application/json" + ]; + try { + $client = $this->getHttpClient(); + } catch (Exception $e) { + $this->error( + "error while setting up the client: " . $e->getMessage() + ); + return false; + } + $client->setHeaders($headers); + $client->setMethod($requestType); + $client->setUri($url); + if ($params != null) { + $jsonData = ['fields' => []]; + foreach ($params as $key => $value) { + $jsonData['fields'][] = [ + 'name' => $key, + 'value' => $value + ]; + } + $postData = json_encode($jsonData); + $client->setRawBody($postData); + } + $this->debug("patronURL data sent: $postData"); + $this->debug("patronURL method: " . $client->getMethod()); + $this->debug("client: " . $client->getRequest()); + try { + $response = $client->send(); + } catch (Exception $ex) { + $this->error( + "Exception during request: " . + $ex->getMessage() + ); + return false; + } + $body = $response->getBody(); + + //if all goes well for DELETE, the code will be 204 + //and response is empty. + if ($requestType == "DELETE") { + if ($response->getStatusCode() == 204) { + $this->debug("DELETE Patron call appears to have worked."); + return true; + } else { + $this->error( + "DELETE Patron call failed. HTTP return code: " . + $response->getStatusCode() + ); + return false; + } + } + + $returnVal = json_decode($body); + $this->debug("response from call: " . print_r($returnVal, true)); + + if ($returnVal != null) { + if (!isset($returnVal->message) + || $returnVal->message != 'An unexpected error has occurred.' + ) { + return $returnVal; + } else { + $this->debug( + "Overdrive API problem: " . $returnVal->message + ); + } + } else { + $this->error( + "Overdrive Error: Nothing returned from API call." + ); + return false; + } + } else { + $this->error("Overdrive Error: Not connected to the Patron API."); + } + return false; + } + + /** + * Connect to Patron API + * + * @param string $patronBarcode Patrons barcode + * @param string $patronPin Patrons password + * @param bool $forceNewConnection force a new connection (get a new + * token) + * + * @return string token for the session + */ + protected function connectToPatronAPI( + $patronBarcode, + $patronPin = '1234', + $forceNewConnection = false + ) { + $patronTokenData = $this->getSessionContainer()->patronTokenData; + $config = $this->getConfig(); + if ($forceNewConnection + || $patronTokenData == null + || ($patronTokenData->expirationTime + && time() >= $patronTokenData->expirationTime) + ) { + $this->debug("connecting to patron API for new token."); + $url = $config->patronTokenURL; + $websiteId = $config->websiteID; + $ilsname = $config->ILSname; + $authHeader = base64_encode( + $config->clientKey . ":" . $config->clientSecret + ); + $headers = [ + "Content-Type: application/x-www-form-urlencoded;charset=UTF-8", + "Authorization: Basic $authHeader", + "User-Agent: VuFind" + ]; + try { + $client = $this->getHttpClient($url); + } catch (Exception $e) { + $this->error( + "error while setting up the client: " . $e->getMessage() + ); + return false; + } + $client->setHeaders($headers); + $client->setMethod("POST"); + if ($patronPin == null) { + $postFields = "grant_type=password&username={$patronBarcode}"; + $postFields .= "&password=ignore&password_required=false"; + $postFields .= "&scope=websiteId:{$websiteId}%20"; + $postFields .= "authorizationname:{$ilsname}"; + } else { + $postFields = "grant_type=password&username={$patronBarcode}"; + $postFields .= "&password={$patronPin}&scope=websiteId"; + $postFields .= ":{$websiteId}%20authorizationname:{$ilsname}"; + } + $this->debug("patron API token data: $postFields"); + $client->setRawBody($postFields); + $response = $client->setUri($url)->send(); + $body = $response->getBody(); + $patronTokenData = json_decode($body); + + if (isset($patronTokenData->expires_in)) { + $patronTokenData->expirationTime = time() + + $patronTokenData->expires_in; + } else { + $this->debug( + "problem with OD patron API token Call: " . + print_r( + $patronTokenData, true + ) + ); + //if we have an unauthorized error, then we are going + //to cache that in the session so we don't keep making + //unnecessary calls, otherwise, just don't store the tokenData + //object so that it gets checked again next time + if ($patronTokenData->error == 'unauthorized_client') { + $this->getSessionContainer()->odAccessMessage + = $patronTokenData->error_description; + $this->getSessionContainer()->patronTokenData + = $patronTokenData; + } else { + $patronTokenData = null; + } + return false; + } + $this->getSessionContainer()->patronTokenData = $patronTokenData; + } + if (isset($patronTokenData->error)) { + return false; + } + return $patronTokenData; + } + + /** + * Get an HTTP client + * + * @param string $url URL for client to use + * + * @return \Zend\Http\Client + * @throws Exception + */ + protected function getHttpClient($url = null) + { + if (null === $this->httpService) { + throw new Exception('HTTP service missing.'); + } + if (!$this->client) { + $this->client = $this->httpService->createClient($url); + //set keep alive to true since we are sending to the same server + $this->client->setOptions(['keepalive', true]); + } + $this->client->resetParameters(); + return $this->client; + } + + /** + * Set a cache storage object. + * + * @param StorageInterface $cache Cache storage interface + * + * @return void + */ + public function setCacheStorage(StorageInterface $cache = null) + { + $this->cache = $cache; + } + + /** + * Helper function for fetching cached data. + * Data is cached for up to $this->cacheLifetime seconds so that it would + * be + * faster to process e.g. requests where multiple calls to the backend are + * made. + * + * @param string $key Cache entry key + * + * @return mixed|null Cached entry or null if not cached or expired + */ + protected function getCachedData($key) + { + // No cache object, no cached results! + if (null === $this->cache) { + return null; + } + $conf = $this->getConfig(); + $fullKey = $this->getCacheKey($key); + $item = $this->cache->getItem($fullKey); + $this->debug( + "pulling item from cache for key $key : " . $item['entry'] + ); + if (null !== $item) { + // Return value if still valid: + if (time() - $item['time'] < $conf->tokenCacheLifetime) { + return $item['entry']; + } + + // Clear expired item from cache: + $this->cache->removeItem($fullKey); + } + return null; + } + + /** + * Helper function for storing cached data. + * Data is cached for up to $this->cacheLifetime seconds so that it would + * be + * faster to process e.g. requests where multiple calls to the backend are + * made. + * + * @param string $key Cache entry key + * @param mixed $entry Entry to be cached + * + * @return void + */ + protected function putCachedData($key, $entry) + { + // Don't write to cache if we don't have a cache! + if (null === $this->cache) { + return; + } + $item = [ + 'time' => time(), + 'entry' => $entry + ]; + $this->debug("putting item from cache for key $key : $entry"); + $this->cache->setItem($this->getCacheKey($key), $item); + } + + /** + * Helper function for removing cached data. + * + * @param string $key Cache entry key + * + * @return void + */ + protected function removeCachedData($key) + { + // Don't write to cache if we don't have a cache! + if (null === $this->cache) { + return; + } + $this->cache->removeItem($this->getCacheKey($key)); + } + + /** + * Get Result Object + * + * @param bool $status Whether it succeeded + * @param string $msg More information + * @param string $code code used for end user display/translation + * + * @return object + */ + public function getResultObject($status = false, $msg = "", $code = "") + { + return (object)[ + 'status' => $status, + 'msg' => $msg, + 'data' => false, + 'code' => $code + ]; + } +} diff --git a/module/VuFind/src/VuFind/DigitalContent/OverdriveConnectorFactory.php b/module/VuFind/src/VuFind/DigitalContent/OverdriveConnectorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c3501ce710025ed37148c8bf436cea11e5f141ca --- /dev/null +++ b/module/VuFind/src/VuFind/DigitalContent/OverdriveConnectorFactory.php @@ -0,0 +1,97 @@ +<?php +/** + * Overdrive Connector factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * @category VuFind + * @package DigitalContent + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\DigitalContent; + +use Interop\Container\ContainerInterface; + +/** + * Generic Amazon content plugin factory. + * + * @category VuFind + * @package DigitalContent + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + */ +class OverdriveConnectorFactory implements + \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $odConfig = $container->get('VuFind\Config\PluginManager')->get( + 'Overdrive' + ); + $auth = $container->get('VuFind\Auth\ILSAuthenticator'); + $sessionContainer = null; + + if (PHP_SAPI !== 'cli') { + $sessionContainer = new \Zend\Session\Container( + 'DigitalContent\OverdriveController', + $container->get('Zend\Session\SessionManager') + ); + } + $connector = new $requestedName( + $config, $odConfig, $auth, $sessionContainer + ); + + // Populate cache storage + $connector->setCacheStorage( + $container->get('VuFind\Cache\Manager')->getCache( + 'object', "Overdrive" + ) + ); + + return $connector; + } +} diff --git a/module/VuFind/src/VuFind/DoiLinker/BrowZineFactory.php b/module/VuFind/src/VuFind/DoiLinker/BrowZineFactory.php index 4256b24dae4502e1b6201709904053ac8117fe62..42a6320c128a867e004dc330d4ab902b08ee542e 100644 --- a/module/VuFind/src/VuFind/DoiLinker/BrowZineFactory.php +++ b/module/VuFind/src/VuFind/DoiLinker/BrowZineFactory.php @@ -62,7 +62,8 @@ class BrowZineFactory implements \Zend\ServiceManager\Factory\FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $backend = $container->get('VuFind\Search\BackendManager')->get('BrowZine'); + $backend = $container->get(\VuFind\Search\BackendManager::class) + ->get('BrowZine'); return new $requestedName($backend->getConnector()); } } diff --git a/module/VuFind/src/VuFind/DoiLinker/PluginManager.php b/module/VuFind/src/VuFind/DoiLinker/PluginManager.php index 38dd88ccfcf10b9064dee8d80f6bf6eeba8c1832..55d0039b0f4325ea710ee927908575f063bcde5e 100644 --- a/module/VuFind/src/VuFind/DoiLinker/PluginManager.php +++ b/module/VuFind/src/VuFind/DoiLinker/PluginManager.php @@ -44,7 +44,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'browzine' => 'VuFind\DoiLinker\BrowZine', + 'browzine' => BrowZine::class, + 'unpaywall' => Unpaywall::class, ]; /** @@ -53,7 +54,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\DoiLinker\BrowZine' => 'VuFind\DoiLinker\BrowZineFactory', + BrowZine::class => BrowZineFactory::class, + Unpaywall::class => UnpaywallFactory::class, ]; /** @@ -64,6 +66,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\DoiLinker\DoiLinkerInterface'; + return DoiLinkerInterface::class; } } diff --git a/module/VuFind/src/VuFind/DoiLinker/Unpaywall.php b/module/VuFind/src/VuFind/DoiLinker/Unpaywall.php new file mode 100644 index 0000000000000000000000000000000000000000..8e0e53cb720019b39303efadf09707c73c4ed26f --- /dev/null +++ b/module/VuFind/src/VuFind/DoiLinker/Unpaywall.php @@ -0,0 +1,127 @@ +<?php +/** + * Unpaywall DOI linker + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package DOI + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:doi_linkers Wiki + */ +namespace VuFind\DoiLinker; + +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFindHttp\HttpServiceAwareInterface; + +/** + * Unpaywall DOI linker + * + * @category VuFind + * @package DOI + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:doi_linkers Wiki + */ +class Unpaywall implements DoiLinkerInterface, TranslatorAwareInterface, + HttpServiceAwareInterface +{ + use \VuFindHttp\HttpServiceAwareTrait; + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * URL to Unpaywall API + * + * @var string api url + */ + protected $apiUrl; + + /** + * E-mail used as parameter when calling API + * + * @var string email + */ + protected $email; + + /** + * Constructor + * + * @param \Zend\Config\Config $config DOI section of main VuFind config + * + * @throws \Exception + */ + public function __construct($config) + { + if (!isset($config->unpaywall_email)) { + throw new \Exception( + "Missing configuration for Unpaywall DOI linker: unpaywall_email" + ); + } + $this->email = $config->unpaywall_email; + $this->apiUrl = $config->unpaywall_api_url ?? "https://api.unpaywall.org/v2"; + } + + /** + * Given an array of DOIs, perform a lookup and return an associative array + * of arrays, keyed by DOI. Each array contains one or more associative arrays + * with required 'link' (URL to related resource) and 'label' (display text) + * keys and an optional 'icon' (URL to icon graphic) key. + * + * @param array $doiArray DOIs to look up + * + * @return array + */ + public function getLinks(array $doiArray) + { + $response = []; + foreach ($doiArray as $doi) { + $json = $this->callApi($doi); + if ($json === null) { + continue; + } + $data = json_decode($json, true); + if (!empty($data['best_oa_location']['url_for_pdf'])) { + $response[$doi][] = [ + 'link' => $data['best_oa_location']['url_for_pdf'], + 'label' => $this->translate('PDF Full Text'), + ]; + } + } + return $response; + } + + /** + * Takes a DOI and do an API call to Unpaywall service + * + * @param string $doi DOI + * + * @return null|string + */ + protected function callApi($doi) + { + $url = $this->apiUrl . "/" . urlencode($doi) . "?" + . http_build_query(['email' => $this->email]); + $client = $this->httpService->createClient($url); + $response = $client->send(); + if ($response->isSuccess()) { + return $response->getBody(); + } + return null; + } +} diff --git a/module/VuFind/src/VuFind/DoiLinker/UnpaywallFactory.php b/module/VuFind/src/VuFind/DoiLinker/UnpaywallFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..47fdfb817784baace97268f0a3d0abb9e5d3e59b --- /dev/null +++ b/module/VuFind/src/VuFind/DoiLinker/UnpaywallFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Unpaywall DOI linker factory + * + * PHP version 7 + * + * Copyright (C) Moravian library 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package DOI + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:doi_linkers Wiki + */ +namespace VuFind\DoiLinker; + +use Interop\Container\ContainerInterface; + +/** + * BrowZine DOI linker factory + * + * @category VuFind + * @package DOI + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:doi_linkers Wiki + */ +class UnpaywallFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config')->DOI; + return new $requestedName($config); + } +} diff --git a/module/VuFind/src/VuFind/Exception/AuthEmailNotVerified.php b/module/VuFind/src/VuFind/Exception/AuthEmailNotVerified.php new file mode 100644 index 0000000000000000000000000000000000000000..219028c8021b4a4b7d6f95060f54b690c2025d63 --- /dev/null +++ b/module/VuFind/src/VuFind/Exception/AuthEmailNotVerified.php @@ -0,0 +1,47 @@ +<?php +/** + * Unverified email address exception. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Exceptions + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Exception; + +/** + * Unverified email address exception. + * + * @category VuFind + * @package Exceptions + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AuthEmailNotVerified extends \VuFind\Exception\Auth +{ + /** + * User object with unverified email. + * + * @var \VuFind\Db\Row\User + */ + public $user; +} diff --git a/module/VuFind/src/VuFind/Exception/AuthInProgress.php b/module/VuFind/src/VuFind/Exception/AuthInProgress.php new file mode 100644 index 0000000000000000000000000000000000000000..31d6da3e6533a9779e4804069900db56958adad1 --- /dev/null +++ b/module/VuFind/src/VuFind/Exception/AuthInProgress.php @@ -0,0 +1,41 @@ +<?php +/** + * Authentication in Progress Exception + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Exceptions + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Exception; + +/** + * Authentication in Progress Exception + * + * @category VuFind + * @package Exceptions + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AuthInProgress extends Auth +{ +} diff --git a/module/VuFind/src/VuFind/Exception/BadConfig.php b/module/VuFind/src/VuFind/Exception/BadConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..06b4020fe174e9c559e7cc838cade97df45a1bb5 --- /dev/null +++ b/module/VuFind/src/VuFind/Exception/BadConfig.php @@ -0,0 +1,41 @@ +<?php +/** + * Bad Configuration Exception + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Exceptions + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Exception; + +/** + * Bad Configuration Exception + * + * @category VuFind + * @package Exceptions + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class BadConfig extends \Exception +{ +} diff --git a/module/VuFind/src/VuFind/Export.php b/module/VuFind/src/VuFind/Export.php index 27966018627315e18d18c812ba527feb5ae63922..bd95bfa6de1879b40495ac1c350419df8f223061 100644 --- a/module/VuFind/src/VuFind/Export.php +++ b/module/VuFind/src/VuFind/Export.php @@ -74,18 +74,6 @@ class Export $this->exportConfig = $exportConfig; } - /** - * Get bulk export options. - * - * @deprecated use getActiveFormats($context) instead - * - * @return array - */ - public function getBulkOptions() - { - return $this->getActiveFormats('bulk'); - } - /** * Get the URL for bulk export. * diff --git a/module/VuFind/src/VuFind/ExportFactory.php b/module/VuFind/src/VuFind/ExportFactory.php index fdbfbe68b0bab999753866a2dbe9473ae0818618..69f9b153050cec3c2f1f2117893f275929943024 100644 --- a/module/VuFind/src/VuFind/ExportFactory.php +++ b/module/VuFind/src/VuFind/ExportFactory.php @@ -62,8 +62,8 @@ class ExportFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config'), - $container->get('VuFind\Config\PluginManager')->get('export') + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\Config\PluginManager::class)->get('export') ); } } diff --git a/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php b/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php index 8ef1bf9226d5d70d91117ba14c8deb210092ded3..4e64951424adeb6ae530e383c2ef532c86e5b818 100644 --- a/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php +++ b/module/VuFind/src/VuFind/Favorites/FavoritesServiceFactory.php @@ -56,11 +56,11 @@ class FavoritesServiceFactory implements FactoryInterface */ public function __invoke(ContainerInterface $sm, $name, array $options = null) { - $tableManager = $sm->get('VuFind\Db\Table\PluginManager'); + $tableManager = $sm->get(\VuFind\Db\Table\PluginManager::class); return new FavoritesService( $tableManager->get('userlist'), $tableManager->get('resource'), - $sm->get('VuFind\Record\Cache') + $sm->get(\VuFind\Record\Cache::class) ); } } diff --git a/module/VuFind/src/VuFind/Form/Form.php b/module/VuFind/src/VuFind/Form/Form.php index 7da118a665772730fbd2ee36b2936df1a96aea24..867b7d83fa4f6cbf47aecc2e6b63f952fd9e761e 100644 --- a/module/VuFind/src/VuFind/Form/Form.php +++ b/module/VuFind/src/VuFind/Form/Form.php @@ -54,11 +54,14 @@ class Form extends \Zend\Form\Form implements protected $inputFilter; /** - * Validation messages + * Default, untranslated validation messages * * @var array */ - protected $messages; + protected $messages = [ + 'empty' => 'This field is required', + 'invalid_email' => 'Email address is invalid', + ]; /** * Default form config (from config.ini > Feedback) @@ -118,13 +121,6 @@ class Form extends \Zend\Form\Form implements throw new \VuFind\Exception\RecordMissing("Form '$formId' not found"); } - $this->messages = []; - $this->messages['empty'] - = $this->translate('This field is required'); - - $this->messages['invalid_email'] - = $this->translate('Email address is invalid'); - $this->formElementConfig = $this->parseConfig($formId, $config); @@ -220,7 +216,8 @@ class Form extends \Zend\Form\Form implements $element = []; $required = ['type', 'name']; - $optional = ['required', 'help','value', 'inputType', 'group']; + $optional + = ['required', 'help','value', 'inputType', 'group', 'placeholder']; foreach (array_merge($required, $optional) as $field ) { if (!isset($el[$field])) { @@ -229,17 +226,45 @@ class Form extends \Zend\Form\Form implements $value = $el[$field]; $element[$field] = $value; } - $element['label'] = $this->translate($el['label']); + + if (in_array($element['type'], ['checkbox', 'radio']) + && ! isset($element['group']) + ) { + $element['group'] = $element['name']; + } + + $element['label'] = $this->translate($el['label'] ?? null); $elementType = $element['type']; - if ($elementType === 'select') { + if (in_array($elementType, ['checkbox', 'radio', 'select'])) { if (empty($el['options']) && empty($el['optionGroups'])) { continue; } if (isset($el['options'])) { $options = []; + $isSelect = $elementType === 'select'; + $placeholder = $element['placeholder'] ?? null; + + if ($isSelect && $placeholder) { + // Add placeholder option (without value) for + // select element. + $options[] = [ + 'value' => '', + 'label' => $this->translate($placeholder), + 'attributes' => [ + 'selected' => 'selected', 'disabled' => 'disabled' + ] + ]; + } foreach ($el['options'] as $option) { - $options[$option] = $this->translate($option); + if ($isSelect) { + $options[] = [ + 'value' => $option, + 'label' => $this->translate($option) + ]; + } else { + $options[$option] = $this->translate($option); + } } $element['options'] = $options; } elseif (isset($el['optionGroups'])) { @@ -262,7 +287,12 @@ class Form extends \Zend\Form\Form implements $settings = []; if (isset($el['settings'])) { foreach ($el['settings'] as list($settingId, $settingVal)) { - $settings[trim($settingId)] = trim($settingVal); + $settingId = trim($settingId); + $settingVal = trim($settingVal); + if ($settingId === 'placeholder') { + $settingVal = $this->translate($settingVal); + } + $settings[$settingId] = $settingVal; } $element['settings'] = $settings; } @@ -367,6 +397,40 @@ class Form extends \Zend\Form\Form implements } switch ($type) { + case 'checkbox': + $options = []; + if (isset($el['options'])) { + $options = $el['options']; + } + $optionElements = []; + foreach ($options as $key => $val) { + $optionElements[] = [ + 'label' => $val, + 'value' => $key, + 'attributes' => ['id' => $val] + ]; + } + $conf['options'] = ['value_options' => $optionElements]; + break; + case 'radio': + $options = []; + if (isset($el['options'])) { + $options = $el['options']; + } + $optionElements = []; + $first = true; + foreach ($options as $key => $val) { + $optionElements[] = [ + 'label' => $val, + 'value' => $key, + 'label_attributes' => ['for' => $val], + 'attributes' => ['id' => $val], + 'selected' => $first + ]; + $first = false; + } + $conf['options'] = ['value_options' => $optionElements]; + break; case 'select': if (isset($el['options'])) { $conf['options'] = ['value_options' => $el['options']]; @@ -397,10 +461,12 @@ class Form extends \Zend\Form\Form implements protected function getFormElementClass($type) { $map = [ + 'checkbox' => '\Zend\Form\Element\MultiCheckbox', 'text' => '\Zend\Form\Element\Text', 'url' => '\Zend\Form\Element\Url', 'email' => '\Zend\Form\Element\Email', 'textarea' => '\Zend\Form\Element\Textarea', + 'radio' => '\Zend\Form\Element\Radio', 'select' => '\Zend\Form\Element\Select', 'submit' => '\Zend\Form\Element\Submit' ]; @@ -450,24 +516,26 @@ class Form extends \Zend\Form\Form implements } /** - * Return form recipient. + * Return form recipient(s). * - * @return array with name, email or null if not configured + * @return array of reciepients, each consisting of an array with + * name, email or null if not configured */ public function getRecipient() { - $recipient = $this->formConfig['recipient'] ?? null; + $recipient = $this->formConfig['recipient'] ?? [null]; + $recipients = isset($recipient['email']) || isset($recipient['name']) + ? [$recipient] : $recipient; - $recipientEmail = $recipient['email'] - ?? $this->defaultFormConfig['recipient_email'] ?? null; + foreach ($recipients as &$recipient) { + $recipient['email'] = $recipient['email'] + ?? $this->defaultFormConfig['recipient_email'] ?? null; - $recipientName = $recipient['name'] - ?? $this->defaultFormConfig['recipient_name'] ?? null; + $recipient['name'] = $recipient['name'] + ?? $this->defaultFormConfig['recipient_name'] ?? null; + } - return [ - $recipientName, - $recipientEmail, - ]; + return $recipients; } /** @@ -547,18 +615,37 @@ class Form extends \Zend\Form\Form implements } $value = $requestParams[$el['name']] ?? null; - if ($type === 'select') { + if (in_array($type, ['radio', 'select'])) { $value = $this->translate($value); + } elseif ($type === 'checkbox') { + $translated = []; + foreach ($value as $val) { + $translated[] = $this->translate($val); + } + $value = implode(', ', $translated); } - $label = $this->translate($el['label']); - - $params[$label] = ['type' => $type, 'value' => $value]; + $label = isset($el['label']) ? $this->translate($el['label']) : null; + $params[] = ['type' => $type, 'value' => $value, 'label' => $label]; } return [$params, 'Email/form.phtml']; } + /** + * Get translated validation message. + * + * @param string $messageId Message identifier + * + * @return string + */ + protected function getValidationMessage($messageId) + { + return $this->translate( + $this->messages[$messageId] ?? $messageId + ); + } + /** * Retrieve input filter used by this form * @@ -576,14 +663,14 @@ class Form extends \Zend\Form\Form implements 'email' => [ 'name' => EmailAddress::class, 'options' => [ - 'message' => $this->messages['invalid_email'] + 'message' => $this->getValidationMessage('invalid_email'), ] ], 'notEmpty' => [ 'name' => NotEmpty::class, 'options' => [ 'message' => [ - NotEmpty::IS_EMPTY => $this->messages['empty'] + NotEmpty::IS_EMPTY => $this->getValidationMessage('empty'), ] ] ] diff --git a/module/VuFind/src/VuFind/Form/FormFactory.php b/module/VuFind/src/VuFind/Form/FormFactory.php index f50cad21a09c7c76c685b9d556cb7212cd3e3ad1..4849f589dd0e7ba457b558642a049af2ebabe659 100644 --- a/module/VuFind/src/VuFind/Form/FormFactory.php +++ b/module/VuFind/src/VuFind/Form/FormFactory.php @@ -62,9 +62,9 @@ class FormFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager') + $config = $container->get(\VuFind\Config\PluginManager::class) ->get('config')->toArray(); - $yamlReader = $container->get('VuFind\Config\YamlReader'); + $yamlReader = $container->get(\VuFind\Config\YamlReader::class); return new $requestedName( $yamlReader, $config['Feedback'] ?? null diff --git a/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php index dd18620c4c876e65d72be883c07b310e64ef62fd..a9563aa74dd1bd6c55185504e7a7d544ee6767c5 100644 --- a/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php +++ b/module/VuFind/src/VuFind/GeoFeatures/AbstractConfigFactory.php @@ -64,6 +64,8 @@ class AbstractConfigFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Config\PluginManager')); + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class) + ); } } diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php index 4a55c3fcb38927fb63400ebdec51af963b2db2ff..008c566f2eda5f51a6bd0d2a82990a8efa274b3f 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/ConfigurationBasedFactory.php @@ -65,7 +65,7 @@ class ConfigurationBasedFactory $parts = explode('\\', $requestedName); $config = end($parts); // Set up options based on global VuFind settings: - $configReader = $sm->get('VuFind\Config\PluginManager'); + $configReader = $sm->get(\VuFind\Config\PluginManager::class); $globalConfig = $configReader->get('config'); $options = [ 'enabled' => $globalConfig->Hierarchy->showTree ?? false @@ -77,8 +77,8 @@ class ConfigurationBasedFactory // Build object: return new ConfigurationBased( $driverConfig, - $sm->get('VuFind\Hierarchy\TreeDataSource\PluginManager'), - $sm->get('VuFind\Hierarchy\TreeRenderer\PluginManager'), + $sm->get(\VuFind\Hierarchy\TreeDataSource\PluginManager::class), + $sm->get(\VuFind\Hierarchy\TreeRenderer\PluginManager::class), $options ); } diff --git a/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php index ac4437f53ecabeeb40505fdde4918d6a25722c1a..7fc0814f11a118db2982d1b4bec0e1cc7e8a913a 100644 --- a/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/Driver/PluginManager.php @@ -44,8 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'default' => 'VuFind\Hierarchy\Driver\HierarchyDefault', - 'flat' => 'VuFind\Hierarchy\Driver\HierarchyFlat', + 'default' => HierarchyDefault::class, + 'flat' => HierarchyFlat::class, + 'search2' => HierarchySearch2::class ]; /** @@ -54,10 +55,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Hierarchy\Driver\HierarchyDefault' => - 'VuFind\Hierarchy\Driver\ConfigurationBasedFactory', - 'VuFind\Hierarchy\Driver\HierarchyFlat' => - 'VuFind\Hierarchy\Driver\ConfigurationBasedFactory', + HierarchyDefault::class => ConfigurationBasedFactory::class, + HierarchyFlat::class => ConfigurationBasedFactory::class, + HierarchySearch2::class => ConfigurationBasedFactory::class ]; /** @@ -68,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Hierarchy\Driver\AbstractBase'; + return AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php index ccd65bdce601d7a119eb129f1a0faa13bba43ade..d253c282ce6757329bf469d75f6f05bd8d083a60 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataFormatter/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Hierarchy\TreeDataFormatter; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Hierarchy tree data formatter plugin manager * @@ -44,8 +46,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'json' => 'VuFind\Hierarchy\TreeDataFormatter\Json', - 'xml' => 'VuFind\Hierarchy\TreeDataFormatter\Xml', + 'json' => Json::class, + 'xml' => Xml::class, ]; /** @@ -54,10 +56,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Hierarchy\TreeDataFormatter\Json' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Hierarchy\TreeDataFormatter\Xml' => - 'Zend\ServiceManager\Factory\InvokableFactory', + Json::class => InvokableFactory::class, + Xml::class => InvokableFactory::class, ]; /** @@ -68,6 +68,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Hierarchy\TreeDataFormatter\AbstractBase'; + return AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php index 653275eb32d555eb5b643df37dd975dd45d80d36..047ea3328bff41ffadef4bc538b0d7a9852057ae 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/AbstractBase.php @@ -49,6 +49,20 @@ abstract class AbstractBase implements \Zend\Log\LoggerAwareInterface */ protected $hierarchyDriver = null; + /** + * Collection page route. + * + * @var string + */ + protected $collectionRoute = 'collection'; + + /** + * Record page route. + * + * @var string + */ + protected $recordRoute = 'record'; + /** * Get the hierarchy driver * @@ -76,6 +90,26 @@ abstract class AbstractBase implements \Zend\Log\LoggerAwareInterface return $this; } + /** + * Get collection page route. + * + * @return string + */ + public function getCollectionRoute() + { + return $this->collectionRoute; + } + + /** + * Get recordpage route. + * + * @return string + */ + public function getRecordRoute() + { + return $this->recordRoute; + } + /** * Get XML for the specified hierarchy ID. * diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php index abafb511aa5e2c1b75c21b3efc9c54ef9230b802..c0926101f5faad58580c55cdbba691ddfc6c4214 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Hierarchy\TreeDataSource; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Hierarchy tree data source plugin manager * @@ -44,8 +46,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'solr' => 'VuFind\Hierarchy\TreeDataSource\Solr', - 'xmlfile' => 'VuFind\Hierarchy\TreeDataSource\XMLFile', + 'solr' => Solr::class, + 'search2' => Search2::class, + 'xmlfile' => XMLFile::class, ]; /** @@ -54,10 +57,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Hierarchy\TreeDataSource\Solr' => - 'VuFind\Hierarchy\TreeDataSource\SolrFactory', - 'VuFind\Hierarchy\TreeDataSource\XMLFile' => - 'Zend\ServiceManager\Factory\InvokableFactory', + Solr::class => SolrFactory::class, + Search2::class => Search2Factory::class, + XMLFile::class => InvokableFactory::class, ]; /** @@ -68,6 +70,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Hierarchy\TreeDataSource\AbstractBase'; + return AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2.php new file mode 100644 index 0000000000000000000000000000000000000000..c5657f22df2614640416211b5b58768a4ee77873 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2.php @@ -0,0 +1,65 @@ +<?php +/** + * Hierarchy Tree Data Source (Search2) + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Luke O'Sullivan <l.osullivan@swansea.ac.uk> + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace VuFind\Hierarchy\TreeDataSource; + +/** + * Hierarchy Tree Data Source (Search2) + * + * This is a base helper class for producing hierarchy Trees. + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Luke O'Sullivan <l.osullivan@swansea.ac.uk> + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class Search2 extends Solr +{ + /** + * Collection page route. + * + * @var string + */ + protected $collectionRoute = 'search2collection'; + + /** + * Record page route. + * + * @var string + */ + protected $recordRoute = 'search2record'; + + /** + * Hierarchy cache file prefix. + * + * @var string + */ + protected $cachePrefix = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2Factory.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2Factory.php new file mode 100644 index 0000000000000000000000000000000000000000..4702af69177dd5ac20374690ff72947d88d4e970 --- /dev/null +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Search2Factory.php @@ -0,0 +1,47 @@ +<?php +/** + * Search2 Hierarchy tree data source plugin factory. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Hierarchy\TreeDataSource; + +/** + * Search2 Hierarchy tree data source plugin factory. + * + * @category VuFind + * @package HierarchyTree_DataSource + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Search2Factory extends SolrFactory +{ + /** + * Search backend identifier. + * + * @var string + */ + protected $backendId = 'Search2'; +} diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php index 1fe4a1277ddea3631534041e9a7edbbe275a688e..2aee535968196f9dea0e58d04d9aa79a45a09386 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/Solr.php @@ -80,6 +80,13 @@ class Solr extends AbstractBase */ protected $batchSize = 1000; + /** + * Hierarchy cache file prefix. + * + * @var string + */ + protected $cachePrefix = null; + /** * Constructor. * @@ -265,7 +272,9 @@ class Solr extends AbstractBase $cacheTemplate = 'tree_%s' ) { $cacheFile = (null !== $this->cacheDir) - ? $this->cacheDir . '/' . sprintf($cacheTemplate, urlencode($id)) + ? $this->cacheDir . '/' + . ($this->cachePrefix ? "{$this->cachePrefix}_" : '') + . sprintf($cacheTemplate, urlencode($id)) : false; $useCache = isset($options['refresh']) ? !$options['refresh'] : true; diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php index dd154c2d64ef277895339046e9222e50fb9d904a..324e38511f88d3133e9d7ff2a2582c476044cc6a 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeDataSource/SolrFactory.php @@ -40,6 +40,13 @@ use Interop\Container\ContainerInterface; */ class SolrFactory implements \Zend\ServiceManager\Factory\FactoryInterface { + /** + * Search backend identifier. + * + * @var string + */ + protected $backendId = 'Solr'; + /** * Create an object * @@ -60,20 +67,21 @@ class SolrFactory implements \Zend\ServiceManager\Factory\FactoryInterface if ($options !== null) { throw new \Exception('Unexpected options sent to factory!'); } - $cacheDir = $container->get('VuFind\Cache\Manager') + $cacheDir = $container->get(\VuFind\Cache\Manager::class) ->getCacheDir(false); - $hierarchyFilters = $container->get('VuFind\Config\PluginManager') + $hierarchyFilters = $container->get(\VuFind\Config\PluginManager::class) ->get('HierarchyDefault'); $filters = isset($hierarchyFilters->HierarchyTree->filterQueries) ? $hierarchyFilters->HierarchyTree->filterQueries->toArray() : []; - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $batchSize = isset($config->Index->cursor_batch_size) ? $config->Index->cursor_batch_size : 1000; - $solr = $container->get('VuFind\Search\BackendManager') - ->get('Solr')->getConnector(); + $solr = $container->get(\VuFind\Search\BackendManager::class) + ->get($this->backendId)->getConnector(); $formatterManager = $container - ->get('VuFind\Hierarchy\TreeDataFormatter\PluginManager'); + ->get(\VuFind\Hierarchy\TreeDataFormatter\PluginManager::class); return new $requestedName( $solr, $formatterManager, rtrim($cacheDir, '/') . '/hierarchy', $filters, $batchSize diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php index c1fb104367c8cab650f04a9ebef94495003e6aba..7146729f2067c927400d52c01bab8ff8da05fff3 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTree.php @@ -249,11 +249,30 @@ class JSTree extends AbstractBase 'recordID' => '__record_id__' ] ]; - $cache[$route] = $this->router->fromRoute($route, $params, $options); + $cache[$route] = $this->router->fromRoute( + $this->getRouteNameFromDataSource($route), $params, $options + ); } return str_replace('__record_id__', urlencode($id), $cache[$route]); } + /** + * Get route name from data source. + * + * @param string $route Route + * + * @return string + */ + protected function getRouteNameFromDataSource($route) + { + if ($route === 'collection') { + return $this->getDataSource()->getCollectionRoute(); + } elseif ($route === 'record') { + return $this->getDataSource()->getRecordRoute(); + } + return $route; + } + /** * Convert JSTree JSON structure to HTML * diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php index 77f61d133bb924513698239a926877564d94b1aa..7f43f3a736c98eeed742cfb37ee15408a30fc603 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/JSTreeFactory.php @@ -60,7 +60,8 @@ class JSTreeFactory implements \Zend\ServiceManager\Factory\FactoryInterface if ($options !== null) { throw new \Exception('Unexpected options sent to factory!'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName( $container->get('ControllerPluginManager')->get('Url'), !empty($config->Collections->collections) diff --git a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php index f6a8fcb79fe3e07f64b87acd6211d9c45605eadf..2b6221c36da75816d5983b556708eea00689140e 100644 --- a/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php +++ b/module/VuFind/src/VuFind/Hierarchy/TreeRenderer/PluginManager.php @@ -44,7 +44,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'jstree' => 'VuFind\Hierarchy\TreeRenderer\JSTree' + 'jstree' => JSTree::class ]; /** @@ -53,8 +53,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Hierarchy\TreeRenderer\JSTree' => - 'VuFind\Hierarchy\TreeRenderer\JSTreeFactory' + JSTree::class => JSTreeFactory::class ]; /** @@ -65,6 +64,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Hierarchy\TreeRenderer\AbstractBase'; + return AbstractBase::class; } } diff --git a/module/VuFind/src/VuFind/Http/PhpEnvironment/RemoteAddressFactory.php b/module/VuFind/src/VuFind/Http/PhpEnvironment/RemoteAddressFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8e113bf5a94c8e46e3c775e78e43f3c5bde9f307 --- /dev/null +++ b/module/VuFind/src/VuFind/Http/PhpEnvironment/RemoteAddressFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * RemoteAddress utility factory. This uses the core Zend RemoteAddress but + * configures it according to VuFind settings. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Http\PhpEnvironment; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * RemoteAddress utility factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RemoteAddressFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $cfg = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + $object = new $requestedName(); + if ($cfg->Site->reverse_proxy ?? false) { + $object->setUseProxy(true); + } + return $object; + } +} diff --git a/module/VuFind/src/VuFind/Http/PhpEnvironment/Request.php b/module/VuFind/src/VuFind/Http/PhpEnvironment/Request.php new file mode 100644 index 0000000000000000000000000000000000000000..752ff1fcce140545a8563d0655b455ce3bfecc14 --- /dev/null +++ b/module/VuFind/src/VuFind/Http/PhpEnvironment/Request.php @@ -0,0 +1,135 @@ +<?php +/** + * HTTP Request class + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package HTTP + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Http\PhpEnvironment; + +/** + * HTTP Request class + * + * @category VuFind + * @package HTTP + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Request extends \Zend\Http\PhpEnvironment\Request +{ + /** + * Return the parameter container responsible for query parameters or a single + * query parameter + * + * @param string|null $name Parameter name to retrieve, or null to get the + * whole container. + * @param mixed|null $default Default value to use when the parameter is + * missing. + * + * @return \Zend\Stdlib\ParametersInterface|mixed + */ + public function getQuery($name = null, $default = null) + { + return $this->cleanup(parent::getQuery($name, $default)); + } + + /** + * Return the parameter container responsible for post parameters or a single + * post parameter. + * + * @param string|null $name Parameter name to retrieve, or null to get the + * whole container. + * @param mixed|null $default Default value to use when the parameter is + * missing. + * + * @return \Zend\Stdlib\ParametersInterface|mixed + */ + public function getPost($name = null, $default = null) + { + return $this->cleanup(parent::getPost($name, $default)); + } + + /** + * Return the parameter container responsible for server parameters or a single + * parameter value. + * + * @param string|null $name Parameter name to retrieve, or null to get the + * whole container. + * @param mixed|null $default Default value to use when the parameter is + * missing. + * + * @see http://www.faqs.org/rfcs/rfc3875.html + * @return \Zend\Stdlib\ParametersInterface|mixed + */ + public function getServer($name = null, $default = null) + { + return $this->cleanup(parent::getServer($name, $default)); + } + + /** + * Clean up a parameter + * + * @param \Zend\Stdlib\ParametersInterface|mixed $param Parameter + * + * @return \Zend\Stdlib\ParametersInterface|mixed + */ + protected function cleanup($param) + { + if (is_array($param) || $param instanceof \Zend\Stdlib\ParametersInterface) { + foreach ($param as $key => &$value) { + if (is_array($value)) { + $value = $this->cleanup($value); + } elseif (!$this->isValid($key) || !$this->isValid($value)) { + unset($param[$key]); + } + } + return $param; + } + + if (is_string($param) && !$this->isValid($param)) { + return ''; + } + + return $param; + } + + /** + * Check if a string is a valid parameter + * + * @param string $str String to check + * + * @return bool + */ + protected function isValid($str) + { + // Check if the string is UTF-8 + if (is_string($str) && $str !== '' && !preg_match('/^./su', $str)) { + return false; + } + if (strpos($str, "\x00") !== false) { + return false; + } + return true; + } +} diff --git a/module/VuFind/src/VuFind/I18n/Translator/LanguageInitializerTrait.php b/module/VuFind/src/VuFind/I18n/Translator/LanguageInitializerTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..d4404e07316ae9e97b44cc7056612196ed74802c --- /dev/null +++ b/module/VuFind/src/VuFind/I18n/Translator/LanguageInitializerTrait.php @@ -0,0 +1,99 @@ +<?php +/** + * Logic for initializing a language within a translator used by VuFind. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Translator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\I18n\Translator; + +/** + * Logic for initializing a language within a translator used by VuFind. + * + * @category VuFind + * @package Translator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +trait LanguageInitializerTrait +{ + /** + * Array of flags to indicate which languages have already been initialized. + * + * @var array + */ + protected $initializedI18nLanguages = []; + + /** + * Look up all text domains. + * + * @return array + */ + protected function getTextDomains() + { + $base = APPLICATION_PATH; + $local = LOCAL_OVERRIDE_DIR; + $languagePathParts = ["$base/languages"]; + if (!empty($local)) { + $languagePathParts[] = "$local/languages"; + } + $languagePathParts[] = "$base/themes/*/languages"; + + $domains = []; + foreach ($languagePathParts as $current) { + $places = glob($current . '/*', GLOB_ONLYDIR | GLOB_NOSORT); + $domains = array_merge($domains, array_map('basename', $places)); + } + + return array_unique($domains); + } + + /** + * Configure a translator to support the requested language. + * + * @param \Zend\Mvc\I18n\Translator $translator Translator + * @param string $language Language to set up + * + * @return void + */ + protected function addLanguageToTranslator($translator, $language) + { + // Don't double-initialize languages: + if (isset($this->initializedI18nLanguages[$language])) { + return; + } + $this->initializedI18nLanguages[$language] = true; + + // If we got this far, we need to set everything up: + $translator->addTranslationFile('ExtendedIni', null, 'default', $language); + foreach ($this->getTextDomains() as $domain) { + // Set up text domains using the domain name as the filename; + // this will help the ExtendedIni loader dynamically locate + // the appropriate files. + $translator->addTranslationFile( + 'ExtendedIni', $domain, $domain, $language + ); + } + } +} diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php index bc654ff435eeb9bb20e7516aec7902a5950752c0..92527f53ba3f84fa34c21e182dbaa6858f3a1714 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php @@ -121,6 +121,32 @@ trait TranslatorAwareTrait return $this->translateString((string)$str, $tokens, $default, $domain); } + /** + * Translate a string (or string-castable object) using a prefix, or without the + * prefix if a prefixed translation is not found. + * + * @param string $prefix Translation key prefix + * @param string|object|array $target String to translate or an array of text + * domain and string to translate + * @param array $tokens Tokens to inject into the translated + * string + * @param string $default Default value to use if no translation is + * found (null for no default). + * + * @return string + */ + public function translateWithPrefix($prefix, $target, $tokens = [], + $default = null + ) { + if (is_string($target)) { + if (null === $default) { + $default = $target; + } + $target = $prefix . $target; + } + return $this->translate($target, $tokens, $default); + } + /** * Get translation for a string * @@ -140,7 +166,8 @@ trait TranslatorAwareTrait // Did the translation fail to change anything? If so, use default: if (null !== $default && $msg == $str) { - $msg = $default; + $msg = $default instanceof \VuFind\I18n\TranslatableStringInterface + ? $default->getDisplayString() : $default; } // Do we need to perform substitutions? diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorFactory.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..64f495a5476dda58f1c4c0e4618190f3cf9f676a --- /dev/null +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorFactory.php @@ -0,0 +1,103 @@ +<?php +/** + * Translator factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Translator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\I18n\Translator; + +use Interop\Container\ContainerInterface; + +/** + * Translator factory. + * + * @category VuFind + * @package Translator + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class TranslatorFactory extends \Zend\Mvc\I18n\TranslatorFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $translator = parent::__invoke($container, $requestedName, $options); + + // Set up the ExtendedIni plugin: + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $pathStack = [ + APPLICATION_PATH . '/languages', + LOCAL_OVERRIDE_DIR . '/languages' + ]; + $fallbackLocales = $config->Site->language == 'en' + ? 'en' + : [$config->Site->language, 'en']; + try { + $pm = $translator->getPluginManager(); + } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $ex) { + // If getPluginManager is missing, this means that the user has + // disabled translation in module.config.php or PHP's intl extension + // is missing. We can do no further configuration of the object. + return $translator; + } + $pm->setService( + 'ExtendedIni', new Loader\ExtendedIni($pathStack, $fallbackLocales) + ); + + // Set up language caching for better performance: + try { + $translator->setCache( + $container->get(\VuFind\Cache\Manager::class)->getCache('language') + ); + } catch (\Exception $e) { + // Don't let a cache failure kill the whole application, but make + // note of it: + $logger = $container->get(\VuFind\Log\Logger::class); + $logger->debug( + 'Problem loading cache: ' . get_class($e) . ' exception: ' + . $e->getMessage() + ); + } + + return $translator; + } +} diff --git a/module/VuFind/src/VuFind/ILS/Connection.php b/module/VuFind/src/VuFind/ILS/Connection.php index db0de12f09c386e9b064f174f35ecdec33071ac2..cc57f909badcf2310b55be8ba39848907b34998b 100644 --- a/module/VuFind/src/VuFind/ILS/Connection.php +++ b/module/VuFind/src/VuFind/ILS/Connection.php @@ -31,6 +31,7 @@ */ namespace VuFind\ILS; +use VuFind\Exception\BadConfig; use VuFind\Exception\ILS as ILSException; use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFind\ILS\Driver\DriverInterface; @@ -110,6 +111,13 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface */ protected $failing = false; + /** + * Request object + * + * @var \Zend\Http\Request + */ + protected $request; + /** * Constructor * @@ -117,10 +125,12 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface * representing the [Catalog] section of config.ini * @param \VuFind\ILS\Driver\PluginManager $driverManager Driver plugin manager * @param \VuFind\Config\PluginManager $configReader Configuration loader + * @param \Zend\Http\Request $request Request object */ public function __construct(\Zend\Config\Config $config, \VuFind\ILS\Driver\PluginManager $driverManager, - \VuFind\Config\PluginManager $configReader + \VuFind\Config\PluginManager $configReader, + \Zend\Http\Request $request = null ) { if (!isset($config->driver)) { throw new \Exception('ILS driver setting missing.'); @@ -131,6 +141,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface $this->config = $config; $this->configReader = $configReader; $this->driverManager = $driverManager; + $this->request = $request; } /** @@ -164,7 +175,15 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface */ protected function initializeDriver() { - $this->driver->setConfig($this->getDriverConfig()); + try { + $this->driver->setConfig($this->getDriverConfig()); + } catch (\Exception $e) { + // Any errors thrown during configuration should be cast to BadConfig + // so we can handle them differently from other runtime problems. + throw $e instanceof BadConfig + ? $e + : new BadConfig('Failure during configuration.', 0, $e); + } $this->driver->init(); $this->driverInitialized = true; } @@ -185,10 +204,20 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface * If configured, fail over to the NoILS driver and return true; otherwise, * return false. * + * @param \Exception $e The exception that triggered the failover. + * * @return bool */ - protected function failOverToNoILS() + protected function failOverToNoILS(\Exception $e = null) { + // If the exception is caused by a configuration error, the administrator + // needs to fix it, but failing over to NoILS will mask the error and cause + // confusion. We shouldn't do that! + if ($e instanceof BadConfig) { + return false; + } + + // If we got this far, we want to proceed with failover... $this->failing = true; // Only fail over if we're configured to allow it and we haven't already @@ -222,7 +251,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface try { $this->initializeDriver(); } catch (\Exception $e) { - if (!$this->failOverToNoILS()) { + if (!$this->failOverToNoILS($e)) { throw $e; } } @@ -658,6 +687,24 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return false; } + /** + * Check Patron login + * + * A support method for checkFunction(). This is responsible for checking + * the driver configuration to determine if the system supports patron login. + * It is currently assumed that all drivers do. + * + * @param array $functionConfig The patronLogin configuration values + * @param array $params An array of function-specific params (or null) + * + * @return mixed On success, an associative array with specific function keys + * and values for login; on failure, false. + */ + protected function checkMethodpatronLogin($functionConfig, $params) + { + return $functionConfig; + } + /** * Get proper help text from the function config * @@ -694,7 +741,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return $this->getDriver()->checkRequestIsValid($id, $data, $patron); } } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; @@ -729,7 +776,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface ); } } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; @@ -761,7 +808,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface ); } } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; @@ -845,7 +892,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return $this->checkCapability('hasHoldings', [$id]) ? $this->getDriver()->hasHoldings($id) : true; } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; @@ -866,7 +913,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return $this->checkCapability('loginIsHidden') ? $this->getDriver()->loginIsHidden() : false; } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; @@ -973,6 +1020,51 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface return $result; } + /** + * Get holdings + * + * Retrieve holdings from ILS driver class and normalize result array if needed. + * + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Additional options + * + * @return array Array with holding data + */ + public function getHolding($id, $patron = null, $options = []) + { + // Get pagination options for holdings tab: + $params = compact('id', 'patron'); + $config = $this->checkCapability('getConfig', ['Holdings', $params]) + ? $this->getDriver()->getConfig('Holdings', $params) : []; + if (empty($config['itemLimit'])) { + // Use itemLimit in Holds as fallback for backward compatibility: + $config + = $this->checkCapability('getConfig', ['Holds', $params]) + ? $this->getDriver()->getConfig('Holds', $params) : []; + } + $itemLimit = !empty($config['itemLimit']) ? $config['itemLimit'] : null; + + $page = $this->request ? $this->request->getQuery('page', 1) : 1; + $offset = ($itemLimit && is_numeric($itemLimit)) + ? ($page * $itemLimit) - $itemLimit + : null; + $defaultOptions = compact('page', 'itemLimit', 'offset'); + $finalOptions = $options + $defaultOptions; + + // Get the holdings from the ILS + $holdings = $this->__call('getHolding', [$id, $patron, $finalOptions]); + + // Return all the necessary details: + return [ + 'total' => $holdings['total'] ?? count($holdings), + 'holdings' => $holdings['holdings'] ?? $holdings, + 'electronic_holdings' => $holdings['electronic_holdings'] ?? [], + 'page' => $finalOptions['page'], + 'itemLimit' => $finalOptions['itemLimit'], + ]; + } + /** * Default method -- pass along calls to the driver if available; return * false otherwise. This allows custom functions to be implemented in @@ -993,7 +1085,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface ); } } catch (\Exception $e) { - if ($this->failOverToNoILS()) { + if ($this->failOverToNoILS($e)) { return call_user_func_array([$this, __METHOD__], func_get_args()); } throw $e; diff --git a/module/VuFind/src/VuFind/ILS/ConnectionFactory.php b/module/VuFind/src/VuFind/ILS/ConnectionFactory.php index e9a7f0f7e847972ef5884808848d0b173734f5f1..ff4992b2e66a6585dd372520a3b4336ccf0f57d4 100644 --- a/module/VuFind/src/VuFind/ILS/ConnectionFactory.php +++ b/module/VuFind/src/VuFind/ILS/ConnectionFactory.php @@ -61,11 +61,16 @@ class ConnectionFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } + $configManager = $container->get(\VuFind\Config\PluginManager::class); + $request = $container->get('Request'); $catalog = new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config')->Catalog, - $container->get('VuFind\ILS\Driver\PluginManager'), - $container->get('VuFind\Config\PluginManager') + $configManager->get('config')->Catalog, + $container->get(\VuFind\ILS\Driver\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $request instanceof \Zend\Http\Request ? $request : null + ); + return $catalog->setHoldConfig( + $container->get(\VuFind\ILS\HoldSettings::class) ); - return $catalog->setHoldConfig($container->get('VuFind\ILS\HoldSettings')); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/AbstractAPI.php b/module/VuFind/src/VuFind/ILS/Driver/AbstractAPI.php index 65811acf2c97cc35da63fc7c1f5d7b1d7caa8c05..572f9a5d31b6eb9c32bc6b55365d254aad967e03 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/AbstractAPI.php +++ b/module/VuFind/src/VuFind/ILS/Driver/AbstractAPI.php @@ -27,10 +27,11 @@ */ namespace VuFind\ILS\Driver; -use VuFind\Exception\BadRequest as BadRequest; -use VuFind\Exception\Forbidden as Forbidden; +use VuFind\Exception\BadConfig; +use VuFind\Exception\BadRequest; +use VuFind\Exception\Forbidden; use VuFind\Exception\ILS as ILSException; -use VuFind\Exception\RecordMissing as RecordMissing; +use VuFind\Exception\RecordMissing; use VuFindHttp\HttpServiceAwareInterface; use Zend\Log\LoggerAwareInterface; @@ -64,6 +65,32 @@ abstract class AbstractAPI extends AbstractBase implements HttpServiceAwareInter return [$headers, $params]; } + /** + * Function that obscures and logs debug data + * + * @param string $method Request method GET/POST/PUT/DELETE/etc + * @param string $path Request URL + * @param array $params Request parameters + * @param \Zend\Http\Headers $req_headers Headers object + * + * @return void + */ + protected function debugRequest($method, $path, $params, $req_headers) + { + $logParams = []; + $logHeaders = []; + if ($method == 'GET') { + $logParams = $params; + $logHeaders = $req_headers->toArray(); + } + $this->debug( + $method . ' request.' . + ' URL: ' . $path . '.' . + ' Params: ' . print_r($logParams, true) . '.' . + ' Headers: ' . print_r($logHeaders, true) + ); + } + /** * Make requests * @@ -82,13 +109,16 @@ abstract class AbstractAPI extends AbstractBase implements HttpServiceAwareInter $method, 120 ); - // error_log($method . ' ' . $this->config['API']['base_url'] . $path); // Add default headers and parameters $req_headers = $client->getRequest()->getHeaders(); $req_headers->addHeaders($headers); list($req_headers, $params) = $this->preRequest($req_headers, $params); + if ($this->logger) { + $this->debugRequest($method, $path, $params, $req_headers); + } + // Add params if ($method == 'GET') { $client->setParameterGet($params); @@ -120,7 +150,7 @@ abstract class AbstractAPI extends AbstractBase implements HttpServiceAwareInter * @param array $config Configuration array (usually loaded from a VuFind .ini * file whose name corresponds with the driver class name). * - * @throws ILSException if base url excluded + * @throws BadConfig if base url excluded * @return void */ public function setConfig($config) @@ -128,7 +158,7 @@ abstract class AbstractAPI extends AbstractBase implements HttpServiceAwareInter parent::setConfig($config); // Base URL required for API drivers if (!isset($config['API']['base_url'])) { - throw new ILSException('API Driver configured without base url.'); + throw new BadConfig('API Driver configured without base url.'); } } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Aleph.php b/module/VuFind/src/VuFind/ILS/Driver/Aleph.php index 681fa50488a0b8cf5e72c03c30bf4071a78d0d98..bbaf2c78b0d968a138a6cec33fae4c2f6274b434 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Aleph.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Aleph.php @@ -567,7 +567,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $this->debug("url: $url response: $answer"); } $answer = str_replace('xmlns=', 'ns=', $answer); - $result = simplexml_load_string($answer); + $result = @simplexml_load_string($answer); if (!$result) { if ($this->debug_enabled) { $this->debug("XML is not valid, URL: $url"); @@ -734,16 +734,19 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $holding = []; list($bib, $sys_no) = $this->parseId($id); @@ -994,7 +997,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $z30 = $item->z30; $group = $item->xpath('@href'); $group = substr(strrchr($group[0], "/"), 1); - //$renew = $item->xpath('@renew'); + $renew = $item->xpath('@renew'); //$docno = (string) $z36->{'z36-doc-number'}; //$itemseq = (string) $z36->{'z36-item-sequence'}; //$seq = (string) $z36->{'z36-sequence'}; @@ -1025,7 +1028,7 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, 'duedate' => $this->parseDate($due), //'holddate' => $holddate, //'delete' => $delete, - 'renewable' => true, + 'renewable' => $renew[0] == "Y", //'create' => $this->parseDate($create) ]; } @@ -1067,13 +1070,16 @@ class Aleph extends AbstractBase implements \Zend\Log\LoggerAwareInterface, $result = []; foreach ($details['details'] as $id) { try { - $this->doRestDLFRequest( + $xml = $this->doRestDLFRequest( [ 'patron', $patron['id'], 'circulationActions', 'loans', $id ], null, 'POST', null ); - $result[$id] = ['success' => true]; + $due = (string)$xml->xpath('//new-due-date'); + $result[$id] = [ + 'success' => true, 'new_date' => $this->parseDate($due) + ]; } catch (AlephRestfulException $ex) { $result[$id] = [ 'success' => false, 'sysMessage' => $ex->getMessage() diff --git a/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php b/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php index a0a303d7be42b075aaae19b5390bdce5ae0aebc9..7d40d503b5bae0b20e482e98bbcb324fc62dfa34 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/AlephFactory.php @@ -61,7 +61,9 @@ class AlephFactory extends DriverWithDateConverterFactory throw new \Exception('Unexpected options passed to factory.'); } return parent::__invoke( - $container, $requestedName, [$container->get('VuFind\Cache\Manager')] + $container, + $requestedName, + [$container->get(\VuFind\Cache\Manager::class)] ); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Alma.php b/module/VuFind/src/VuFind/ILS/Driver/Alma.php index a662c471337dbc41e949b98192afda88c932c005..0cd9ac92ceee7c05261bcac4de632471716b7457 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Alma.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Alma.php @@ -40,9 +40,11 @@ use Zend\Http\Headers; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ -class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface +class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface, + \Zend\Log\LoggerAwareInterface { use \VuFindHttp\HttpServiceAwareTrait; + use \VuFind\Log\LoggerAwareTrait; use CacheTrait; /** @@ -108,13 +110,17 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface /** * Make an HTTP request against Alma * - * @param string $path Path to retrieve from API (excluding base - * URL/API key) - * @param array $paramsGet Additional GET params - * @param array $paramsPost Additional POST params - * @param string $method GET or POST. Default is GET. - * @param string $rawBody Request body. - * @param Headers|array $headers Add headers to the call. + * @param string $path Path to retrieve from API (excluding base + * URL/API key) + * @param array $paramsGet Additional GET params + * @param array $paramsPost Additional POST params + * @param string $method GET or POST. Default is GET. + * @param string $rawBody Request body. + * @param Headers|array $headers Add headers to the call. + * @param array $allowedErrors HTTP status codes that are not treated as + * API errors. + * @param bool $returnStatus Whether to return HTTP status in addition + * to the response. * * @throws ILSException * @return NULL|SimpleXMLElement @@ -125,17 +131,20 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $paramsPost = [], $method = 'GET', $rawBody = null, - $headers = null + $headers = null, + $allowedErrors = [], + $returnStatus = false ) { // Set some variables $result = null; $statusCode = null; $returnValue = null; + $startTime = microtime(true); try { // Set API key if it is not already available in the GET params - if (!isset($paramsGet['apiKey'])) { - $paramsGet['apiKey'] = $this->apiKey; + if (!isset($paramsGet['apikey'])) { + $paramsGet['apikey'] = $this->apiKey; } // Create the API URL @@ -147,17 +156,16 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface // Set method $client->setMethod($method); - // Set other GET parameters - if ($method == 'GET') { - $client->setParameterGet($paramsGet); - } else { - // Always set API key as GET parameter - $client->setParameterGet(['apiKey' => $paramsGet['apiKey']]); + // Set timeout + $timeout = $this->config['Catalog']['http_timeout'] ?? 30; + $client->setOptions(['timeout' => $timeout]); - // Set POST parameters - if ($method == 'POST') { - $client->setParameterPost($paramsPost); - } + // Set other GET parameters (apikey and other URL parameters are used + // also with e.g. POST requests) + $client->setParameterGet($paramsGet); + // Set POST parameters + if ($method == 'POST') { + $client->setParameterPost($paramsPost); } // Set body if applicable @@ -173,31 +181,53 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface // Execute HTTP call $result = $client->send(); } catch (\Exception $e) { + $this->logError("$method request for $url failed: " . $e->getMessage()); throw new ILSException($e->getMessage()); } + $duration = round(microtime(true) - $startTime, 4); + $urlParams = $client->getRequest()->getQuery()->toString(); + $code = $result->getStatusCode(); + $this->debug( + "[$duration] $method request for $url?$urlParams results ($code):\n" + . $result->getBody() + ); + // Get the HTTP status code $statusCode = $result->getStatusCode(); // Check for error if ($result->isServerError()) { + $this->logError( + "$method request for $url failed, HTTP error code: $statusCode" + ); throw new ILSException('HTTP error code: ' . $statusCode, $statusCode); } $answer = $result->getBody(); $answer = str_replace('xmlns=', 'ns=', $answer); - $xml = simplexml_load_string($answer); - - if ($result->isSuccess()) { + try { + $xml = simplexml_load_string($answer); + } catch (\Exception $e) { + $this->logError( + "Could not parse response for $method request for $url: " + . $e->getMessage() . ". Response was:\n" + . $result->getHeaders()->toString() + . "\n\n$answer" + ); + throw new ILSException($e->getMessage()); + } + if ($result->isSuccess() || in_array($statusCode, $allowedErrors)) { if (!$xml && $result->isServerError()) { - throw new ILSException( - 'XML is not valid or HTTP error, URL: ' . $url . - ', HTTP status code: ' . $statusCode, $statusCode - ); + $error = 'XML is not valid or HTTP error, URL: ' . $url . + ', HTTP status code: ' . $statusCode; + $this->logError($error); + throw new ILSException($error, $statusCode); } $returnValue = $xml; } else { - $almaErrorMsg = $xml->errorList->error[0]->errorMessage; + $almaErrorMsg = $xml->errorList->error[0]->errorMessage + ?? '[could not parse error message]'; error_log( '[ALMA] ' . $almaErrorMsg . ' | Call to: ' . $client->getUri() . '. GET params: ' . var_export($paramsGet, true) . '. POST params: ' . @@ -205,12 +235,14 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $result->getBody() . '. HTTP status code: ' . $statusCode ); throw new ILSException( - 'Alma error message: ' . $almaErrorMsg . ' | HTTP error code: ' . - $statusCode, $statusCode + "Alma error message for $method request for $url: " + . $almaErrorMsg . ' | HTTP error code: ' + . $statusCode, + $statusCode ); } - return $returnValue; + return $returnStatus ? [$returnValue, $statusCode] : $returnValue; } /** @@ -231,160 +263,175 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Additional options * - * @return array On success an associative array with the following keys: - * id, source, availability (boolean), status, location, - * reserve, callnumber, duedate, returnDate, number, - * barcode, item_notes, item_id, holding_id, addLink. + * @return array On success an array with the key "total" containing the total + * number of items for the given bib id, and the key "holdings" containing an + * array of holding information each one with these keys: id, source, + * availability, status, location, reserve, callnumber, duedate, returnDate, + * number, barcode, item_notes, item_id, holding_id, addLink, description */ - public function getHolding($id, array $patron = null) + public function getHolding($id, $patron = null, array $options = []) { - // Get config data: - $fulfillementUnits = $this->config['FulfillmentUnits'] ?? null; - $requestableConfig = $this->config['Requestable'] ?? null; + // Prepare result array with default values. If no API result can be received + // these will be returned. + $results['total'] = 0; + $results['holdings'] = []; + + // Correct copy count in case of paging + $copyCount = $options['offset'] ?? 0; + + // Paging parameters for paginated API call. The "limit" tells the API how + // many items the call should return at once (e. g. 10). The "offset" defines + // the range (e. g. get items 30 to 40). With these parameters we are able to + // use a paginator for paging through many items. + $apiPagingParams = ''; + if ($options['itemLimit'] ?? null) { + $apiPagingParams = 'limit=' . urlencode($options['itemLimit']) + . '&offset=' . urlencode($options['offset'] ?? 0); + } - $results = []; - $copyCount = 0; - $bibPath = '/bibs/' . urlencode($id) . '/holdings'; - if ($holdings = $this->makeRequest($bibPath)) { - foreach ($holdings->holding as $holding) { - $holdingId = (string)$holding->holding_id; - $locationCode = (string)$holding->location; - $addLink = false; - if ($fulfillementUnits != null && $requestableConfig != null) { - $addLink = $this->requestsAllowed( - $fulfillementUnits, - $locationCode, - $requestableConfig, - $patron + // The path for the API call. We call "ALL" available items, but not at once + // as a pagination mechanism is used. If paging params are not set for some + // reason, the first 10 items are called which is the default API behaviour. + $itemsPath = '/bibs/' . urlencode($id) . '/holdings/ALL/items?' + . $apiPagingParams + . '&order_by=library,location,enum_a,enum_b&direction=desc' + . '&expand=due_date'; + + if ($items = $this->makeRequest($itemsPath)) { + // Get the total number of items returned from the API call and set it to + // a class variable. It is then used in VuFind\RecordTab\HoldingsILS for + // the items paginator. + $results['total'] = (int)$items->attributes()->total_record_count; + + foreach ($items->item as $item) { + $number = ++$copyCount; + $holdingId = (string)$item->holding_data->holding_id; + $itemId = (string)$item->item_data->pid; + $barcode = (string)$item->item_data->barcode; + $status = (string)$item->item_data->base_status[0] + ->attributes()['desc']; + $duedate = $item->item_data->due_date + ? $this->parseDate((string)$item->item_data->due_date) : null; + if ($duedate && 'Item not in place' === $status) { + $status = 'Checked Out'; + } + + $itemNotes = !empty($item->item_data->public_note) + ? [(string)$item->item_data->public_note] : null; + + $processType = (string)($item->item_data->process_type ?? ''); + if ($processType && 'LOAN' !== $processType) { + $status = $this->getTranslatableStatusString( + $item->item_data->process_type ); } - $itemPath = $bibPath . '/' . urlencode($holdingId) . '/items'; - if ($currentItems = $this->makeRequest($itemPath)) { - foreach ($currentItems->item as $item) { - $itemId = (string)$item->item_data->pid; - $barcode = (string)$item->item_data->barcode; - $processType = (string)$item->item_data->process_type; - $itemNotes = null; - if ($item->item_data->public_note != null - && !empty($item->item_data->public_note) - ) { - $itemNotes = [(string)$item->item_data->public_note]; - } - $requested = ((string)$item->item_data->requested == 'false') - ? false - : true; - - $number = ++$copyCount; - $description = null; - if ($item->item_data->description != null - && !empty($item->item_data->description) - ) { - $number = (string)$item->item_data->description; - $description = (string)$item->item_data->description; - } + $description = null; + if (!empty($item->item_data->description)) { + $number = (string)$item->item_data->description; + $description = (string)$item->item_data->description; + } - // For some data we need to do additional API calls - // due to the Alma API architecture - $duedate = ($requested) ? 'requested' : null; - if ($processType == 'LOAN' && !$requested) { - $loanDataPath = '/bibs/' . urlencode($id) . '/holdings/' - . urlencode($holdingId) . '/items/' - . urlencode($itemId) . '/loans'; - $loanData = $this->makeRequest($loanDataPath); - $loan = $loanData->item_loan; - $duedate = $this->parseDate((string)$loan->due_date); - } + $results['holdings'][] = [ + 'id' => $id, + 'source' => 'Solr', + 'availability' => $this->getAvailabilityFromItem($item), + 'status' => $status, + 'location' => $this->getItemLocation($item), + 'reserve' => 'N', // TODO: support reserve status + 'callnumber' => $this->getTranslatableString( + $item->holding_data->call_number + ), + 'duedate' => $duedate, + 'returnDate' => false, // TODO: support recent returns + 'number' => $number, + 'barcode' => empty($barcode) ? 'n/a' : $barcode, + 'item_notes' => $itemNotes ?? null, + 'item_id' => $itemId, + 'holding_id' => $holdingId, + 'holdtype' => 'auto', + 'addLink' => $patron ? 'check' : false, + // For Alma title-level hold requests + 'description' => $description ?? null + ]; + } + } - $results[] = [ - 'id' => $id, - 'source' => 'Solr', - 'availability' => $this->getAvailabilityFromItem($item), - 'status' => (string)$item - ->item_data - ->base_status[0] - ->attributes()['desc'], - 'location' => $locationCode, - 'reserve' => 'N', // TODO: support reserve status - 'callnumber' => (string)$item->holding_data->call_number, - 'duedate' => $duedate, - 'returnDate' => false, // TODO: support recent returns - 'number' => $number,//++$copyCount, - 'barcode' => empty($barcode) ? 'n/a' : $barcode, - 'item_notes' => $itemNotes, - 'item_id' => $itemId, - 'holding_id' => $holdingId, - 'addLink' => $addLink, - // For Alma title-level hold requests - 'description' => $description - ]; - } + // Fetch also digital and/or electronic inventory if configured + $types = $this->getInventoryTypes(); + if (in_array('d_avail', $types) || in_array('e_avail', $types)) { + // No need for physical items + $key = array_search('p_avail', $types); + if (false !== $key) { + unset($types[$key]); + } + $statuses = $this->getStatusesForInventoryTypes((array)$id, $types); + $electronic = []; + foreach ($statuses as $record) { + foreach ($record as $status) { + $electronic[] = $status; } } + $results['electronic_holdings'] = $electronic; } return $results; } /** - * Check if the user is allowed to place requests for an Alma fulfillment - * unit in general. We check for blocks on the patron account that could - * block a request in getRequestBlocks(). - * - * @param array $fulfillementUnits An array of fulfillment units and associated - * locations from Alma.ini (see section - * [FulfillmentUnits]) - * @param string $locationCode The location code of the holding to be - * checked - * @param array $requestableConfig An array of fulfillment units and associated - * patron groups and their request policy from - * Alma.ini (see section [Requestable]) - * @param array $patron An array with the patron details (username - * and password) - * - * @return boolean true if the the patron is allowed to place - * requests on holdings of this fulfillment - * unit, false otherwise. - * @author Michael Birkner + * Check if request is valid + * + * This is responsible for determining if an item is requestable + * + * @param string $id The record id + * @param array $data An array of item data + * @param patron $patron An array of patron data + * + * @return bool True if request is valid, false if not */ - protected function requestsAllowed( - $fulfillementUnits, - $locationCode, - $requestableConfig, - $patron - ) { - $requestsAllowed = false; - - // Get user group code - $cacheId = 'alma|user|' . $patron['cat_username'] . '|group_code'; - $userGroupCode = $this->getCachedData($cacheId); - if ($userGroupCode === null) { - $profile = $this->getMyProfile($patron); - $userGroupCode = (string)$profile['group_code']; + public function checkRequestIsValid($id, $data, $patron) + { + $patronId = $patron['id']; + $level = $data['level'] ?? 'copy'; + if ('copy' === $level) { + // Call the request-options API for the logged-in user + $requestOptionsPath = '/bibs/' . urlencode($id) + . '/holdings/' . urlencode($data['holding_id']) . '/items/' + . urlencode($data['item_id']) . '/request-options?user_id=' + . urlencode($patronId); + + // Make the API request + $requestOptions = $this->makeRequest($requestOptionsPath); + } elseif ('title' === $level) { + $hmac = explode(':', $this->config['Holds']['HMACKeys'] ?? ''); + if (!in_array('level', $hmac) || !in_array('description', $hmac)) { + return false; + } + // Call the request-options API for the logged-in user + $requestOptionsPath = '/bibs/' . urlencode($id) + . '/request-options?user_id=' . urlencode($patronId); + + // Make the API request + $requestOptions = $this->makeRequest($requestOptionsPath); + } else { + return false; } - // Get the fulfillment unit of the location. - $locationFulfillmentUnit = $this->getFulfillmentUnitByLocation( - $locationCode, - $fulfillementUnits + // Check possible request types from the API answer + $requestTypes = $requestOptions->xpath( + '/request_options/request_option//type' ); - - // Check if the group of the currently logged in user is allowed to place - // requests on items belonging to current fulfillment unit - if (($locationFulfillmentUnit != null && !empty($locationFulfillmentUnit)) - && ($userGroupCode != null && !empty($userGroupCode)) - ) { - $requestsAllowed = false; - if ($requestableConfig[$locationFulfillmentUnit][$userGroupCode] == 'Y' - ) { - $requestsAllowed = true; + foreach ($requestTypes as $requestType) { + if ('HOLD' === (string)$requestType) { + return true; } } - return $requestsAllowed; + return false; } /** @@ -412,14 +459,14 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function getAccountBlocks($patron) { - $patronId = $patron['cat_username']; + $patronId = $patron['id']; $cacheId = 'alma|user|' . $patronId . '|blocks'; $cachedBlocks = $this->getCachedData($cacheId); if ($cachedBlocks !== null) { return $cachedBlocks; } - $xml = $this->makeRequest('/users/' . $patron['cat_username']); + $xml = $this->makeRequest('/users/' . $patronId); if ($xml == null || empty($xml)) { return false; } @@ -487,7 +534,6 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function createAlmaUser($formParams) { - // Get config for creating new Alma users from Alma.ini $newUserConfig = $this->config['NewUser']; @@ -606,29 +652,121 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface * * This is responsible for authenticating a patron against the catalog. * - * @param string $barcode The patrons barcode. + * @param string $username The patrons barcode or other username. * @param string $password The patrons password. * * @return string[]|NULL */ - public function patronLogin($barcode, $password) + public function patronLogin($username, $password) { - // Create array of get parameters for API call + $loginMethod = $this->config['Catalog']['loginMethod'] ?? 'vufind'; + + $patron = []; + $patronId = $username; + if ('email' === $loginMethod) { + // Try to find the user in Alma by an identifier + list($response, $status) = $this->makeRequest( + '/users/' . urlencode($username), + [ + 'view' => 'full' + ], + [], + 'GET', + null, + null, + [400], + true + ); + if (400 != $status) { + $patron = [ + 'id' => (string)$response->primary_id, + 'cat_username' => trim($username), + 'email' => $this->getPreferredEmail($response) + ]; + } else { + // Try to find the user in Alma by unique email address + $getParams = [ + 'q' => 'email~' . $username + ]; + + $response = $this->makeRequest( + '/users/', + $getParams + ); + + foreach (($response->user ?? []) as $user) { + if ((string)$user->status !== 'ACTIVE') { + continue; + } + if ($patron) { + // More than one match, cannot log in by email + $this->debug( + "Email $username matches more than one user, cannot" + . ' login' + ); + return null; + } + $patron = [ + 'id' => (string)$user->primary_id, + 'cat_username' => trim($username), + 'email' => trim($username) + ]; + } + } + if (!$patron) { + return null; + } + // Use primary id in further queries + $patronId = $patron['id']; + } elseif ('password' === $loginMethod) { + // Create parameters for API call + $getParams = [ + 'user_id_type' => 'all_unique', + 'op' => 'auth', + 'password' => $password + ]; + + // Try to authenticate the user with Alma + list($response, $status) = $this->makeRequest( + '/users/' . urlencode($username), + $getParams, + [], + 'POST', + null, + null, + [400], + true + ); + if (400 === $status) { + return null; + } + } elseif ('vufind' !== $loginMethod) { + $this->logError("Invalid login method configured: $loginMethod"); + throw new ILSException('Invalid login method configured'); + } + + // Create parameters for API call $getParams = [ 'user_id_type' => 'all_unique', - 'view' => 'brief', + 'view' => 'full', 'expand' => 'none' ]; // Check for patron in Alma - $response = $this->makeRequest('/users/' . urlencode($barcode), $getParams); + $response = $this->makeRequest( + '/users/' . urlencode($patronId), + $getParams + ); - // Test once we have access - if ($response != null) { - return [ - 'cat_username' => trim($barcode), - 'cat_password' => trim($password) - ]; + if ($response !== null) { + // We may already have some information, so just fill the gaps + $patron['id'] = (string)$response->primary_id; + $patron['cat_username'] = trim($username); + $patron['cat_password'] = trim($password); + $patron['firstname'] = (string)$response->first_name ?? ''; + $patron['lastname'] = (string)$response->last_name ?? ''; + $patron['email'] = $this->getPreferredEmail($response); + return $patron; } return null; @@ -645,53 +783,54 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function getMyProfile($patron) { - $patronId = $patron['cat_username']; + $patronId = $patron['id']; $xml = $this->makeRequest('/users/' . $patronId); if (empty($xml)) { return []; } $profile = [ - 'firstname' => (isset($xml->first_name)) - ? (string)$xml->first_name - : null, - 'lastname' => (isset($xml->last_name)) - ? (string)$xml->last_name - : null, - 'group' => (isset($xml->user_group['desc'])) - ? (string)$xml->user_group['desc'] - : null, + 'firstname' => (isset($xml->first_name)) + ? (string)$xml->first_name + : null, + 'lastname' => (isset($xml->last_name)) + ? (string)$xml->last_name + : null, + 'group' => isset($xml->user_group) + ? $this->getTranslatableString($xml->user_group) + : null, 'group_code' => (isset($xml->user_group)) - ? (string)$xml->user_group - : null + ? (string)$xml->user_group + : null ]; $contact = $xml->contact_info; if ($contact) { if ($contact->addresses) { $address = $contact->addresses[0]->address; - $profile['address1'] = (isset($address->line1)) - ? (string)$address->line1 - : null; - $profile['address2'] = (isset($address->line2)) - ? (string)$address->line2 - : null; - $profile['address3'] = (isset($address->line3)) - ? (string)$address->line3 - : null; - $profile['zip'] = (isset($address->postal_code)) - ? (string)$address->postal_code - : null; - $profile['city'] = (isset($address->city)) - ? (string)$address->city - : null; - $profile['country'] = (isset($address->country)) - ? (string)$address->country - : null; + $profile['address1'] = (isset($address->line1)) + ? (string)$address->line1 + : null; + $profile['address2'] = (isset($address->line2)) + ? (string)$address->line2 + : null; + $profile['address3'] = (isset($address->line3)) + ? (string)$address->line3 + : null; + $profile['zip'] = (isset($address->postal_code)) + ? (string)$address->postal_code + : null; + $profile['city'] = (isset($address->city)) + ? (string)$address->city + : null; + $profile['country'] = (isset($address->country)) + ? (string)$address->country + : null; } if ($contact->phones) { $profile['phone'] = (isset($contact->phones[0]->phone->phone_number)) - ? (string)$contact->phones[0]->phone->phone_number - : null; + ? (string)$contact->phones[0]->phone->phone_number + : null; } + $profile['email'] = $this->getPreferredEmail($xml); } // Cache the user group code @@ -713,21 +852,19 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function getMyFines($patron) { $xml = $this->makeRequest( - '/users/' . $patron['cat_username'] . '/fees' + '/users/' . $patron['id'] . '/fees' ); $fineList = []; foreach ($xml as $fee) { + $created = (string)$fee->creation_time; $checkout = (string)$fee->status_time; $fineList[] = [ - "title" => (string)$fee->type, - "amount" => $fee->original_amount * 100, - "balance" => $fee->balance * 100, - "checkout" => $this->dateConverter->convert( - 'Y-m-d H:i', - 'm-d-Y', - $checkout - ), - "fine" => (string)$fee->type['desc'] + "title" => (string)($fee->title ?? ''), + "amount" => round(floatval($fee->original_amount) * 100), + "balance" => round(floatval($fee->balance) * 100), + "createdate" => $this->parseDate($created, true), + "checkout" => $this->parseDate($checkout, true), + "fine" => $this->getTranslatableString($fee->type) ]; } return $fineList; @@ -747,16 +884,31 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function getMyHolds($patron) { $xml = $this->makeRequest( - '/users/' . $patron['cat_username'] . '/requests', + '/users/' . $patron['id'] . '/requests', ['request_type' => 'HOLD'] ); $holdList = []; foreach ($xml as $request) { + $lastInterestDate = $request->last_interest_date + ? $this->dateConverter->convertToDisplayDate( + 'Y-m-dT', (string)$request->last_interest_date + ) : null; + $available = (string)$request->request_status === 'On Hold Shelf'; + $lastPickupDate = null; + if ($available) { + $lastPickupDate = $request->expiry_date + ? $this->dateConverter->convertToDisplayDate( + 'Y-m-dT', (string)$request->expiry_date + ) : null; + } $holdList[] = [ - 'create' => (string)$request->request_date, - 'expire' => (string)$request->last_interest_date, + 'create' => $this->dateConverter->convertToDisplayDate( + 'Y-m-dT', (string)$request->request_date + ), + 'expire' => $lastInterestDate, 'id' => (string)$request->request_id, - 'in_transit' => (string)$request->request_status !== 'On Hold Shelf', + 'available' => $available, + 'last_pickup_date' => $lastPickupDate, 'item_id' => (string)$request->mms_id, 'location' => (string)$request->pickup_location, 'processed' => $request->item_policy === 'InterlibraryLoan' @@ -811,7 +963,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function cancelHolds($cancelDetails) { $returnArray = []; - $patronId = $cancelDetails['patron']['cat_username']; + $patronId = $cancelDetails['patron']['id']; $count = 0; foreach ($cancelDetails['details'] as $requestId) { @@ -895,7 +1047,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function getMyStorageRetrievalRequests($patron) { $xml = $this->makeRequest( - '/users/' . $patron['cat_username'] . '/requests', + '/users/' . $patron['id'] . '/requests', ['request_type' => 'MOVE'] ); $holdList = []; @@ -935,7 +1087,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function getMyILLRequests($patron) { $xml = $this->makeRequest( - '/users/' . $patron['cat_username'] . '/requests', + '/users/' . $patron['id'] . '/requests', ['request_type' => 'MOVE'] ); $holdList = []; @@ -965,41 +1117,55 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface * Get transactions of the current patron. * * @param array $patron The patron array from patronLogin + * @param array $params Parameters * - * @return string[] Transaction information as array or empty array if the - * patron has no transactions. + * @return array Transaction information as array. * * @author Michael Birkner */ - public function getMyTransactions($patron) + public function getMyTransactions($patron, $params = []) { // Defining the return value $returnArray = []; - // Get the patrons user name - $patronUserName = $patron['cat_username']; + // Get the patron id + $patronId = $patron['id']; // Create a timestamp for calculating the due / overdue status - $nowTS = mktime(); + $nowTS = time(); + + $sort = explode( + ' ', !empty($params['sort']) ? $params['sort'] : 'checkout desc', 2 + ); + if ($sort[0] == 'checkout') { + $sortKey = 'loan_date'; + } elseif ($sort[0] == 'title') { + $sortKey = 'title'; + } else { + $sortKey = 'due_date'; + } + $direction = (isset($sort[1]) && 'desc' === $sort[1]) ? 'DESC' : 'ASC'; - // Create parameters for the API call - // INFO: "order_by" does not seem to work as expected! - // This is an Alma API problem. + $pageSize = $params['limit'] ?? 50; $params = [ - 'limit' => '100', - 'order_by' => 'due_date', - 'direction' => 'DESC', + 'limit' => $pageSize, + 'offset' => isset($params['page']) + ? ($params['page'] - 1) * $pageSize : 0, + 'order_by' => $sortKey, + 'direction' => $direction, 'expand' => 'renewable' ]; // Get user loans from Alma API $apiResult = $this->makeRequest( - '/users/' . $patronUserName . '/loans/', + '/users/' . $patronId . '/loans', $params ); // If there is an API result, process it + $totalCount = 0; if ($apiResult) { + $totalCount = $apiResult->attributes()->total_record_count; // Iterate over all item loans foreach ($apiResult->item_loan as $itemLoan) { $loan['duedate'] = $this->parseDate( @@ -1023,12 +1189,14 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface //$loan['message'] = ; $loan['title'] = (string)$itemLoan->title; $loan['item_id'] = (string)$itemLoan->loan_id; - $loan['institution_name'] = (string)$itemLoan->library; + $loan['institution_name'] + = $this->getTranslatableString($itemLoan->library); //$loan['isbn'] = ; //$loan['issn'] = ; //$loan['oclc'] = ; //$loan['upc'] = ; - $loan['borrowingLocation'] = (string)$itemLoan->circ_desk; + $loan['borrowingLocation'] + = $this->getTranslatableString($itemLoan->circ_desk); // Calculate due status $dueDateTS = strtotime($loan['duedate']); @@ -1044,7 +1212,10 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface } } - return $returnArray; + return [ + 'count' => $totalCount, + 'records' => $returnArray + ]; } /** @@ -1077,7 +1248,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface public function renewMyItems($renewDetails) { $returnArray = []; - $patronUserName = $renewDetails['patron']['cat_username']; + $patronId = $renewDetails['patron']['id']; foreach ($renewDetails['details'] as $loanId) { // Create an empty array that holds the information for a renewal @@ -1086,7 +1257,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface try { // POST the renewals to Alma $apiResult = $this->makeRequest( - '/users/' . $patronUserName . '/loans/' . $loanId . '/?op=renew', + '/users/' . $patronId . '/loans/' . $loanId . '/?op=renew', [], [], 'POST' @@ -1132,7 +1303,9 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function getStatus($id) { - return $this->getHolding($id); + $idList = [$id]; + $status = $this->getStatuses($idList); + return current($status); } /** @@ -1147,60 +1320,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function getStatuses($ids) { - $results = []; - $params = [ - 'mms_id' => implode(',', $ids), - 'expand' => 'p_avail,e_avail,d_avail' - ]; - if ($bibs = $this->makeRequest('/bibs', $params)) { - foreach ($bibs as $bib) { - $marc = new \File_MARCXML( - $bib->record->asXML(), - \File_MARCXML::SOURCE_STRING - ); - $status = []; - $tmpl = [ - 'id' => (string)$bib->mms_id, - 'source' => 'Solr', - 'callnumber' => isset($bib->isbn) - ? (string)$bib->isbn - : '' - ]; - if ($record = $marc->next()) { - // Physical - $physicalItems = $record->getFields('AVA'); - foreach ($physicalItems as $field) { - $avail = $field->getSubfield('e')->getData(); - $item = $tmpl; - $item['availability'] = strtolower($avail) === 'available'; - $item['location'] = (string)$field->getSubfield('c') - ->getData(); - $status[] = $item; - } - // Electronic - $electronicItems = $record->getFields('AVE'); - foreach ($electronicItems as $field) { - $avail = $field->getSubfield('e')->getData(); - $item = $tmpl; - $item['availability'] = strtolower($avail) === 'available'; - $status[] = $item; - } - // Digital - $digitalItems = $record->getFields('AVD'); - foreach ($digitalItems as $field) { - $avail = $field->getSubfield('e')->getData(); - $item = $tmpl; - $item['availability'] = strtolower($avail) === 'available'; - $status[] = $item; - } - } else { - // TODO: Throw error - error_log('no record'); - } - $results[] = $status; - } - } - return $results; + return $this->getStatusesForInventoryTypes($ids, $this->getInventoryTypes()); } /** @@ -1232,8 +1352,33 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface */ public function getConfig($function, $params = null) { + if ($function == 'patronLogin') { + return [ + 'loginMethod' => $this->config['Catalog']['loginMethod'] ?? 'vufind' + ]; + } if (isset($this->config[$function])) { $functionConfig = $this->config[$function]; + + // Set default value for "itemLimit" in Alma driver + if ($function === 'Holdings') { + // Use itemLimit in Holds as fallback for backward compatibility + $functionConfig['itemLimit'] = ($functionConfig['itemLimit'] + ?? $this->config['Holds']['itemLimit'] + ?? 10) ?: 10; + } + } elseif ('getMyTransactions' === $function) { + $functionConfig = [ + 'max_results' => 100, + 'sort' => [ + 'checkout desc' => 'sort_checkout_date_desc', + 'checkout asc' => 'sort_checkout_date_asc', + 'due desc' => 'sort_due_date_desc', + 'due asc' => 'sort_due_date_asc', + 'title asc' => 'sort_title' + ], + 'default_sort' => 'due asc' + ]; } else { $functionConfig = false; } @@ -1261,7 +1406,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $mmsId = $holdDetails['id']; $holId = $holdDetails['holding_id']; $itmId = $holdDetails['item_id']; - $patronCatUsername = $holdDetails['patron']['cat_username']; + $patronId = $holdDetails['patron']['id']; $pickupLocation = $holdDetails['pickUpLocation'] ?? null; $comment = $holdDetails['comment'] ?? null; $requiredBy = (isset($holdDetails['requiredBy'])) @@ -1295,8 +1440,8 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface // Create HTTP client with Alma API URL for title level requests $client = $this->httpService->createClient( $this->baseUrl . '/bibs/' . urlencode($mmsId) - . '/requests?apiKey=' . urlencode($this->apiKey) - . '&user_id=' . urlencode($patronCatUsername) + . '/requests?apikey=' . urlencode($this->apiKey) + . '&user_id=' . urlencode($patronId) . '&format=json' ); } else { @@ -1305,8 +1450,8 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $this->baseUrl . '/bibs/' . urlencode($mmsId) . '/holdings/' . urlencode($holId) . '/items/' . urlencode($itmId) - . '/requests?apiKey=' . urlencode($this->apiKey) - . '&user_id=' . urlencode($patronCatUsername) + . '/requests?apikey=' . urlencode($this->apiKey) + . '&user_id=' . urlencode($patronId) . '&format=json' ); } @@ -1345,6 +1490,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface return [ 'success' => false, 'sysMessage' => $error->errorList->error[0]->errorMessage + ?? 'hold_error_fail' ]; } @@ -1411,8 +1557,9 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $xml = $this->makeRequest('/courses/' . $courseID . '/reading-lists'); $reserves = []; foreach ($xml as $list) { + $listId = $list->id; $listXML = $this->makeRequest( - "/courses/${$courseID}/reading-lists/${$list->id}/citations" + "/courses/${$courseID}/reading-lists/${$listId}/citations" ); foreach ($listXML as $citation) { $reserves[$citation->id] = $citation->metadata; @@ -1433,8 +1580,8 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface { // Remove trailing Z from end of date // e.g. from Alma we get dates like 2012-07-13Z without time, which is wrong) - if (strpos($date, 'Z', (strlen($date) - 1))) { - $date = preg_replace('/Z{1}$/', '', $date); + if (strpos($date, 'T') === false && substr($date, -1) === 'Z') { + $date = substr($date, 0, -1); } $compactDate = "/^[0-9]{8}$/"; // e. g. 20120725 @@ -1442,7 +1589,9 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface $euro = "/^[0-9]+\/[0-9]+\/[0-9]{4}$/"; // e. g. 13/7/2012 $euroPad = "/^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}$/"; // e. g. 13/07/2012 $datestamp = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/"; // e. g. 2012-07-13 - $timestamp = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/"; + $timestamp = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/"; + $timestampMs + = "/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z$/"; // e. g. 2017-07-09T18:00:00 if ($date == null || $date == '') { @@ -1457,11 +1606,23 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface return $this->dateConverter->convertToDisplayDate('d/m/y', $date); } elseif (preg_match($datestamp, $date) === 1) { return $this->dateConverter->convertToDisplayDate('Y-m-d', $date); - } elseif (preg_match($timestamp, substr($date, 0, 19)) === 1) { + } elseif (preg_match($timestamp, $date) === 1) { if ($withTime) { return $this->dateConverter->convertToDisplayDateAndTime( - 'Y-m-d\TH:i:s', - substr($date, 0, 19) + 'Y-m-d\TH:i:sT', + $date + ); + } else { + return $this->dateConverter->convertToDisplayDate( + 'Y-m-d', + substr($date, 0, 10) + ); + } + } elseif (preg_match($timestampMs, $date) === 1) { + if ($withTime) { + return $this->dateConverter->convertToDisplayDateAndTime( + 'Y-m-d\TH:i:s#???T', + $date ); } else { return $this->dateConverter->convertToDisplayDate( @@ -1474,6 +1635,246 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface } } + /** + * Helper method to determine whether or not a certain method can be + * called on this driver. Required method for any smart drivers. + * + * @param string $method The name of the called method. + * @param array $params Array of passed parameters + * + * @return bool True if the method can be called with the given parameters, + * false otherwise. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function supportsMethod($method, $params) + { + return is_callable([$this, $method]); + } + + /** + * Get the inventory types to be displayed. Possible values are: + * p_avail,e_avail,d_avail + * + * @return array + */ + protected function getInventoryTypes() + { + $types = explode( + ':', + $this->config['Holdings']['inventoryTypes'] + ?? 'physical:digital:electronic' + ); + + $result = []; + $map = [ + 'physical' => 'p_avail', + 'digital' => 'd_avail', + 'electronic' => 'e_avail' + ]; + $types = array_flip($types); + foreach ($map as $src => $dest) { + if (isset($types[$src])) { + $result[] = $dest; + } + } + + return $result; + } + + /** + * Get Statuses for inventory types + * + * This is responsible for retrieving the status information for a + * collection of records with specified inventory types. + * + * @param array $ids The array of record ids to retrieve the status for + * @param array $types Inventory types + * + * @return array An array of getStatus() return values on success. + */ + protected function getStatusesForInventoryTypes($ids, $types) + { + $results = []; + $params = [ + 'mms_id' => implode(',', $ids), + 'expand' => implode(',', $types) + ]; + if ($bibs = $this->makeRequest('/bibs', $params)) { + foreach ($bibs as $bib) { + $marc = new \File_MARCXML( + $bib->record->asXML(), + \File_MARCXML::SOURCE_STRING + ); + $status = []; + $tmpl = [ + 'id' => (string)$bib->mms_id, + 'source' => 'Solr', + 'callnumber' => '', + 'reserve' => 'N', + ]; + if ($record = $marc->next()) { + // Physical + $physicalItems = $record->getFields('AVA'); + foreach ($physicalItems as $field) { + $avail = $this->getMarcSubfield($field, 'e'); + $item = $tmpl; + $item['availability'] = strtolower($avail) === 'available'; + $item['location'] = $this->getMarcSubfield($field, 'c'); + $item['callnumber'] = $this->getMarcSubfield($field, 'd'); + $status[] = $item; + } + // Electronic + $electronicItems = $record->getFields('AVE'); + foreach ($electronicItems as $field) { + $avail = $this->getMarcSubfield($field, 'e'); + $item = $tmpl; + $item['availability'] = strtolower($avail) === 'available'; + // Use the following subfields for location: + // m (Collection name) + // i (Available for library) + // d (Available for library) + // b (Available for library) + $location = [ + $this->getMarcSubfield($field, 'm') ?: 'Get full text' + ]; + foreach (['i', 'd', 'b'] as $code) { + if ($content = $this->getMarcSubfield($field, $code)) { + $location[] = $content; + } + } + $item['location'] = implode(' - ', $location); + $item['callnumber'] = $this->getMarcSubfield($field, 't'); + $url = $this->getMarcSubfield($field, 'u'); + if (preg_match('/^https?:\/\//', $url)) { + $item['locationhref'] = $url; + } + $item['status'] = $this->getMarcSubfield($field, 's') + ?: null; + if ($note = $this->getMarcSubfield($field, 'n')) { + $item['item_notes'] = [$note]; + } + $status[] = $item; + } + // Digital + $deliveryUrl + = $this->config['Holdings']['digitalDeliveryUrl'] ?? ''; + $digitalItems = $record->getFields('AVD'); + if ($digitalItems && !$deliveryUrl) { + $this->logWarning( + 'Digital items exist for ' . (string)$bib->mms_id + . ', but digitalDeliveryUrl not set -- unable to' + . ' generate links' + ); + } + foreach ($digitalItems as $field) { + $item = $tmpl; + unset($item['callnumber']); + $item['availability'] = true; + $item['location'] = $this->getMarcSubfield($field, 'e'); + // Using subfield 'd' ('Repository Name') as callnumber + $item['callnumber'] = $this->getMarcSubfield($field, 'd'); + if ($deliveryUrl) { + $item['locationhref'] = str_replace( + '%%id%%', + $this->getMarcSubfield($field, 'b'), + $deliveryUrl + ); + } + $status[] = $item; + } + } + $results[(string)$bib->mms_id] = $status; + } + } + return $results; + } + + /** + * Get the preferred email address for the user (or first one if no preferred one + * is found) + * + * @param SimpleXMLElement $user User data + * + * @return string|null + */ + protected function getPreferredEmail($user) + { + if (!empty($user->contact_info->emails->email)) { + foreach ($user->contact_info->emails->email as $email) { + if ('true' === (string)$email['preferred']) { + return isset($email->email_address) + ? (string)$email->email_address : null; + } + } + $email = $user->contact_info->emails->email[0]; + return isset($email->email_address) + ? (string)$email->email_address : null; + } + return null; + } + + /** + * Gets a translatable string from an element with content and a desc attribute. + * + * @param SimpleXMLElement $element XML element + * + * @return \VuFind\I18n\TranslatableString + */ + protected function getTranslatableString($element) + { + if (null === $element) { + return null; + } + $value = ($this->config['Catalog']['translationPrefix'] ?? '') + . (string)$element; + $desc = (string)($element->attributes()->desc ?? $element); + return new \VuFind\I18n\TranslatableString($value, $desc); + } + + /** + * Gets a translatable string from an element with content and a desc attribute. + * + * @param SimpleXMLElement $element XML element + * + * @return \VuFind\I18n\TranslatableString + */ + protected function getTranslatableStatusString($element) + { + if (null === $element) { + return null; + } + $value = 'status_' . strtolower((string)$element); + $desc = (string)($element->attributes()->desc ?? $element); + return new \VuFind\I18n\TranslatableString($value, $desc); + } + + /** + * Get a MARC subfield from a MARC field + * + * @param \File_MARC_Subfield $field MARC Field + * @param string $subfield Subfield code + * + * @return string + */ + protected function getMarcSubfield($field, $subfield) + { + $subfield = $field->getSubfield($subfield); + return false === $subfield ? '' : $subfield->getData(); + } + + /** + * Get location for an item + * + * @param SimpleXMLElement $item Item + * + * @return \VuFind\I18n\TranslatableString|string + */ + protected function getItemLocation($item) + { + return $this->getTranslatableString($item->item_data->location); + } + // @codingStandardsIgnoreStart /** @@ -1485,23 +1886,5 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface * } */ - /* ================= METHODS INACCESSIBLE OUTSIDE OF GET ================== */ - - /** - * @param array $cancelDetails An associative array with two keys: - * patron (array returned by the driver's patronLogin method) - * details (array returned by the driver's getCancelHoldDetails) - * - * @return array count – The number of items successfully cancelled - * items – Associative array where keyed by item_id (getMyHolds) - * success – Boolean true or false - * status – A status message from the language file (required) - * sysMessage - A system supplied failure message (optional) - * / - * public function cancelHolds($cancelDetails) { - * // https://developers.exlibrisgroup.com/alma/apis/users - * // DELETE /almaws/v1/users/{user_id}/requests/{request_id} - * } - */ // @codingStandardsIgnoreEnd } diff --git a/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php b/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php index fad09dc0c16a72cbc7ecb8c76b1360d2e85a7484..a4d46669aeb6675b6db3d9a3b32f9b03d1625380 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/AlmaFactory.php @@ -66,15 +66,15 @@ class AlmaFactory implements FactoryInterface // Set up the driver with the date converter (and any extra parameters // passed in as options): $driver = new $requestedName( - $container->get('VuFind\Date\Converter'), - $container->get('VuFind\Config\PluginManager'), + $container->get(\VuFind\Date\Converter::class), + $container->get(\VuFind\Config\PluginManager::class), ...($options ?: []) ); // Populate cache storage if a setCacheStorage method is present: if (method_exists($driver, 'setCacheStorage')) { $driver->setCacheStorage( - $container->get('VuFind\Cache\Manager')->getCache('object') + $container->get(\VuFind\Cache\Manager::class)->getCache('object') ); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Amicus.php b/module/VuFind/src/VuFind/ILS/Driver/Amicus.php index e267986e82a5c7a10e895d4eec29e4df65e5a7ec..75224a2e7cb6d4807cfc351049b02aa888f88207 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Amicus.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Amicus.php @@ -437,16 +437,19 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { include_once 'File/MARC.php'; @@ -607,7 +610,7 @@ class Amicus extends AbstractBase implements TranslatorAwareInterface "AS DUEDATE, CIRT_ITM.BIB_ITM_NBR AS BIB_ID " . "FROM LV_USER, CIRT_ITM " . "WHERE LV_USER.PRSN_NBR = CIRT_ITM.PRSN_NBR " . - "AND LV_USER.LOGIN = '" . $patron['id'] . "'"; + "AND LV_USER.LOGIN = '" . $patron['id'] . "'"; try { $sqlStmt = $this->db->prepare($sql); $sqlStmt->execute(); diff --git a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php index 52b5d4d9ea2fea939f411139c343872ea814667c..828df269f2ed657563aa45db8987853ed52877c0 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DAIA.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DAIA.php @@ -383,14 +383,17 @@ class DAIA extends AbstractBase implements * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Demo.php b/module/VuFind/src/VuFind/ILS/Driver/Demo.php index 003e2757bb19709fe4a3cbe53922abaca9e9819f..273d890c6e67617d48f54b0036b424a7b6df49cc 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Demo.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Demo.php @@ -69,9 +69,9 @@ class Demo extends AbstractBase /** * Container for storing persistent simulated ILS data. * - * @var SessionContainer + * @var SessionContainer[] */ - protected $session = null; + protected $session = []; /** * Factory function for constructing the SessionContainer. @@ -290,7 +290,7 @@ class Demo extends AbstractBase ? $this->config['Records']['query'] : '*:*'; $result = $this->searchService->random($source, new Query($query), 1); if (count($result) === 0) { - throw new \Exception('Problem retrieving random record from $source.'); + throw new \Exception("Problem retrieving random record from $source."); } $record = current($result->getRecords()); return [$record->getUniqueId(), $record->getTitle()]; @@ -376,7 +376,7 @@ class Demo extends AbstractBase $status = $this->getFakeStatus(); $location = $this->getFakeLoc(); $locationhref = ($location === 'Campus A') ? 'http://campus-a' : false; - return [ + $result = [ 'id' => $id, 'source' => $this->getRecordSource(), 'item_id' => $number, @@ -396,8 +396,26 @@ class Demo extends AbstractBase 'addStorageRetrievalRequestLink' => $patron ? 'check' : false, 'ILLRequest' => 'auto', 'addILLRequestLink' => $patron ? 'check' : false, - 'services' => $status == 'Available' ? $this->getFakeServices() : [] + 'services' => $status == 'Available' ? $this->getFakeServices() : [], ]; + + switch (rand(1, 5)) { + case 1: + $result['location'] = 'Digital copy available'; + $result['locationhref'] = 'http://digital'; + $result['__electronic__'] = true; + $result['availability'] = true; + $result['status'] = ''; + break; + case 2: + $result['location'] = 'Electronic Journals'; + $result['locationhref'] = 'http://electronic'; + $result['__electronic__'] = true; + $result['availability'] = true; + $result['status'] = 'Available from ' . rand(2010, 2019); + } + + return $result; } /** @@ -525,19 +543,38 @@ class Demo extends AbstractBase return $this->getSimulatedStatus($id); } + /** + * Get suppressed records. + * + * @return array ID numbers of suppressed records in the system. + */ + public function getSuppressedRecords() + { + return $this->config['Records']['suppressed'] ?? []; + } + /** * Get the session container (constructing it on demand if not already present) * + * @param string $patron ID of current patron + * * @return SessionContainer */ - protected function getSession() + protected function getSession($patron = null) { + // We have a separate session for each user ID; if none is specified, + // try to pick the first one arbitrarily; the difference only matters + // when testing multiple accounts. + $selectedPatron = empty($patron) + ? (current(array_keys($this->session)) ?: 'default') + : md5($patron); + // SessionContainer not defined yet? Build it now: - if (null === $this->session) { + if (!isset($this->session[$selectedPatron])) { $factory = $this->sessionFactory; - $this->session = $factory(); + $this->session[$selectedPatron] = $factory($selectedPatron); } - return $this->session; + return $this->session[$selectedPatron]; } /** @@ -557,7 +594,7 @@ class Demo extends AbstractBase $id = (string)$id; // Do we have a fake status persisted in the session? - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (isset($session->statuses[$id])) { return $session->statuses[$id]; } @@ -566,7 +603,7 @@ class Demo extends AbstractBase $holding = []; $records = rand() % 15; for ($i = 1; $i <= $records; $i++) { - $holding[] = $this->getRandomHolding($id, $i, $patron); + $holding[] = $this->setStatus($id, [], true, $patron); } return $holding; } @@ -579,16 +616,17 @@ class Demo extends AbstractBase * number, barcode, availability, status, location, * reserve, callnumber, duedate, is_holdable, and addLink * @param bool $append add another record or replace current record + * @param array $patron Patron data * * @return array */ - protected function setStatus($id, $holding = [], $append = true) + protected function setStatus($id, $holding = [], $append = true, $patron = null) { $id = (string)$id; - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); $i = isset($session->statuses[$id]) ? count($session->statuses[$id]) + 1 : 1; - $holding = array_merge($this->getRandomHolding($id, $i), $holding); + $holding = array_merge($this->getRandomHolding($id, $i, $patron), $holding); // if statuses is already stored if ($session->statuses) { @@ -629,14 +667,15 @@ class Demo extends AbstractBase * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options * - * @return array On success, an associative array with the following - * keys: id, availability (boolean), status, location, reserve, callnumber, + * @return array On success, an associative array with the following keys: + * id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $this->checkIntermittentFailure(); @@ -651,7 +690,8 @@ class Demo extends AbstractBase $status[$i]['holdings_notes'] = []; $status[$i]['item_notes'] = []; for ($j = 1; $j <= $noteCount; $j++) { - $status[$i]['holdings_notes'][] = "Item $itemNum holdings note $j"; + $status[$i]['holdings_notes'][] = "Item $itemNum holdings note $j" + . ($j === 1 ? ' https://vufind.org/?f=1&b=2#sample_link' : ''); $status[$i]['item_notes'][] = "Item $itemNum note $j"; } $summCount = rand(1, 3); @@ -665,8 +705,46 @@ class Demo extends AbstractBase $status[$i]['enumchron'] = "volume $volume, issue $seriesIssue"; } + // Filter out electronic holdings from the normal holdings list: + $status = array_filter( + $status, + function ($a) { + return !($a['__electronic__'] ?? false); + } + ); + + // Slice out a chunk if pagination is enabled. + $slice = null; + if ($options['itemLimit'] ?? null) { + // For sensible pagination, we need to sort by location: + $callback = function ($a, $b) { + return strcmp($a['location'], $b['location']); + }; + usort($status, $callback); + $slice = array_slice( + $status, + $options['offset'] ?? 0, + $options['itemLimit'] + ); + } + + // Electronic holdings: + $statuses = $this->getStatus($id); + $electronic = []; + foreach ($statuses as $item) { + if ($item['__electronic__'] ?? false) { + // Don't expose internal __electronic__ flag upstream: + unset($item['__electronic__']); + $electronic[] = $item; + } + } + // Send back final value: - return $status; + return [ + 'total' => count($status), + 'holdings' => $slice ?: $status, + 'electronic_holdings' => $electronic + ]; } /** @@ -695,33 +773,42 @@ class Demo extends AbstractBase * * This is responsible for authenticating a patron against the catalog. * - * @param string $barcode The patron barcode + * @param string $username The patron username * @param string $password The patron password * * @throws ILSException * @return mixed Associative array of patron info on successful login, * null on unsuccessful login. */ - public function patronLogin($barcode, $password) + public function patronLogin($username, $password) { $this->checkIntermittentFailure(); + + $user = [ + 'id' => trim($username), + 'firstname' => 'Lib', + 'lastname' => 'Rarian', + 'cat_username' => trim($username), + 'cat_password' => trim($password), + 'email' => 'Lib.Rarian@library.not', + 'major' => null, + 'college' => null + ]; + + $loginMethod = $this->config['Catalog']['loginMethod'] ?? 'password'; + if ('email' === $loginMethod) { + $user['email'] = $username; + $user['cat_password'] = ''; + return $user; + } + if (isset($this->config['Users'])) { - if (!isset($this->config['Users'][$barcode]) - || $password !== $this->config['Users'][$barcode] + if (!isset($this->config['Users'][$username]) + || $password !== $this->config['Users'][$username] ) { return null; } } - $user = []; - - $user['id'] = trim($barcode); - $user['firstname'] = trim("Lib"); - $user['lastname'] = trim("Rarian"); - $user['cat_username'] = trim($barcode); - $user['cat_password'] = trim($password); - $user['email'] = trim("Lib.Rarian@library.not"); - $user['major'] = null; - $user['college'] = null; return $user; } @@ -768,7 +855,7 @@ class Demo extends AbstractBase public function getMyFines($patron) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->fines)) { // How many items are there? %20 - 2 = 10% chance of none, // 90% of 1-18 (give or take some odd maths) @@ -827,7 +914,7 @@ class Demo extends AbstractBase public function getMyHolds($patron) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->holds)) { $session->holds = $this->createRequestList('Holds'); } @@ -848,7 +935,7 @@ class Demo extends AbstractBase public function getMyStorageRetrievalRequests($patron) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->storageRetrievalRequests)) { $session->storageRetrievalRequests = $this->createRequestList('StorageRetrievalRequests'); @@ -870,7 +957,7 @@ class Demo extends AbstractBase public function getMyILLRequests($patron) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->ILLRequests)) { $session->ILLRequests = $this->createRequestList('ILLRequests'); } @@ -893,6 +980,24 @@ class Demo extends AbstractBase : $this->getRandomTransactionList(); } + /** + * Calculate the due status for a due date. + * + * @param int $due Due date as Unix timestamp + * + * @return string + */ + protected function calculateDueStatus($due) + { + $dueRelative = $due - time(); + if ($dueRelative < 0) { + return 'overdue'; + } elseif ($dueRelative < 24 * 60 * 60) { + return 'due'; + } + return false; + } + /** * Construct a random set of transactions for getMyTransactions(). * @@ -909,16 +1014,9 @@ class Demo extends AbstractBase // When is it due? +/- up to 15 days $due_relative = rand() % 30 - 15; // Due date - $dueStatus = false; - if ($due_relative >= 0) { - $rawDueDate = strtotime("now +$due_relative days"); - if ($due_relative == 0) { - $dueStatus = 'due'; - } - } else { - $rawDueDate = strtotime("now $due_relative days"); - $dueStatus = 'overdue'; - } + $rawDueDate = strtotime( + 'now ' . ($due_relative >= 0 ? '+' : '') . $due_relative . ' days' + ); // Times renewed : 0,0,0,0,0,1,2,3,4,5 $renew = rand() % 10 - 5; @@ -944,7 +1042,7 @@ class Demo extends AbstractBase 'U', $rawDueDate ), 'rawduedate' => $rawDueDate, - 'dueStatus' => $dueStatus, + 'dueStatus' => $this->calculateDueStatus($rawDueDate), 'barcode' => sprintf("%08d", rand() % 50000), 'renew' => $renew, 'renewLimit' => $renewLimit, @@ -992,7 +1090,7 @@ class Demo extends AbstractBase public function getMyTransactions($patron, $params = []) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->transactions)) { $session->transactions = $this->getTransactionList(); } @@ -1114,7 +1212,7 @@ class Demo extends AbstractBase public function getMyTransactionHistory($patron, $params) { $this->checkIntermittentFailure(); - $session = $this->getSession(); + $session = $this->getSession($patron['id'] ?? null); if (!isset($session->historicLoans)) { $session->historicLoans = $this->getHistoricTransactionList(); } @@ -1454,7 +1552,7 @@ class Demo extends AbstractBase // cancel. $newHolds = new ArrayObject(); $retVal = ['count' => 0, 'items' => []]; - $session = $this->getSession(); + $session = $this->getSession($cancelDetails['patron']['id'] ?? null); foreach ($session->holds as $current) { if (!in_array($current['reqnum'], $cancelDetails['details'])) { $newHolds->append($current); @@ -1518,7 +1616,7 @@ class Demo extends AbstractBase // cancel. $newRequests = new ArrayObject(); $retVal = ['count' => 0, 'items' => []]; - $session = $this->getSession(); + $session = $this->getSession($cancelDetails['patron']['id'] ?? null); foreach ($session->storageRetrievalRequests as $current) { if (!in_array($current['reqnum'], $cancelDetails['details'])) { $newRequests->append($current); @@ -1591,13 +1689,15 @@ class Demo extends AbstractBase $finalResult = ['blocks' => false, 'details' => []]; // Grab transactions from session so we can modify them: - $session = $this->getSession(); + $session = $this->getSession($renewDetails['patron']['id'] ?? null); $transactions = $session->transactions; foreach ($transactions as $i => $current) { // Only renew requested items: if (in_array($current['item_id'], $renewDetails['details'])) { if (!$this->isFailing(__METHOD__, 50)) { - $transactions[$i]['rawduedate'] += 7 * 24 * 60 * 60; + $transactions[$i]['rawduedate'] += 21 * 24 * 60 * 60; + $transactions[$i]['dueStatus'] + = $this->calculateDueStatus($transactions[$i]['rawduedate']); $transactions[$i]['duedate'] = $this->dateConverter->convertToDisplayDate( 'U', $transactions[$i]['rawduedate'] @@ -1705,7 +1805,7 @@ class Demo extends AbstractBase ]; } - $session = $this->getSession(); + $session = $this->getSession($holdDetails['patron']['id'] ?? null); if (!isset($session->holds)) { $session->holds = new ArrayObject(); } @@ -1827,7 +1927,7 @@ class Demo extends AbstractBase ]; } - $session = $this->getSession(); + $session = $this->getSession($details['patron']['id'] ?? null); if (!isset($session->storageRetrievalRequests)) { $session->storageRetrievalRequests = new ArrayObject(); } @@ -1941,7 +2041,7 @@ class Demo extends AbstractBase ]; } - $session = $this->getSession(); + $session = $this->getSession($details['patron']['id'] ?? null); if (!isset($session->ILLRequests)) { $session->ILLRequests = new ArrayObject(); } @@ -2117,7 +2217,7 @@ class Demo extends AbstractBase // cancel. $newRequests = new ArrayObject(); $retVal = ['count' => 0, 'items' => []]; - $session = $this->getSession(); + $session = $this->getSession($cancelDetails['patron']['id'] ?? null); foreach ($session->ILLRequests as $current) { if (!in_array($current['reqnum'], $cancelDetails['details'])) { $newRequests->append($current); @@ -2208,6 +2308,11 @@ class Demo extends AbstractBase 'defaultRequiredDate' => 'driver:0:2:0', ]; } + if ($function == 'Holdings') { + return [ + 'itemLimit' => $this->config['Holdings']['itemLimit'] ?? null, + ]; + } if ($function == 'StorageRetrievalRequests' && $this->storageRetrievalRequests ) { @@ -2269,6 +2374,12 @@ class Demo extends AbstractBase 'default_sort' => 'due asc' ]; } + if ($function == 'patronLogin') { + return [ + 'loginMethod' + => $this->config['Catalog']['loginMethod'] ?? 'password' + ]; + } return []; } diff --git a/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php b/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php index 307b874ae53d3e80c78145f6d727261c2f54e5bc..f3dc65a72f45e14bb297ee827f0ca931c2f01878 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DemoFactory.php @@ -60,13 +60,13 @@ class DemoFactory extends DriverWithDateConverterFactory if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $sessionFactory = function () use ($container) { - $manager = $container->get('Zend\Session\SessionManager'); - return new \Zend\Session\Container('DemoDriver', $manager); + $sessionFactory = function ($ns) use ($container) { + $manager = $container->get(\Zend\Session\SessionManager::class); + return new \Zend\Session\Container('DemoDriver' . $ns, $manager); }; return parent::__invoke( $container, $requestedName, - [$container->get('VuFindSearch\Service'), $sessionFactory] + [$container->get(\VuFindSearch\Service::class), $sessionFactory] ); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php b/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php index 37cad7d7a888044d40baefc3307c34ed5087b479..22b46c392eefcf8ef240969ce59aa47323444bdd 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DriverInterface.php @@ -101,15 +101,16 @@ interface DriverInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options * * @throws \VuFind\Exception\ILS * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. */ - public function getHolding($id, array $patron = null); + public function getHolding($id, array $patron = null, array $options = []); /** * Get Purchase History diff --git a/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php b/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php index 8fd6268073a509a7df3b707fd1064f056641ba79..9d87a28ddf167e3721e2fb10414716146a5d8376 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/DriverWithDateConverterFactory.php @@ -61,13 +61,13 @@ class DriverWithDateConverterFactory implements FactoryInterface // Set up the driver with the date converter (and any extra parameters // passed in as options): $driver = new $requestedName( - $container->get('VuFind\Date\Converter'), ...($options ?: []) + $container->get(\VuFind\Date\Converter::class), ...($options ?: []) ); // Populate cache storage if a setCacheStorage method is present: if (method_exists($driver, 'setCacheStorage')) { $driver->setCacheStorage( - $container->get('VuFind\Cache\Manager')->getCache('object') + $container->get(\VuFind\Cache\Manager::class)->getCache('object') ); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php b/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php index 76d002315f0f5de2c1baa8675312c6d3c46b8953..4d0d9f2584dd8af158ae7ee47d533e59aef87d77 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Evergreen.php @@ -188,16 +188,19 @@ HERE; * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $holding = []; diff --git a/module/VuFind/src/VuFind/ILS/Driver/Folio.php b/module/VuFind/src/VuFind/ILS/Driver/Folio.php index 356334a88d88f2e14f04d2fe5fe4b18ba435caef..72d1911b8b2c6530bfdc6aa5e8157522f8d6486d 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Folio.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Folio.php @@ -45,6 +45,10 @@ class Folio extends AbstractAPI implements { use \VuFindHttp\HttpServiceAwareTrait; use \VuFind\I18n\Translator\TranslatorAwareTrait; + use \VuFind\Log\LoggerAwareTrait { + logWarning as warning; + logError as error; + } /** * Authentication tenant (X-Okapi-Tenant) @@ -103,6 +107,41 @@ class Folio extends AbstractAPI implements $this->tenant = $this->config['API']['tenant']; } + /** + * Function that obscures and logs debug data + * + * @param string $method Request method GET/POST/PUT/DELETE/etc + * @param string $path Request URL + * @param array $params Request parameters + * @param \Zend\Http\Headers $req_headers Headers object + * + * @return void + */ + protected function debugRequest($method, $path, $params, $req_headers) + { + // Only log non-GET requests + if ($method == 'GET') { + return; + } + // remove passwords + $logParams = $params; + if (isset($logParams['password'])) { + unset($logParams['password']); + } + // truncate headers for token obscuring + $logHeaders = $req_headers->toArray(); + if (isset($logHeaders['X-Okapi-Token'])) { + $logHeaders['X-Okapi-Token'] = substr($val, 0, 30) . '...'; + } + + $this->debug( + $method . ' request.' . + ' URL: ' . $path . '.' . + ' Params: ' . print_r($logParams, true) . '.' . + ' Headers: ' . print_r($logHeaders, true) + ); + } + /** * (From AbstractAPI) Allow default corrections to all requests * @@ -145,6 +184,10 @@ class Folio extends AbstractAPI implements $this->token = $response->getHeaders()->get('X-Okapi-Token') ->getFieldValue(); $this->sessionCache->folio_token = $this->token; + $this->debug( + 'Token renewed. Tenant: ' . $auth['username'] . + ' Token: ' . substr($this->token, 0, 30) . '...' + ); } /** @@ -176,6 +219,9 @@ class Folio extends AbstractAPI implements $this->sessionCache = $factory($this->tenant); if ($this->sessionCache->folio_token ?? false) { $this->token = $this->sessionCache->folio_token; + $this->debug( + 'Token taken from cache: ' . substr($this->token, 0, 30) . '...' + ); } if ($this->token == null) { $this->renewTenantToken(); @@ -285,12 +331,15 @@ class Folio extends AbstractAPI implements /** * This method queries the ILS for holding information. * - * @param string $bibId Bib-level id - * @param array $patron Patron login information from $this->patronLogin + * @param string $bibId Bib-level id + * @param array $patron Patron login information from $this->patronLogin + * @param array $options Extra options (not currently used) * * @return array An array of associative holding arrays + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($bibId, array $patron = null) + public function getHolding($bibId, array $patron = null, array $options = []) { $instance = $this->getInstance($bibId); $query = ['query' => '(instanceId="' . $instance->id . '")']; @@ -327,7 +376,7 @@ class Folio extends AbstractAPI implements 'status' => $item->status->name, 'availability' => $item->status->name == 'Available', 'notes' => $item->notes ?? [], - 'callnumber' => $holding->callNumber, + 'callnumber' => $holding->callNumber ?? '', 'location' => $locationName, 'reserve' => 'TODO', 'addLink' => true @@ -375,6 +424,10 @@ class Folio extends AbstractAPI implements // Replace admin with user as tenant $this->token = $response->getHeaders()->get('X-Okapi-Token') ->getFieldValue(); + $this->debug( + 'User logged in. User: ' . $username . '.' . + ' Token: ' . substr($this->token, 0, 30) . '...' + ); return [ 'id' => $profile->id, 'username' => $username, @@ -461,7 +514,7 @@ class Folio extends AbstractAPI implements */ public function getMyTransactions($patron) { - $query = ['query' => 'userId==' . $patron['username']]; + $query = ['query' => 'userId==' . $patron['id']]; $response = $this->makeRequest("GET", '/circulation/loans', $query); $json = json_decode($response->getBody()); if (count($json->loans) == 0) { @@ -469,15 +522,16 @@ class Folio extends AbstractAPI implements } $transactions = []; foreach ($json->loans as $trans) { - $dueDate = date_create($trans['dueDate']); + $date = date_create($trans->dueDate); $transactions[] = [ 'duedate' => date_format($date, "j M Y"), 'dueTime' => date_format($date, "g:i:s a"), // TODO: Due Status // 'dueStatus' => $trans['itemId'], - 'id' => $trans['itemId'], - 'barcode' => $trans['item']['barcode'], - 'title' => $trans['item']['title'], + 'id' => $trans->item->instanceId, + 'item_id' => $trans->item->id, + 'barcode' => $trans->item->barcode, + 'title' => $trans->item->title, ]; } return $transactions; diff --git a/module/VuFind/src/VuFind/ILS/Driver/FolioFactory.php b/module/VuFind/src/VuFind/ILS/Driver/FolioFactory.php index a37d4b6a1a5f2adce682bce1ad586d1b33534d6f..677d6c2319eb7d4b32ca4580500e78ea0bcf064e 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/FolioFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/FolioFactory.php @@ -61,7 +61,7 @@ class FolioFactory extends DriverWithDateConverterFactory throw new \Exception('Unexpected options passed to factory.'); } $sessionFactory = function ($namespace) use ($container) { - $manager = $container->get('Zend\Session\SessionManager'); + $manager = $container->get(\Zend\Session\SessionManager::class); return new \Zend\Session\Container("Folio_$namespace", $manager); }; return parent::__invoke($container, $requestedName, [$sessionFactory]); diff --git a/module/VuFind/src/VuFind/ILS/Driver/Horizon.php b/module/VuFind/src/VuFind/ILS/Driver/Horizon.php index 953767656f3202e56f4fffe9eaa3e51b852e5b8b..6ee8a0d3882ec4a8dd825efe72c9e3769420bfad 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Horizon.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Horizon.php @@ -332,16 +332,19 @@ class Horizon extends AbstractBase implements LoggerAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $sqlArray = $this->getHoldingSql($id); $sql = $this->buildSqlFromArray($sqlArray); diff --git a/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php b/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php index 3359cac62304244704cfe0efaca37ee19cd6c23c..aec518b811a894810865b76287d08d4866467fc3 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php +++ b/module/VuFind/src/VuFind/ILS/Driver/HorizonXMLAPI.php @@ -546,7 +546,7 @@ class HorizonXMLAPI extends Horizon implements \VuFindHttp\HttpServiceAwareInter $initResponse = $this->makeRequest($params); if ($initResponse->request_confirm) { - $confirmParams = [ + $confirmParams = [ "session" => $session, "profile" => $this->wsProfile, "bibkey" => $requestDetails['bibId'], diff --git a/module/VuFind/src/VuFind/ILS/Driver/Innovative.php b/module/VuFind/src/VuFind/ILS/Driver/Innovative.php index 127b000e90f85ab97ccd4fa94e6490c97db20cb1..4bf2da95e8002aff14260909825b76a430f07c92 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Innovative.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Innovative.php @@ -267,16 +267,19 @@ class Innovative extends AbstractBase implements * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Koha.php b/module/VuFind/src/VuFind/ILS/Driver/Koha.php index 7999f80d4119dca3b8cc907fad1e111bceb540c9..b57e07abfe2789e3e34f5c266001a346276df0b6 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Koha.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Koha.php @@ -157,16 +157,19 @@ class Koha extends AbstractBase * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $holding = []; $available = true; @@ -229,7 +232,7 @@ class Koha extends AbstractBase //Retrieving the location (shelf types) $shelf = $rowItem['LOCATION']; $loc = (null != $shelf) - ? $loc . ": " . $this->locCodes[$shelf] + ? $loc . ": " . ($this->locCodes[$shelf] ?? $shelf) : $loc . ": " . 'Unknown'; //A default value is stored for null diff --git a/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php b/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php index 49c0bb9c8d6d04821baf48ba2ac867bb81f09c3c..e528c429e46eb357652f527f37d184c50fb8a524 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php +++ b/module/VuFind/src/VuFind/ILS/Driver/KohaILSDI.php @@ -482,7 +482,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements "Y-m-d", $display_date ); - $checkTime = $this->dateConverter->convertFromDisplayDate( + $checkTime = $this->dateConverter->convertFromDisplayDate( "U", $display_date ); if (!is_numeric($checkTime)) { @@ -770,16 +770,19 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $this->debug( "Function getHolding($id, " @@ -1029,7 +1032,7 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements $rescount = 0; foreach ($itemSqlStmt->fetchAll() as $rowItem) { - $items[] = [ + $items[] = [ 'id' => $rowItem['id'] ]; $rescount++; @@ -1618,12 +1621,11 @@ class KohaILSDI extends \VuFind\ILS\Driver\AbstractBase implements } $sql = "SELECT b.title, b.biblionumber, - MAX(CONCAT(s.publisheddate, ' / ',s.serialseq)) + CONCAT(s.publisheddate, ' / ',s.serialseq) AS 'date and enumeration' FROM serial s LEFT JOIN biblio b USING (biblionumber) WHERE s.STATUS=2 and b.biblionumber = :id - GROUP BY b.biblionumber ORDER BY s.publisheddate DESC"; $sqlStmt = $this->db->prepare($sql); diff --git a/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php b/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php index db1a3ec4684bb40c3dadf2c3c26c037a8d7b1d59..d1c2cb57516a2ce98014ae6fdd8d0e41c61e6300 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php +++ b/module/VuFind/src/VuFind/ILS/Driver/MultiBackend.php @@ -245,14 +245,17 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $source = $this->getSource($id); $driver = $this->getDriver($source); @@ -265,7 +268,8 @@ class MultiBackend extends AbstractBase implements \Zend\Log\LoggerAwareInterfac } $holdings = $driver->getHolding( $this->getLocalId($id), - $this->stripIdPrefixes($patron, $source) + $this->stripIdPrefixes($patron, $source), + $options ); return $this->addIdPrefixes($holdings, $source); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php b/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php index f58c05904bd43bfe6dea435352194ca91e08bad1..232f916b67c4dd5b91b1530b1565d75a7e82dd54 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/MultiBackendFactory.php @@ -62,9 +62,9 @@ class MultiBackendFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager'), - $container->get('VuFind\Auth\ILSAuthenticator'), - $container->get('VuFind\ILS\Driver\PluginManager') + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Auth\ILSAuthenticator::class), + $container->get(\VuFind\ILS\Driver\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php b/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php index 2d0e9a6738256c0217d861769ca04f78b2cd0e46..052ed53d0330d8825a4eea19211f4f6dc3d0203c 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php +++ b/module/VuFind/src/VuFind/ILS/Driver/NewGenLib.php @@ -84,14 +84,17 @@ class NewGenLib extends AbstractBase * * @param string $RecordID The record id to retrieve the holdings for * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($RecordID, array $patron = null) + public function getHolding($RecordID, array $patron = null, array $options = []) { $holding = $this->getItemStatus($RecordID); for ($i = 0; $i < count($holding); $i++) { diff --git a/module/VuFind/src/VuFind/ILS/Driver/NoILS.php b/module/VuFind/src/VuFind/ILS/Driver/NoILS.php index 15972e22ac091dc229ac1d0567546652f9d44984..0d97256071a847cb6efd1cd81f6d7655efaa04d3 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/NoILS.php +++ b/module/VuFind/src/VuFind/ILS/Driver/NoILS.php @@ -192,15 +192,18 @@ class NoILS extends AbstractBase implements TranslatorAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $useHoldings = $this->config['settings']['useHoldings'] ?? 'none'; diff --git a/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php b/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php index d0db0202e247d15e02b54389c71a4ae468d785c3..08654dbe28058faa4a0d28a46da233a673e9aea7 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/NoILSFactory.php @@ -61,6 +61,6 @@ class NoILSFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - return new $requestedName($container->get('VuFind\Record\Loader')); + return new $requestedName($container->get(\VuFind\Record\Loader::class)); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/PAIA.php b/module/VuFind/src/VuFind/ILS/Driver/PAIA.php index d5abc56d15384e7ff7ce70073e3131f66855b1ab..12d7b9ef4d7a815ae129135e63747e939d315c98 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PAIA.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PAIA.php @@ -31,6 +31,8 @@ */ namespace VuFind\ILS\Driver; +use VuFind\Exception\Auth as AuthException; +use VuFind\Exception\Forbidden as ForbiddenException; use VuFind\Exception\ILS as ILSException; /** @@ -100,7 +102,30 @@ class PAIA extends DAIA ]; /** - * PAIA constructor. + * PAIA scopes as defined in + * http://gbv.github.io/paia/paia.html#access-tokens-and-scopes + * + * Notice: logged in users should ALWAYS have scope read_patron as the PAIA + * driver performs paiaGetUserDetails() upon each call of VuFind's patronLogin(). + * That means if paiaGetUserDetails() fails (which is the case if the patron has + * NOT the scope read_patron) the patronLogin() will fail as well even though + * paiaLogin() might have succeeded. Any other scope not being available for the + * patron will be handled more or less gracefully through exception handling. + */ + const SCOPE_READ_PATRON = 'read_patron'; + const SCOPE_UPDATE_PATRON = 'update_patron'; + const SCOPE_UPDATE_PATRON_NAME = 'update_patron_name'; + const SCOPE_UPDATE_PATRON_EMAIL = 'update_patron_email'; + const SCOPE_UPDATE_PATRON_ADDRESS = 'update_patron_address'; + const SCOPE_READ_FEES = 'read_fees'; + const SCOPE_READ_ITEMS = 'read_items'; + const SCOPE_WRITE_ITEMS = 'write_items'; + const SCOPE_CHANGE_PASSWORD = 'change_password'; + const SCOPE_READ_NOTIFICATIONS = 'read_notifications'; + const SCOPE_DELETE_NOTIFICATIONS = 'delete_notifications'; + + /** + * Constructor * * @param \VuFind\Date\Converter $converter Date converter * @param \Zend\Session\SessionManager $sessionManager Session Manager @@ -227,6 +252,14 @@ class PAIA extends DAIA */ public function cancelHolds($cancelDetails) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) { + throw new ForbiddenException( + 'Exception::access_denied_write_items' + ); + } + $it = $cancelDetails['details']; $items = []; foreach ($it as $item) { @@ -239,7 +272,7 @@ class PAIA extends DAIA $array_response = $this->paiaPostAsArray( 'core/' . $patron['cat_username'] . '/cancel', $post_data ); - } catch (ILSException $e) { + } catch (\Exception $e) { $this->debug($e->getMessage()); return [ 'success' => false, @@ -260,7 +293,7 @@ class PAIA extends DAIA $elements = $array_response['doc']; foreach ($elements as $element) { $item_id = $element['item']; - if ($element['error']) { + if ($element['error'] ?? false) { $details[$item_id] = [ 'success' => false, 'status' => $element['error'], @@ -304,6 +337,14 @@ class PAIA extends DAIA */ public function changePassword($details) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_CHANGE_PASSWORD)) { + throw new ForbiddenException( + 'Exception::access_denied_change_password' + ); + } + $post_data = [ "patron" => $details['patron']['cat_username'], "username" => $details['patron']['cat_username'], @@ -315,7 +356,12 @@ class PAIA extends DAIA $array_response = $this->paiaPostAsArray( 'auth/change', $post_data ); - } catch (ILSException $e) { + } catch (AuthException $e) { + return [ + 'success' => false, + 'status' => 'password_error_auth_old' + ]; + } catch (\Exception $e) { $this->debug($e->getMessage()); return [ 'success' => false, @@ -573,10 +619,15 @@ class PAIA extends DAIA */ public function getMyFines($patron) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_READ_FEES)) { + throw new ForbiddenException('Exception::access_denied_read_fines'); + } + $fees = $this->paiaGetAsArray( 'core/' . $patron['cat_username'] . '/fees' ); - // PAIA simple data type money: a monetary value with currency (format // [0-9]+\.[0-9][0-9] [A-Z][A-Z][A-Z]), for instance 0.80 USD. $feeConverter = function ($fee) { @@ -656,14 +707,14 @@ class PAIA extends DAIA */ public function getMyHolds($patron) { - // filters for getMyHolds are: + // filters for getMyHolds are by default configuration: // status = 1 - reserved (the document is not accessible for the patron yet, // but it will be) // 4 - provided (the document is ready to be used by the patron) - $filter = ['status' => [1, 4]]; + $status = $this->config['Holds']['status'] ?? '1:4'; + $filter = ['status' => explode(':', $status)]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); - return $this->mapPaiaItems($items, 'myHoldsMapping'); } @@ -678,29 +729,51 @@ class PAIA extends DAIA */ public function getMyProfile($patron) { - //todo: read VCard if avaiable in patron info - //todo: make fields more configurable if (is_array($patron)) { + $type = isset($patron['type']) + ? implode( + ', ', array_map( + [$this, 'getReadableGroupType'], (array)$patron['type'] + ) + ) + : null; return [ 'firstname' => $patron['firstname'], 'lastname' => $patron['lastname'], - 'address1' => null, + 'address1' => $patron['address'], 'address2' => null, 'city' => null, 'country' => null, 'zip' => null, 'phone' => null, - 'group' => null, + 'mobile_phone' => null, + 'group' => $type, // PAIA specific custom values 'expires' => isset($patron['expires']) ? $this->convertDate($patron['expires']) : null, 'statuscode' => $patron['status'] ?? null, - 'canWrite' => in_array('write_items', $this->getScope()), + 'canWrite' => in_array(self::SCOPE_WRITE_ITEMS, $this->getScope()), ]; } return []; } + /** + * Get Readable Group Type + * + * Due to PAIA specifications type returns an URI. This method offers a + * possibility to translate the URI in a readable value by inheritance + * and implementing a personal proceeding. + * + * @param string $type URI of usertype + * + * @return string URI of usertype + */ + protected function getReadableGroupType($type) + { + return $type; + } + /** * Get Patron Transactions * @@ -713,12 +786,12 @@ class PAIA extends DAIA */ public function getMyTransactions($patron) { - // filters for getMyTransactions are: + // filters for getMyTransactions are by default configuration: // status = 3 - held (the document is on loan by the patron) - $filter = ['status' => [3]]; + $status = $this->config['Transactions']['status'] ?? '3'; + $filter = ['status' => explode(':', $status)]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); - return $this->mapPaiaItems($items, 'myTransactionsMapping'); } @@ -734,12 +807,12 @@ class PAIA extends DAIA */ public function getMyStorageRetrievalRequests($patron) { - // filters for getMyStorageRetrievalRequests are: + // filters for getMyStorageRetrievalRequests are by default configuration: // status = 2 - ordered (the document is ordered by the patron) - $filter = ['status' => [2]]; + $status = $this->config['StorageRetrievalRequests']['status'] ?? '2'; + $filter = ['status' => explode(':', $status)]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); - return $this->mapPaiaItems($items, 'myStorageRetrievalRequestsMapping'); } @@ -838,14 +911,10 @@ class PAIA extends DAIA // if we already have a session with access_token and patron id, try to get // patron info with session data if (isset($session->expires) && $session->expires > time()) { - try { - return $this->enrichUserDetails( - $this->paiaGetUserDetails($session->patron), - $password - ); - } catch (ILSException $e) { - $this->debug('Session expired, login again', ['info' => 'info']); - } + return $this->enrichUserDetails( + $this->paiaGetUserDetails($session->patron), + $password + ); } try { if ($this->paiaLogin($username, $password)) { @@ -854,8 +923,99 @@ class PAIA extends DAIA $password ); } - } catch (ILSException $e) { - throw new ILSException($e->getMessage()); + } catch (AuthException $e) { + // swallow auth exceptions and return null compliant to spec at: + // https://vufind.org/wiki/development:plugins:ils_drivers#patronlogin + return null; + } + } + + /** + * Handle PAIA request errors and throw appropriate exception. + * + * @param array $array Array containing error messages + * + * @return void + * + * @throws AuthException + * @throws ILSException + */ + protected function paiaHandleErrors($array) + { + // TODO: also have exception contain content of 'error' as for at least + // error code 403 two differing errors are possible + // (cf. http://gbv.github.io/paia/paia.html#request-errors) + if (isset($array['error'])) { + switch ($array['error']) { + // cf. http://gbv.github.io/paia/paia.html#request-errors + // error code error_description + // access_denied 403 Wrong or missing credentials to get an + // access token + case 'access_denied': + throw new AuthException( + $array['error_description'] ?? $array['error'], + $array['code'] ?? '' + ); + + // invalid_grant 401 The access token was missing, invalid + // or expired + case 'invalid_grant': + + // insufficient_scope 403 The access token was accepted but + // it lacks permission for the request + case 'insufficient_scope': + throw new ForbiddenException( + $array['error_description'] ?? $array['error'], + $array['code'] ?? '' + ); + + // not_found 404 Unknown request URL or unknown patron. + // Implementations SHOULD first check + // authentication and prefer error invalid_grant + // or access_denied to prevent leaking patron + // identifiers. + case 'not_found': + + // not_implemented 501 Known but unsupported request URL (for + // instance a PAIA auth server server may + // not implement + // http://example.org/core/change) + case 'not_implemented': + + // invalid_request 405 Unexpected HTTP verb + // invalid_request 400 Malformed request (for instance error + // parsing JSON, unsupported request content + // type, etc.) + // invalid_request 422 The request parameters could be parsed + // but they don’t match the request method + // (for instance missing fields, invalid + // values, etc.) + case 'invalid_request': + + // internal_error 500 An unexpected error occurred. This + // error corresponds to a bug in the + // implementation of a PAIA auth/core server + case 'internal_error': + + // service_unavailable 503 The request couldn’t be serviced + // because of a temporary failure + case 'service_unavailable': + + // bad_gateway 502 The request couldn’t be serviced because + // of a backend failure (for instance the library + // system’s database) + case 'bad_gateway': + + // gateway_timeout 504 The request couldn’t be serviced + // because of a backend failure + case 'gateway_timeout': + + default: + throw new ILSException( + $array['error_description'] ?? $array['error'], + $array['code'] ?? '' + ); + } } } @@ -914,6 +1074,14 @@ class PAIA extends DAIA */ public function placeHold($holdDetails) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) { + throw new ForbiddenException( + 'Exception::access_denied_write_items' + ); + } + $item = $holdDetails['item_id']; $patron = $holdDetails['patron']; @@ -928,7 +1096,7 @@ class PAIA extends DAIA $array_response = $this->paiaPostAsArray( 'core/' . $patron['cat_username'] . '/request', $post_data ); - } catch (ILSException $e) { + } catch (\Exception $e) { $this->debug($e->getMessage()); return [ 'success' => false, @@ -1006,6 +1174,14 @@ class PAIA extends DAIA */ public function renewMyItems($details) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) { + throw new ForbiddenException( + 'Exception::access_denied_write_items' + ); + } + $it = $details['details']; $items = []; foreach ($it as $item) { @@ -1018,7 +1194,7 @@ class PAIA extends DAIA $array_response = $this->paiaPostAsArray( 'core/' . $patron['cat_username'] . '/renew', $post_data ); - } catch (ILSException $e) { + } catch (\Exception $e) { $this->debug($e->getMessage()); return [ 'success' => false, @@ -1107,6 +1283,12 @@ class PAIA extends DAIA */ protected function paiaGetItems($patron, $filter = []) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_READ_ITEMS)) { + throw new ForbiddenException('Exception::access_denied_read_items'); + } + // check for existing data in cache if ($this->paiaCacheEnabled) { $itemsResponse = $this->getCachedData($patron['cat_username']); @@ -1243,7 +1425,8 @@ class PAIA extends DAIA $result['item_id'] = ($doc['item'] ?? ''); $result['cancel_details'] - = (isset($doc['cancancel']) && $doc['cancancel']) + = (isset($doc['cancancel']) && $doc['cancancel'] + && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) ? $result['item_id'] : ''; // edition (0..1) URI of a the document (no particular copy) @@ -1258,7 +1441,7 @@ class PAIA extends DAIA $result['location'] = ($doc['storage'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['position'] = ($doc['queue'] ?? null); + $result['position'] = ($doc['queue'] ?? null); // only true if status == 4 $result['available'] = false; @@ -1296,7 +1479,7 @@ class PAIA extends DAIA // Optional VuFind fields /* $result['reqnum'] = null; - $result['volume'] = null; + $result['volume'] = null; $result['publication_year'] = null; $result['isbn'] = null; $result['issn'] = null; @@ -1371,28 +1554,18 @@ class PAIA extends DAIA $results = []; foreach ($items as $doc) { - $result = []; - // canrenew (0..1) whether a document can be renewed (bool) - $result['renewable'] = ($doc['canrenew'] ?? false); + $result = $this->getBasicDetails($doc); - // item (0..1) URI of a particular copy - $result['item_id'] = ($doc['item'] ?? ''); + // canrenew (0..1) whether a document can be renewed (bool) + $result['renewable'] = (isset($doc['canrenew']) + && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) + ? $doc['canrenew'] : false; $result['renew_details'] - = (isset($doc['canrenew']) && $doc['canrenew']) + = (isset($doc['canrenew']) && $doc['canrenew'] + && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS)) ? $result['item_id'] : ''; - // edition (0..1) URI of a the document (no particular copy) - // hook for retrieving alternative ItemId in case PAIA does not - // the needed id - $result['id'] = (isset($doc['edition']) - ? $this->getAlternativeItemId($doc['edition']) : ''); - - // requested (0..1) URI that was originally requested - - // about (0..1) textual description of the document - $result['title'] = ($doc['about'] ?? null); - // queue (0..1) number of waiting requests for the document or item $result['request'] = ($doc['queue'] ?? null); @@ -1428,10 +1601,6 @@ class PAIA extends DAIA // storageid (0..1) location URI - // PAIA custom field - // label (0..1) call number, shelf mark or similar item label - $result['callnumber'] = $this->getCallNumber($doc); - // Optional VuFind fields /* $result['barcode'] = null; @@ -1472,17 +1641,13 @@ class PAIA extends DAIA $http_headers['Authorization'] = 'Bearer ' . $access_token; } - try { - $result = $this->httpService->post( - $this->paiaURL . $file, - $postData, - 'application/json; charset=UTF-8', - $this->paiaTimeout, - $http_headers - ); - } catch (\Exception $e) { - throw new ILSException($e->getMessage()); - } + $result = $this->httpService->post( + $this->paiaURL . $file, + $postData, + 'application/json; charset=UTF-8', + $this->paiaTimeout, + $http_headers + ); if (!$result->isSuccess()) { // log error for debugging @@ -1510,16 +1675,10 @@ class PAIA extends DAIA 'Authorization' => 'Bearer ' . $access_token, 'Content-type' => 'application/json; charset=UTF-8', ]; - - try { - $result = $this->httpService->get( - $this->paiaURL . $file, - [], $this->paiaTimeout, $http_headers - ); - } catch (\Exception $e) { - throw new ILSException($e->getMessage()); - } - + $result = $this->httpService->get( + $this->paiaURL . $file, + [], $this->paiaTimeout, $http_headers + ); if (!$result->isSuccess()) { // log error for debugging $this->debug( @@ -1537,17 +1696,16 @@ class PAIA extends DAIA * @param string $file JSON data * * @return mixed - * @throws ILSException + * @throws \Exception */ protected function paiaParseJsonAsArray($file) { $responseArray = json_decode($file, true); + // if we have an error response handle it accordingly (any will throw an + // exception at the moment) and pass on the resulting exception if (isset($responseArray['error'])) { - throw new ILSException( - $responseArray['error'], - $responseArray['code'] - ); + $this->paiaHandleErrors($responseArray); } return $responseArray; @@ -1567,14 +1725,7 @@ class PAIA extends DAIA $file, $this->getSession()->access_token ); - - try { - $responseArray = $this->paiaParseJsonAsArray($responseJson); - } catch (ILSException $e) { - $this->debug($e->getCode() . ':' . $e->getMessage()); - return []; - } - + $responseArray = $this->paiaParseJsonAsArray($responseJson); return $responseArray; } @@ -1594,15 +1745,7 @@ class PAIA extends DAIA $data, $this->getSession()->access_token ); - - try { - $responseArray = $this->paiaParseJsonAsArray($responseJson); - } catch (ILSException $e) { - $this->debug($e->getCode() . ':' . $e->getMessage()); - /* TODO: do not return empty array, this causes eventually confusion */ - return []; - } - + $responseArray = $this->paiaParseJsonAsArray($responseJson); return $responseArray; } @@ -1623,22 +1766,15 @@ class PAIA extends DAIA "username" => $username, "password" => $password, "grant_type" => "password", - "scope" => "read_patron read_fees read_items write_items " . - "change_password" + "scope" => self::SCOPE_READ_PATRON . " " . + self::SCOPE_READ_FEES . " " . + self::SCOPE_READ_ITEMS . " " . + self::SCOPE_WRITE_ITEMS . " " . + self::SCOPE_CHANGE_PASSWORD ]; $responseJson = $this->paiaPostRequest('auth/login', $post_data); - try { - $responseArray = $this->paiaParseJsonAsArray($responseJson); - } catch (ILSException $e) { - if ($e->getMessage() === 'access_denied') { - return false; - } - throw new ILSException( - $e->getCode() . ':' . $e->getMessage() - ); - } - + $responseArray = $this->paiaParseJsonAsArray($responseJson); if (!isset($responseArray['access_token'])) { throw new ILSException( 'Unknown error! Access denied.' @@ -1678,20 +1814,34 @@ class PAIA extends DAIA */ protected function paiaGetUserDetails($patron) { + // check if user has appropriate scope (refer to scope declaration above for + // further details) + if (!$this->paiaCheckScope(self::SCOPE_READ_PATRON)) { + throw new ForbiddenException( + 'Exception::access_denied_read_patron' + ); + } + $responseJson = $this->paiaGetRequest( 'core/' . $patron, $this->getSession()->access_token ); - - try { - $responseArray = $this->paiaParseJsonAsArray($responseJson); - } catch (ILSException $e) { - throw new ILSException( - $e->getMessage(), $e->getCode() - ); - } + $responseArray = $this->paiaParseJsonAsArray($responseJson); return $this->paiaParseUserDetails($patron, $responseArray); } + /** + * Checks if the current scope is set for active session. + * + * @param string $scope The scope to test for with the current session scopes. + * + * @return boolean + */ + protected function paiaCheckScope($scope) + { + return (!empty($scope) && is_array($this->getScope())) + ? in_array($scope, $this->getScope()) : false; + } + /** * Check if storage retrieval request available * @@ -1728,10 +1878,186 @@ class PAIA extends DAIA // TODO: make this more configurable if (isset($patron['status']) && $patron['status'] == 0 && isset($patron['expires']) && $patron['expires'] > date('Y-m-d') - && in_array('write_items', $this->getScope()) + && in_array(self::SCOPE_WRITE_ITEMS, $this->getScope()) ) { return true; } return false; } + + /** + * PAIA support method for PAIA core method 'notifications' + * + * @param array $patron Array with patron information + * + * @return array|mixed Array of system notifications for the patron + * @throws \Exception + * @throws ILSException You are not entitled to read notifications + */ + protected function paiaGetSystemMessages($patron) + { + // check if user has appropriate scope + if (!$this->paiaCheckScope(self::SCOPE_READ_NOTIFICATIONS)) { + throw new ILSException('You are not entitled to read notifications.'); + } + + if ($this->paiaCacheEnabled) { + $cacheKey = $this->getCacheKey( + 'notifications_' . $patron['cat_username'] + ); + $response = $this->getCachedData($cacheKey); + if (!empty($response)) { + return $response; + } + } + + try { + $response = $this->paiaGetAsArray( + 'core/' . $patron['cat_username'] . '/notifications' + ); + } catch (\Exception $e) { + // all error handling is done in paiaHandleErrors + // so pass on the exception + throw $e; + } + + $response = $this->enrichNotifications($response); + + if ($this->paiaCacheEnabled) { + $this->putCachedData($cacheKey, $response); + } + + return $response; + } + + /** + * Enriches PAIA notifications response with additional mappings + * + * @param array $notifications list of PAIA notifications + * + * @return array list of enriched PAIA notifications + */ + protected function enrichNotifications(array $notifications) + { + // not yet implemented + return $notifications; + } + + /** + * PAIA support method for PAIA core method DELETE 'notifications' + * + * @param array $patron Array with patron information + * @param string $messageId PAIA service specific ID + * of the notification to remove + * @param bool $keepCache if set to TRUE the notification cache will survive + * the remote operation, this is used by + * \VuFind\ILS\Driver\PAIA::paiaRemoveSystemMessages + * to avoid unnecessary cache operations + * + * @return array|mixed Array of system notifications for the patron + * @throws \Exception + * @throws ILSException You are not entitled to read notifications + */ + protected function paiaRemoveSystemMessage( + $patron, $messageId, $keepCache = false + ) { + // check if user has appropriate scope + if (!$this->paiaCheckScope(self::SCOPE_DELETE_NOTIFICATIONS)) { + throw new ILSException('You are not entitled to delete notifications.'); + } + + try { + $response = $this->paiaDeleteRequest( + 'core/' + . $patron['cat_username'] + . '/notifications/' + . $this->getPaiaNotificationsId($messageId) + ); + } catch (\Exception $e) { + // all error handling is done in paiaHandleErrors + // so pass on the exception + throw $e; + } + + if (!$keepCache && $this->paiaCacheEnabled) { + $this->removeCachedData( + $this->getCacheKey('notifications_' . $patron['cat_username']) + ); + } + + return $response; + } + + /** + * Removes multiple System Messages. Bulk deletion is not implemented in PAIA, + * so this method iterates over the set of IDs and removes them separately + * + * @param array $patron Array with patron information + * @param array $messageIds list of PAIA service specific IDs + * of the notifications to remove + * + * @return bool TRUE if all messages have been successfully removed, + * otherwise FALSE + * @throws ILSException + */ + protected function paiaRemoveSystemMessages($patron, array $messageIds) + { + foreach ($messageIds as $messageId) { + if (!$this->paiaRemoveSystemMessage($patron, $messageId, true)) { + return false; + } + } + + if ($this->paiaCacheEnabled) { + $this->removeCachedData( + $this->getCacheKey('notifications_' . $patron['cat_username']) + ); + } + + return true; + } + + /** + * DELETE data on foreign host + * + * @param string $file DELETE target URL + * @param string $access_token PAIA access token for current session + * + * @return bool|string + * @throws ILSException + */ + protected function paiaDeleteRequest($file, $access_token = null) + { + if (null === $access_token) { + $access_token = $this->getSession()->access_token; + } + + $http_headers = [ + 'Authorization' => 'Bearer ' . $access_token, + 'Content-type' => 'application/json; charset=UTF-8', + ]; + + try { + $client = $this->httpService->createClient( + $this->paiaURL . $file, + \Zend\Http\Request::METHOD_DELETE, + $this->paiaTimeout + ); + $client->setHeaders($http_headers); + $result = $client->send(); + } catch (\Exception $e) { + throw new ILSException($e->getMessage()); + } + + if (!$result->isSuccess()) { + // log error for debugging + $this->debug( + 'HTTP status ' . $result->getStatusCode() . + ' received' + ); + return false; + } + // return TRUE on success + return true; + } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php b/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php index 64db51aab4bc0e4ec660c5b839dff9c28397e232..f7d54d8a9d33b6a96f1dea47794b989dbf9242aa 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PAIAFactory.php @@ -62,7 +62,7 @@ class PAIAFactory extends DriverWithDateConverterFactory } return parent::__invoke( $container, $requestedName, - [$container->get('Zend\Session\SessionManager')] + [$container->get(\Zend\Session\SessionManager::class)] ); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php index 0382cdc0b012b1c9c73941fc1b54d2d70e088487..63bda229895370de25af58a2ac211f852e27c1e7 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/ILS/Driver/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\ILS\Driver; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * ILS driver plugin manager * @@ -44,33 +46,33 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'aleph' => 'VuFind\ILS\Driver\Aleph', - 'alma' => 'VuFind\ILS\Driver\Alma', - 'amicus' => 'VuFind\ILS\Driver\Amicus', - 'daia' => 'VuFind\ILS\Driver\DAIA', - 'demo' => 'VuFind\ILS\Driver\Demo', - 'evergreen' => 'VuFind\ILS\Driver\Evergreen', - 'folio' => 'VuFind\ILS\Driver\Folio', - 'horizon' => 'VuFind\ILS\Driver\Horizon', - 'horizonxmlapi' => 'VuFind\ILS\Driver\HorizonXMLAPI', - 'innovative' => 'VuFind\ILS\Driver\Innovative', - 'koha' => 'VuFind\ILS\Driver\Koha', - 'kohailsdi' => 'VuFind\ILS\Driver\KohaILSDI', - 'lbs4' => 'VuFind\ILS\Driver\LBS4', - 'multibackend' => 'VuFind\ILS\Driver\MultiBackend', - 'newgenlib' => 'VuFind\ILS\Driver\NewGenLib', - 'noils' => 'VuFind\ILS\Driver\NoILS', - 'paia' => 'VuFind\ILS\Driver\PAIA', - 'polaris' => 'VuFind\ILS\Driver\Polaris', - 'sample' => 'VuFind\ILS\Driver\Sample', - 'sierra' => 'VuFind\ILS\Driver\Sierra', - 'sierrarest' => 'VuFind\ILS\Driver\SierraRest', - 'symphony' => 'VuFind\ILS\Driver\Symphony', - 'unicorn' => 'VuFind\ILS\Driver\Unicorn', - 'virtua' => 'VuFind\ILS\Driver\Virtua', - 'voyager' => 'VuFind\ILS\Driver\Voyager', - 'voyagerrestful' => 'VuFind\ILS\Driver\VoyagerRestful', - 'xcncip2' => 'VuFind\ILS\Driver\XCNCIP2', + 'aleph' => Aleph::class, + 'alma' => Alma::class, + 'amicus' => Amicus::class, + 'daia' => DAIA::class, + 'demo' => Demo::class, + 'evergreen' => Evergreen::class, + 'folio' => Folio::class, + 'horizon' => Horizon::class, + 'horizonxmlapi' => HorizonXMLAPI::class, + 'innovative' => Innovative::class, + 'koha' => Koha::class, + 'kohailsdi' => KohaILSDI::class, + 'lbs4' => LBS4::class, + 'multibackend' => MultiBackend::class, + 'newgenlib' => NewGenLib::class, + 'noils' => NoILS::class, + 'paia' => PAIA::class, + 'polaris' => Polaris::class, + 'sample' => Sample::class, + 'sierra' => Sierra::class, + 'sierrarest' => SierraRest::class, + 'symphony' => Symphony::class, + 'unicorn' => Unicorn::class, + 'virtua' => Virtua::class, + 'voyager' => Voyager::class, + 'voyagerrestful' => VoyagerRestful::class, + 'xcncip2' => XCNCIP2::class, ]; /** @@ -79,47 +81,33 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\ILS\Driver\Aleph' => 'VuFind\ILS\Driver\AlephFactory', - 'VuFind\ILS\Driver\Alma' => 'VuFind\ILS\Driver\AlmaFactory', - 'VuFind\ILS\Driver\Amicus' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\DAIA' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\Demo' => 'VuFind\ILS\Driver\DemoFactory', - 'VuFind\ILS\Driver\Evergreen' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\Folio' => 'VuFind\ILS\Driver\FolioFactory', - 'VuFind\ILS\Driver\Horizon' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\HorizonXMLAPI' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\Innovative' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\Koha' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\KohaILSDI' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\LBS4' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\MultiBackend' => 'VuFind\ILS\Driver\MultiBackendFactory', - 'VuFind\ILS\Driver\NewGenLib' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\NoILS' => 'VuFind\ILS\Driver\NoILSFactory', - 'VuFind\ILS\Driver\PAIA' => 'VuFind\ILS\Driver\PAIAFactory', - 'VuFind\ILS\Driver\Polaris' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\Sample' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\Sierra' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\SierraRest' => 'VuFind\ILS\Driver\SierraRestFactory', - 'VuFind\ILS\Driver\Symphony' => 'VuFind\ILS\Driver\SymphonyFactory', - 'VuFind\ILS\Driver\Unicorn' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\Virtua' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\ILS\Driver\Voyager' => - 'VuFind\ILS\Driver\DriverWithDateConverterFactory', - 'VuFind\ILS\Driver\VoyagerRestful' => - 'VuFind\ILS\Driver\VoyagerRestfulFactory', - 'VuFind\ILS\Driver\XCNCIP2' => - 'Zend\ServiceManager\Factory\InvokableFactory', + Aleph::class => AlephFactory::class, + Alma::class => AlmaFactory::class, + Amicus::class => InvokableFactory::class, + DAIA::class => DriverWithDateConverterFactory::class, + Demo::class => DemoFactory::class, + Evergreen::class => InvokableFactory::class, + Folio::class => FolioFactory::class, + Horizon::class => DriverWithDateConverterFactory::class, + HorizonXMLAPI::class => DriverWithDateConverterFactory::class, + Innovative::class => InvokableFactory::class, + Koha::class => DriverWithDateConverterFactory::class, + KohaILSDI::class => DriverWithDateConverterFactory::class, + LBS4::class => DriverWithDateConverterFactory::class, + MultiBackend::class => MultiBackendFactory::class, + NewGenLib::class => InvokableFactory::class, + NoILS::class => NoILSFactory::class, + PAIA::class => PAIAFactory::class, + Polaris::class => InvokableFactory::class, + Sample::class => InvokableFactory::class, + Sierra::class => InvokableFactory::class, + SierraRest::class => SierraRestFactory::class, + Symphony::class => SymphonyFactory::class, + Unicorn::class => DriverWithDateConverterFactory::class, + Virtua::class => InvokableFactory::class, + Voyager::class => DriverWithDateConverterFactory::class, + VoyagerRestful::class => VoyagerRestfulFactory::class, + XCNCIP2::class => InvokableFactory::class, ]; /** @@ -134,7 +122,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\ILS\Driver\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -146,6 +134,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\ILS\Driver\DriverInterface'; + return DriverInterface::class; } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Polaris.php b/module/VuFind/src/VuFind/ILS/Driver/Polaris.php index ced82a5cba77c08f324f1384c375408f83247ae1..94f4826b36f721232ab4772fe7483920a3a91842 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Polaris.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Polaris.php @@ -353,14 +353,17 @@ class Polaris extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @return mixed On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Sample.php b/module/VuFind/src/VuFind/ILS/Driver/Sample.php index ce24eccb7932c6a9b82165d90dac656583d4ebde..d8a39b1e92e41c8c3f9048b1680d464a38f26c8b 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Sample.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Sample.php @@ -107,8 +107,9 @@ class Sample extends AbstractBase * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @return mixed On success, an associative array with the following keys: * id, availability (boolean), status, location, reserve, callnumber, duedate, @@ -116,7 +117,7 @@ class Sample extends AbstractBase * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Sierra.php b/module/VuFind/src/VuFind/ILS/Driver/Sierra.php index f42e5f3439091bdca1acba6e4d350e3e82daa900..45ffb54b1591fd242813533034c3cb406a75fe9c 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Sierra.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Sierra.php @@ -461,16 +461,19 @@ class Sierra extends AbstractBase implements TranslatorAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { try { $holdings = []; diff --git a/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php b/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php index 8dc0282d8c030fe9b32c4da539e792d1a4e407f4..3f7420ed65ae4c6a2a18b48a1e8b84b9c10707b9 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php +++ b/module/VuFind/src/VuFind/ILS/Driver/SierraRest.php @@ -4,7 +4,7 @@ * * PHP version 7 * - * Copyright (C) The National Library of Finland 2016-2018. + * Copyright (C) The National Library of Finland 2016-2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -118,7 +118,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, /** * Item statuses that allow placing a hold * - * @var unknown + * @var array */ protected $validHoldStatuses; @@ -138,16 +138,32 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'p' => '', 'z' => 'Claims Returned', 's' => 'On Search', - 'd' => 'In Process' + 'd' => 'In Process', + '-' => 'On Shelf', + 'Charged' => 'Charged', ]; /** * Available API version * + * Functionality requiring a specific minimum version: + * + * v5: + * - last pickup date for holds + * v5.1 (technically still v5 but added in a later revision): + * - summary holdings information (especially for serials) + * * @var int */ protected $apiVersion = 5; + /** + * Whether to sort items by enumchron. Default is true. + * + * @var array + */ + protected $sortItemsByEnumChron; + /** * Constructor * @@ -233,6 +249,9 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $this->apiVersion = $this->config['Catalog']['api_version']; } + $this->sortItemsByEnumChron + = $this->config['Holdings']['sort_by_enum_chron'] ?? true; + // Init session cache for session-specific data $namespace = md5( $this->config['Catalog']['host'] . '|' @@ -283,8 +302,9 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @return mixed On success, an associative array with the following keys: * id, availability (boolean), status, location, reserve, callnumber, duedate, @@ -292,7 +312,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getItemStatusesForBib($id, true); } @@ -571,7 +591,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, ); $transaction['volume'] = $this->extractVolume($item); if (!empty($item['bibIds'])) { - $transaction['id'] = $item['bibIds'][0]; + $transaction['id'] = $this->formatBibId($item['bibIds'][0]); // Fetch bib information $bib = $this->getBibRecord( @@ -707,7 +727,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, ); $transaction['volume'] = $this->extractVolume($item); if (!empty($item['bibIds'])) { - $transaction['id'] = $item['bibIds'][0]; + $transaction['id'] = $this->formatBibId($item['bibIds'][0]); // Fetch bib information $bib = $this->getBibRecord( @@ -803,7 +823,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'Y-m-d', $entry['pickupByDate'] ) : ''; $holds[] = [ - 'id' => $bibId, + 'id' => $this->formatBibId($bibId), 'requestId' => $this->extractId($entry['id']), 'item_id' => $itemId ? $itemId : $this->extractId($entry['id']), // note that $entry['pickupLocation']['name'] may contain misleading @@ -1030,7 +1050,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, ? $holdDetails['pickUpLocation'] : $this->defaultPickUpLocation; $itemId = $holdDetails['item_id'] ?? false; $comment = $holdDetails['comment'] ?? ''; - $bibId = $holdDetails['id']; + $bibId = $this->extractBibId($holdDetails['id']); // Convert last interest date from Display Format to Sierra's required format try { @@ -1166,7 +1186,7 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'Y-m-d', $entry['assessedDate'] ), 'checkout' => '', - 'id' => $bibId, + 'id' => $this->formatBibId($bibId), 'title' => $title ]; } @@ -1457,9 +1477,13 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $this->error('Could not parse the III CAS login form'); throw new ILSException('Problem with Sierra login.'); } + $usernameField = $this->config['Authentication']['username_field'] + ?? 'code'; + $passwordField = $this->config['Authentication']['password_field'] + ?? 'pin'; $postParams = [ - 'code' => $patron['cat_username'], - 'pin' => $patron['cat_password'], + $usernameField => $patron['cat_username'], + $passwordField => $patron['cat_password'], ]; foreach ($doc->getElementsByTagName('input') as $input) { if ($input->getAttribute('type') == 'hidden') { @@ -1597,6 +1621,23 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return 'SierraRest-' . md5($this->config['Catalog']['host'] . "|$key"); } + /** + * Extract a bib call number from a bib record (if configured to do so). + * + * @param array $bib Bib record + * + * @return string + */ + protected function getBibCallNumber($bib) + { + $result = empty($this->config['CallNumber']['bib_fields']) + ? '' : $this->extractFieldsFromApiData( + [$bib], // wrap $bib in array to conform to expected format + $this->config['CallNumber']['bib_fields'] + ); + return is_array($result) ? reset($result) : $result; + } + /** * Get Item Statuses * @@ -1610,20 +1651,50 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, */ protected function getItemStatusesForBib($id) { - $bib = $this->getBibRecord($id, 'bibLevel'); + // If we need to look at bib call numbers, retrieve varFields: + $bibFields = empty($this->config['CallNumber']['bib_fields']) + ? 'bibLevel' : 'bibLevel,varFields'; + $bib = $this->getBibRecord($id, $bibFields); + $holdingsData = []; + if ($this->apiVersion >= 5.1) { + $holdingsResult = $this->makeRequest( + ['v5', 'holdings'], + [ + 'bibIds' => $this->extractBibId($id), + //'deleted' => 'false', + //'suppressed' => 'false', + 'fields' => 'fixedFields,varFields' + ], + 'GET' + ); + if (!empty($holdingsResult['entries'])) { + foreach ($holdingsResult['entries'] as $entry) { + $location = ''; + foreach ($entry['fixedFields'] as $field) { + if ('LOCATION' === $field['label']) { + $location = $field['value']; + break; + } + } + if ('' === $location) { + continue; + } + $holdingsData[$location][] = $entry; + } + } + } + $offset = 0; $limit = 50; - $fields = 'location,status,barcode,callNumber,fixedFields'; - if ('m' !== $bib['bibLevel']['code']) { - // Fetch varFields for volume information - $fields .= ',varFields'; - } + $fields = 'location,status,barcode,callNumber,fixedFields,varFields'; $statuses = []; - while (!isset($result) || $limit === $result['total']) { + $sort = 0; + $result = null; + while (null === $result || $limit === $result['total']) { $result = $this->makeRequest( ['v3', 'items'], [ - 'bibIds' => $id, + 'bibIds' => $this->extractBibId($id), 'deleted' => 'false', 'suppressed' => 'false', 'fields' => $fields, @@ -1643,14 +1714,15 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return $statuses; } - foreach ($result['entries'] as $i => $item) { + foreach ($result['entries'] as $item) { $location = $this->translateLocation($item['location']); list($status, $duedate, $notes) = $this->getItemStatus($item); - $available = $status == 'On Shelf'; + $available = $status == $this->mapStatusCode('-'); // OPAC message if (isset($item['fixedFields']['108'])) { $opacMsg = $item['fixedFields']['108']; - if (trim($opacMsg['value']) != '-') { + $trimmedMsg = trim($opacMsg['value']); + if (strlen($trimmedMsg) && $trimmedMsg != '-') { $notes[] = $this->translateOpacMessage( trim($opacMsg['value']) ); @@ -1667,11 +1739,12 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, 'status' => $status, 'reserve' => 'N', 'callnumber' => isset($item['callNumber']) - ? preg_replace('/^\|a/', '', $item['callNumber']) : '', + ? preg_replace('/^\|a/', '', $item['callNumber']) + : $this->getBibCallNumber($bib), 'duedate' => $duedate, 'number' => $volume, 'barcode' => $item['barcode'], - 'sort' => $i + 'sort' => $sort-- ]; if ($notes) { $entry['item_notes'] = $notes; @@ -1686,15 +1759,202 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, $entry['is_holdable'] = false; } + $locationCode = $item['location']['code'] ?? ''; + if (!empty($holdingsData[$locationCode])) { + $entry += $this->getHoldingsData($holdingsData[$locationCode]); + $holdingsData[$locationCode]['_hasItems'] = true; + } + $statuses[] = $entry; } $offset += $limit; } + // Add holdings that don't have items + foreach ($holdingsData as $locationCode => $holdings) { + if (!empty($holdings['_hasItems'])) { + continue; + } + + $location = $this->translateLocation( + ['code' => $locationCode, 'name' => ''] + ); + $code = $locationCode; + while ('' === $location && $code) { + $location = $this->getLocationName($code); + $code = substr($code, 0, -1); + } + $entry = [ + 'id' => $id, + 'item_id' => 'HLD_' . $holdings[0]['id'], + 'location' => $location, + 'requests_placed' => 0, + 'status' => '', + 'use_unknown_message' => true, + 'availability' => false, + 'duedate' => '', + 'barcode' => '', + 'sort' => $sort-- + ]; + $entry += $this->getHoldingsData($holdings); + + $statuses[] = $entry; + } + usort($statuses, [$this, 'statusSortFunction']); return $statuses; } + /** + * Get holdings fields according to configuration + * + * @param array $holdings Holdings records + * + * @return array + */ + protected function getHoldingsData($holdings) + { + $result = []; + // Get Notes + if (isset($this->config['Holdings']['notes'])) { + $data = $this->extractFieldsFromApiData( + $holdings, + $this->config['Holdings']['notes'] + ); + if ($data) { + $result['notes'] = $data; + } + } + + // Get Summary (may be multiple lines) + $data = $this->extractFieldsFromApiData( + $holdings, + isset($this->config['Holdings']['summary']) + ? $this->config['Holdings']['summary'] + : 'h' + ); + if ($data) { + $result['summary'] = $data; + } + + // Get Supplements + if (isset($this->config['Holdings']['supplements'])) { + $data = $this->extractFieldsFromApiData( + $holdings, + $this->config['Holdings']['supplements'] + ); + if ($data) { + $result['supplements'] = $data; + } + } + + // Get Indexes + if (isset($this->config['Holdings']['indexes'])) { + $data = $this->extractFieldsFromApiData( + $holdings, + $this->config['Holdings']['indexes'] + ); + if ($data) { + $result['indexes'] = $data; + } + } + return $result; + } + + /** + * Get fields from holdings or bib API response according to the field spec. + * + * @param array $response API response data + * @param array|string $fieldSpecs Array or colon-separated list of + * field/subfield specifications (3 chars for field code and then subfields, + * e.g. 866az) + * + * @return string|string[] Results as a string if single, array if multiple + */ + protected function extractFieldsFromApiData($response, $fieldSpecs) + { + if (!is_array($fieldSpecs)) { + $fieldSpecs = explode(':', $fieldSpecs); + } + $result = []; + foreach ($response as $row) { + foreach ($fieldSpecs as $fieldSpec) { + $fieldCode = substr($fieldSpec, 0, 3); + $subfieldCodes = substr($fieldSpec, 3); + $fields = $row['varFields'] ?? []; + foreach ($fields as $field) { + if (($field['marcTag'] ?? '') !== $fieldCode + && ($field['fieldTag'] ?? '') !== $fieldCode + ) { + continue; + } + $subfields = $field['subfields'] ?? [ + [ + 'tag' => '', + 'content' => $field['content'] ?? '' + ] + ]; + $line = []; + foreach ($subfields as $subfield) { + if ($subfieldCodes + && false === strpos($subfieldCodes, $subfield['tag']) + ) { + continue; + } + $line[] = $subfield['content']; + } + if ($line) { + $result[] = implode(' ', $line); + } + } + } + } + if (!$result) { + return ''; + } + return isset($result[1]) ? $result : $result[0]; + } + + /** + * Get name for a location code + * + * @param string $locationCode Location code + * + * @return string + */ + protected function getLocationName($locationCode) + { + $locations = $this->getCachedData('locations'); + if (null === $locations) { + $locations = []; + $result = $this->makeRequest( + ['v4', 'branches'], + [ + 'limit' => 10000, + 'fields' => 'locations' + ], + 'GET' + ); + if (!empty($result['code'])) { + // An error was returned + $this->error( + "Request for branches returned error code: {$result['code']}, " + . "HTTP status: {$result['httpStatus']}, name: {$result['name']}" + ); + throw new ILSException('Problem with Sierra REST API.'); + } + foreach (($result['entries'] ?? []) as $branch) { + foreach (($branch['locations'] ?? []) as $location) { + $locations[$location['code']] = $this->translateLocation( + $location + ); + } + } + $this->putCachedData('locations', $locations); + } + return $locations[$locationCode] ?? ''; + } + /** * Translate location name * @@ -1715,6 +1975,26 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, ); } + /** + * Status item sort function + * + * @param array $a First status record to compare + * @param array $b Second status record to compare + * + * @return int + */ + protected function statusSortFunction($a, $b) + { + $result = strcmp($a['location'], $b['location']); + if ($result === 0 && $this->sortItemsByEnumChron) { + $result = strnatcmp($b['number'] ?? '', $a['number'] ?? ''); + } + if ($result === 0) { + $result = $a['sort'] - $b['sort']; + } + return $result; + } + /** * Translate OPAC message * @@ -1731,6 +2011,19 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return $this->translate("$prefix$code", null, $code); } + /** + * Get the human-readable equivalent of a status code. + * + * @param string $code Code to map + * @param string $default Default value if no mapping found + * + * @return string + */ + protected function mapStatusCode($code, $default = null) + { + return trim($this->itemStatusMappings[$code] ?? $default ?? $code); + } + /** * Get status for an item * @@ -1742,15 +2035,12 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, { $duedate = ''; $notes = []; - $statusCode = trim($item['status']['code']); - if (isset($this->itemStatusMappings[$statusCode])) { - $status = $this->itemStatusMappings[$statusCode]; - } else { - $status = isset($item['status']['display']) + $status = $this->mapStatusCode( + trim($item['status']['code']), + isset($item['status']['display']) ? ucwords(strtolower($item['status']['display'])) - : '-'; - } - $status = trim($status); + : '-' + ); // For some reason at least API v2.0 returns "ON SHELF" even when the // item is out. Use duedate to check if it's actually checked out. if (isset($item['status']['duedate'])) { @@ -1758,18 +2048,18 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, \DateTime::ISO8601, $item['status']['duedate'] ); - $status = 'Charged'; + $status = $this->mapStatusCode('Charged'); } else { switch ($status) { case '-': - $status = 'On Shelf'; + $status = $this->mapStatusCode('-'); break; case 'Lib Use Only': - $status = 'On Reference Desk'; + $status = $this->mapStatusCode('o'); break; } } - if ($status == 'On Shelf') { + if ($status == $this->mapStatusCode('-')) { // Check for checkin date $today = $this->dateConverter->convertToDisplayDate('U', time()); if (isset($item['fixedFields']['68'])) { @@ -1863,23 +2153,6 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, return empty($blockReason) ? false : $blockReason; } - /** - * Status item sort function - * - * @param array $a First status record to compare - * @param array $b Second status record to compare - * - * @return int - */ - protected function statusSortFunction($a, $b) - { - $result = strcmp($a['location'], $b['location']); - if ($result == 0) { - $result = $a['sort'] - $b['sort']; - } - return $result; - } - /** * Pickup location sort function * @@ -1973,10 +2246,52 @@ class SierraRest extends AbstractBase implements TranslatorAwareInterface, protected function getBibRecord($id, $fields, $patron = false) { return $this->makeRequest( - ['v3', 'bibs', $id], + ['v3', 'bibs', $this->extractBibId($id)], ['fields' => $fields], 'GET', $patron ); } + + /** + * Extract a numeric bib ID value from a string that may be prefixed. + * + * @param string $id Bib record id (with or without .b prefix) + * + * @return int + */ + protected function extractBibId($id) + { + // If the .b prefix is found, strip it and the trailing checksum: + return substr($id, 0, 2) === '.b' + ? substr($id, 2, strlen($id) - 3) : $id; + } + + /** + * If the system is configured to use full prefixed bib IDs, add the prefix + * and checksum. + * + * @param int $id Bib ID that may need to be prefixed. + * + * @return string + */ + protected function formatBibId($id) + { + // Simple case: prefixing is disabled, so return ID unmodified: + if (!($this->config['Catalog']['use_prefixed_ids'] ?? false)) { + return $id; + } + + // If we got this far, we need to generate a check digit: + $multiplier = 2; + $sum = 0; + for ($x = strlen($id) - 1; $x >= 0; $x--) { + $current = substr($id, $x, 1); + $sum += $multiplier * intval($current); + $multiplier++; + } + $checksum = $sum % 11; + $finalChecksum = $checksum === 10 ? 'x' : $checksum; + return '.b' . $id . $finalChecksum; + } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php b/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php index dd0ebd9993174421ccb8c8116ec2b82453109143..05943e737c1b3c2c8d53830018a0874e05926e36 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/SierraRestFactory.php @@ -61,7 +61,7 @@ class SierraRestFactory extends DriverWithDateConverterFactory throw new \Exception('Unexpected options passed to factory.'); } $sessionFactory = function ($namespace) use ($container) { - $manager = $container->get('Zend\Session\SessionManager'); + $manager = $container->get(\Zend\Session\SessionManager::class); return new \Zend\Session\Container("SierraRest_$namespace", $manager); }; return parent::__invoke($container, $requestedName, [$sessionFactory]); diff --git a/module/VuFind/src/VuFind/ILS/Driver/Symphony.php b/module/VuFind/src/VuFind/ILS/Driver/Symphony.php index 3fb4b127f2215505960a99df0acf61d547da3706..353ded512daba667fa691655bea268d332477052 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Symphony.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Symphony.php @@ -1020,15 +1020,18 @@ class Symphony extends AbstractBase implements LoggerAwareInterface * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php b/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php index 52ae28982d8df2728a2717027f3f8cb594a19286..e89fb7dace88bc856c6346172e11ad00ca1bd574 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/SymphonyFactory.php @@ -62,8 +62,8 @@ class SymphonyFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Record\Loader'), - $container->get('VuFind\Cache\Manager') + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Cache\Manager::class) ); } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php b/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php index e5964b720e9f545689c38bc44cc1d2f43c3ee9ab..75aa0c586113194a6851c3611a3bd664c0d38422 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Unicorn.php @@ -393,16 +393,19 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return $this->getStatus($id); } @@ -1176,10 +1179,10 @@ class Unicorn extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf if (empty($url)) { $url = $this->host; if ($this->port) { - $url = "http://" . $url . ":" . $this->port . "/" . + $url = "http://" . $url . ":" . $this->port . "/" . $this->search_prog; } else { - $url = "http://" . $url . "/" . $this->search_prog; + $url = "http://" . $url . "/" . $this->search_prog; } } diff --git a/module/VuFind/src/VuFind/ILS/Driver/Virtua.php b/module/VuFind/src/VuFind/ILS/Driver/Virtua.php index c71de284dc6f58f0e91e2e8bd2b7f486bb9e1a06..9a534fb5ea96bfd135564685065e27c244b84dac 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Virtua.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Virtua.php @@ -323,16 +323,19 @@ class Virtua extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterfa * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { // Strip off the prefix from vtls exports $db_id = str_replace("vtls", "", $id); diff --git a/module/VuFind/src/VuFind/ILS/Driver/Voyager.php b/module/VuFind/src/VuFind/ILS/Driver/Voyager.php index b70e2ccc58a81604d1625d1fc40114b6ca37a9de..0aa6f718a7b6a0de0a625f07b4b8fc7ebed934be 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Voyager.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Voyager.php @@ -873,7 +873,7 @@ EOT; if ($subfields = $field->getSubfields()) { $line = ''; foreach ($subfields as $code => $subfield) { - if (!strstr($subfieldCodes, $code)) { + if (false === strpos($subfieldCodes, $code)) { continue; } if ($line) { @@ -1125,16 +1125,19 @@ EOT; * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws DateException * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $possibleQueries = []; diff --git a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php index a338cd0293d4fda728230f62a5e7fa5dffff53b3..e1a7ad153cbc938f591e3b5e1f7f2146cb4a8593 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php +++ b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestful.php @@ -1507,7 +1507,7 @@ EOT; } // Build request - $xml = <<<EOT + $xml = <<<EOT <?xml version="1.0" encoding="UTF-8"?> <ser:serviceParameters xmlns:ser="http://www.endinfosys.com/Voyager/serviceParameters"> @@ -1973,7 +1973,7 @@ EOT; } try { - $checkTime = $this->dateFormat->convertFromDisplayDate( + $checkTime = $this->dateFormat->convertFromDisplayDate( 'U', $holdDetails['requiredBy'] ); if (!is_numeric($checkTime)) { @@ -2658,7 +2658,7 @@ EOT; return $data; } - if (strstr($patron['id'], '.') === false) { + if (strpos($patron['id'], '.') === false) { $this->debug( "getUBRequestDetails: no prefix in patron id '{$patron['id']}'" ); @@ -2734,7 +2734,7 @@ EOT; return false; } - $xml = <<<EOT + $xml = <<<EOT <?xml version="1.0" encoding="UTF-8"?> <ser:serviceParameters xmlns:ser="http://www.endinfosys.com/Voyager/serviceParameters"> @@ -2943,7 +2943,7 @@ EOT; $barcode = $this->encodeXML($catUsername); $pickupLib = $this->encodeXML($pickupLib); - $xml = <<<EOT + $xml = <<<EOT <?xml version="1.0" encoding="UTF-8"?> <ser:serviceParameters xmlns:ser="http://www.endinfosys.com/Voyager/serviceParameters"> @@ -3056,7 +3056,7 @@ EOT; } // Attempt Request - $xml = <<<EOT + $xml = <<<EOT <?xml version="1.0" encoding="UTF-8"?> <ser:serviceParameters xmlns:ser="http://www.endinfosys.com/Voyager/serviceParameters"> @@ -3313,7 +3313,7 @@ EOT; } $barcode = htmlspecialchars($patron['cat_username'], ENT_COMPAT, 'UTF-8'); - $xml = <<<EOT + $xml = <<<EOT <?xml version="1.0" encoding="UTF-8"?> <ser:serviceParameters xmlns:ser="http://www.endinfosys.com/Voyager/serviceParameters"> diff --git a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php index 56e525226c9e4c15c18fcbc9b6c971208d063ca2..34f2c66d08df688ce0ad5bdaab2f468a7721742c 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php +++ b/module/VuFind/src/VuFind/ILS/Driver/VoyagerRestfulFactory.php @@ -60,7 +60,7 @@ class VoyagerRestfulFactory extends DriverWithDateConverterFactory if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $ils = $container->get('VuFind\ILS\HoldSettings'); + $ils = $container->get(\VuFind\ILS\HoldSettings::class); $extraParams = [$ils->getHoldsMode(), $ils->getTitleHoldsMode()]; return parent::__invoke($container, $requestedName, $extraParams); } diff --git a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php index e7c7d2101f69f118cee5dbad82698e16c04ebb05..ccb27882659cc81860d8ed9d41e09a80c2a2a65f 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php +++ b/module/VuFind/src/VuFind/ILS/Driver/XCNCIP2.php @@ -565,16 +565,19 @@ class XCNCIP2 extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterf * This is responsible for retrieving the holding information of a certain * record. * - * @param string $id The record id to retrieve the holdings for - * @param array $patron Patron data + * @param string $id The record id to retrieve the holdings for + * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws VuFind\Date\DateException; * @throws ILSException * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { $ids = null; if (! $this->consortium) { diff --git a/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php b/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php index 00dc7e2988902d644c7639c5670b75f62f93e8fb..810b1427b8d001062b57649466661ea5e91bfa70 100644 --- a/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php +++ b/module/VuFind/src/VuFind/ILS/HoldSettingsFactory.php @@ -61,8 +61,9 @@ class HoldSettingsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } + $configManager = $container->get(\VuFind\Config\PluginManager::class); return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config')->Catalog + $configManager->get('config')->Catalog ); } } diff --git a/module/VuFind/src/VuFind/ILS/Logic/Holds.php b/module/VuFind/src/VuFind/ILS/Logic/Holds.php index 7455a0960876e9f4b41c1b621124e93484b102e6..507fd5d1256db25f8d21787eb28f5de17aa8f941 100644 --- a/module/VuFind/src/VuFind/ILS/Logic/Holds.php +++ b/module/VuFind/src/VuFind/ILS/Logic/Holds.php @@ -231,7 +231,11 @@ class Holds } return [ 'blocks' => $blocks, - 'holdings' => $this->formatHoldings($holdings) + 'total' => $result['total'], + 'page' => $result['page'], + 'itemLimit' => $result['itemLimit'], + 'holdings' => $this->formatHoldings($holdings), + 'electronic_holdings' => $result['electronic_holdings'] ?? [], ]; } @@ -245,8 +249,8 @@ class Holds protected function standardHoldings($result) { $holdings = []; - if (count($result)) { - foreach ($result as $copy) { + if ($result['total']) { + foreach ($result['holdings'] as $copy) { $show = !in_array($copy['location'], $this->hideHoldings); if ($show) { $groupKey = $this->getHoldingsGroupKey($copy); @@ -270,14 +274,15 @@ class Holds { $holdings = []; - if (count($result)) { - foreach ($result as $copy) { + if ($result['total']) { + foreach ($result['holdings'] as $copy) { $show = !in_array($copy['location'], $this->hideHoldings); if ($show) { if ($holdConfig) { // Is this copy holdable / linkable if (!$requestsBlocked - && isset($copy['addLink']) && $copy['addLink'] + && ($copy['addLink'] ?? false) + && ($copy['is_holdable'] ?? true) ) { $copy['link'] = $this->getRequestDetails( $copy, $holdConfig['HMACKeys'], 'Hold' @@ -315,8 +320,8 @@ class Holds $holds_override = isset($this->config->Catalog->allow_holds_override) ? $this->config->Catalog->allow_holds_override : false; - if (count($result)) { - foreach ($result as $copy) { + if ($result['total']) { + foreach ($result['holdings'] as $copy) { $show = !in_array($copy['location'], $this->hideHoldings); if ($show) { $groupKey = $this->getHoldingsGroupKey($copy); @@ -359,10 +364,7 @@ class Holds } // If a valid holdable status has been set, use it to // determine if a hold link is created - $addlink = isset($copy['is_holdable']) - ? ($addlink && $copy['is_holdable']) : $addlink; - - if ($addlink) { + if ($addlink && ($copy['is_holdable'] ?? true)) { if ($holdConfig['function'] == "getHoldLink") { /* Build opac link */ $holdings[$location_key][$copy_key]['link'] diff --git a/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php b/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php index 0ceae43623bfb71875a8ee815f72bf3929a0096d..33c0d2ff01b8ea61d1efb42ecbd1c3d37dd85ebf 100644 --- a/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php +++ b/module/VuFind/src/VuFind/ILS/Logic/LogicFactory.php @@ -62,10 +62,10 @@ class LogicFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Auth\ILSAuthenticator'), - $container->get('VuFind\ILS\Connection'), - $container->get('VuFind\Crypt\HMAC'), - $container->get('VuFind\Config\PluginManager')->get('config') + $container->get(\VuFind\Auth\ILSAuthenticator::class), + $container->get(\VuFind\ILS\Connection::class), + $container->get(\VuFind\Crypt\HMAC::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config') ); } } diff --git a/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php b/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php index 00f60f67224165e4afa53877fb9eabaed99cb7ca..393213a9546d791f403c4dcc50c9913f7b944f57 100644 --- a/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php +++ b/module/VuFind/src/VuFind/ILS/Logic/TitleHolds.php @@ -155,7 +155,7 @@ class TitleHolds static $holdings = []; if (!isset($holdings[$id])) { - $holdings[$id] = $this->catalog->getHolding($id); + $holdings[$id] = $this->catalog->getHolding($id)['holdings']; } return $holdings[$id]; } diff --git a/module/VuFind/src/VuFind/ImageLoader.php b/module/VuFind/src/VuFind/ImageLoader.php index 228622ead3ed404968a34caf828dcd3fc4650e5a..4746b27e3509fef3a11c0efdafdef7f51244c868 100644 --- a/module/VuFind/src/VuFind/ImageLoader.php +++ b/module/VuFind/src/VuFind/ImageLoader.php @@ -145,7 +145,7 @@ class ImageLoader implements \Zend\Log\LoggerAwareInterface // Check all supported image formats: $filenames = []; foreach ($formats as $format) { - $filenames[] = $path . $format; + $filenames[] = $path . $format; } if (null === $this->themeTools) { throw new \Exception('\VuFindTheme\ThemeInfo object missing'); diff --git a/module/VuFind/src/VuFind/Log/LoggerFactory.php b/module/VuFind/src/VuFind/Log/LoggerFactory.php index bf828b45e96136e1d46a175ecf4c270214aceb24..bd49125f1c85114e32a595e799b50d4cbfe5b4dc 100644 --- a/module/VuFind/src/VuFind/Log/LoggerFactory.php +++ b/module/VuFind/src/VuFind/Log/LoggerFactory.php @@ -29,6 +29,7 @@ namespace VuFind\Log; use Interop\Container\ContainerInterface; use Zend\Config\Config; +use Zend\Console\Console; use Zend\Log\Writer\WriterInterface; use Zend\ServiceManager\Factory\FactoryInterface; @@ -71,7 +72,7 @@ class LoggerFactory implements FactoryInterface // Make Writers $filters = explode(',', $error_types); $writer = new Writer\Db( - $container->get('Zend\Db\Adapter\Adapter'), + $container->get(\Zend\Db\Adapter\Adapter::class), $table_name, $columnMapping ); $this->addWriters($logger, $writer, $filters); @@ -96,7 +97,7 @@ class LoggerFactory implements FactoryInterface $error_types = $parts[1] ?? ''; // use smtp - $mailer = $container->get('VuFind\Mailer\Mailer'); + $mailer = $container->get(\VuFind\Mailer\Mailer::class); $msg = $mailer->getNewMessage() ->addFrom($config->Site->email) ->addTo($email) @@ -164,7 +165,7 @@ class LoggerFactory implements FactoryInterface // Make Writers $writer = new Writer\Slack( $config->Logging->slackurl, - $container->get('VuFindHttp\HttpService')->createClient(), + $container->get(\VuFindHttp\HttpService::class)->createClient(), $options ); $writer->setContentType('application/json'); @@ -175,6 +176,27 @@ class LoggerFactory implements FactoryInterface $this->addWriters($logger, $writer, $filters); } + /** + * Is dynamic debug mode enabled? + * + * @param ContainerInterface $container Service manager + * + * @return bool + */ + protected function hasDynamicDebug(ContainerInterface $container): bool + { + // Query parameters do not apply in console mode; if we do have a debug + // query parameter, and the appropriate permission is set, activate dynamic + // debug: + if (!Console::isConsole() + && $container->get('Request')->getQuery()->get('debug') + ) { + return $container->get(\ZfcRbac\Service\AuthorizationService::class) + ->isGranted('access.DebugMode'); + } + return false; + } + /** * Set configuration * @@ -185,12 +207,13 @@ class LoggerFactory implements FactoryInterface */ protected function configureLogger(ContainerInterface $container, Logger $logger) { - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $hasWriter = false; // DEBUGGER - if (!$config->System->debug == false) { + if (!$config->System->debug == false || $this->hasDynamicDebug($container)) { $hasWriter = true; $this->addDebugWriter($logger, $config->System->debug); } @@ -351,8 +374,19 @@ class LoggerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $logger = new $requestedName(); - $this->configureLogger($container, $logger); - return $logger; + // Construct the logger as a lazy loading value holder so that + // the object is not instantiated until it is called. This helps break + // potential circular dependencies with other services. + $callback = function (& $wrapped, $proxy) use ($container, $requestedName) { + // Indicate that initialization is complete to avoid reinitialization: + $proxy->setProxyInitializer(null); + + // Now build the actual service: + $wrapped = new $requestedName(); + $this->configureLogger($container, $wrapped); + }; + $cfg = $container->get(\ProxyManager\Configuration::class); + $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($cfg); + return $factory->createProxy($requestedName, $callback); } } diff --git a/module/VuFind/src/VuFind/Mailer/Factory.php b/module/VuFind/src/VuFind/Mailer/Factory.php index 6aa5dba410ca2e4d21b2386279f222b9abc29d89..4fcc86269fd6a7ba3518566aff4837c8edc90a71 100644 --- a/module/VuFind/src/VuFind/Mailer/Factory.php +++ b/module/VuFind/src/VuFind/Mailer/Factory.php @@ -64,24 +64,29 @@ class Factory implements FactoryInterface $settings = [ 'host' => $config->Mail->host, 'port' => $config->Mail->port ]; + if (isset($config->Mail->name)) { + $settings['name'] = $config->Mail->name; + } if (isset($config->Mail->username) && isset($config->Mail->password)) { $settings['connection_class'] = 'login'; $settings['connection_config'] = [ 'username' => $config->Mail->username, 'password' => $config->Mail->password ]; + // Set user defined secure connection if provided; otherwise set default + // secure connection based on configured port number. if (isset($config->Mail->secure)) { - // always set user defined secure connection $settings['connection_config']['ssl'] = $config->Mail->secure; - } else { - // set default secure connection based on configured port - if ($settings['port'] == '587') { - $settings['connection_config']['ssl'] = 'tls'; - } elseif ($settings['port'] == '487') { - $settings['connection_config']['ssl'] = 'ssl'; - } + } elseif ($settings['port'] == '587') { + $settings['connection_config']['ssl'] = 'tls'; + } elseif ($settings['port'] == '487') { + $settings['connection_config']['ssl'] = 'ssl'; } } + if (isset($config->Mail->connection_time_limit)) { + $settings['connection_time_limit'] + = $config->Mail->connection_time_limit; + } return new Smtp(new SmtpOptions($settings)); } @@ -107,7 +112,8 @@ class Factory implements FactoryInterface } // Load configurations: - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); // Create service: $class = new $requestedName($this->getTransport($config)); diff --git a/module/VuFind/src/VuFind/Mailer/Mailer.php b/module/VuFind/src/VuFind/Mailer/Mailer.php index 193cb5823cac9a835d16c8ae789d60cc6f8fe74c..98deb61a34e816189ecdcd2373e62b2107ee3f1a 100644 --- a/module/VuFind/src/VuFind/Mailer/Mailer.php +++ b/module/VuFind/src/VuFind/Mailer/Mailer.php @@ -104,6 +104,21 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface return $message; } + /** + * Reset the connection in the transport. Implements a fluent interface. + * + * @return Mailer + */ + public function resetConnection() + { + // If the transport has a disconnect method, call it: + $transport = $this->getTransport(); + if (is_callable([$transport, 'disconnect'])) { + $transport->disconnect(); + } + return $this; + } + /** * Set the mail transport object. * @@ -186,7 +201,11 @@ class Mailer implements \VuFind\I18n\Translator\TranslatorAwareInterface if (!empty($this->fromAddressOverride) && $this->fromAddressOverride != $fromEmail ) { - $replyTo->add($fromEmail); + // Add the original from address as the reply-to address unless + // a reply-to address has been specified + if (count($replyTo) === 0) { + $replyTo->add($fromEmail); + } if (!($from instanceof Address)) { $from = new Address($from); } diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/AbstractBase.php b/module/VuFind/src/VuFind/MetadataVocabulary/AbstractBase.php new file mode 100644 index 0000000000000000000000000000000000000000..71b4348759338885a9d7a02580e4b4cbbb9c450a --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/AbstractBase.php @@ -0,0 +1,114 @@ +<?php +/** + * Metadata vocabulary base class + * (provides results from available RecordDriver methods in a standardized form) + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary base class + * (provides results from available RecordDriver methods in a standardized form) + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractBase implements MetadataVocabularyInterface +{ + /** + * This varriable can be overwritten by child classes + * to define which custom field is filled by which generic fields. + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = []; + + /** + * Generate standardized data from available RecordDriver methods + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + protected function getGenericData(\VuFind\RecordDriver\AbstractBase $driver) + { + return [ + 'author' => array_unique( + array_merge( + $driver->tryMethod('getPrimaryAuthors') ?? [], + $driver->tryMethod('getSecondaryAuthors') ?? [], + $driver->tryMethod('getCorporateAuthors') ?? [] + ) + ), + 'container_title' => $driver->tryMethod('getContainerTitle'), + 'date' => $driver->tryMethod('getPublicationDates'), + 'doi' => $driver->tryMethod('getCleanDOI'), + 'endpage' => $driver->tryMethod('getContainerEndPage'), + 'isbn' => $driver->tryMethod('getCleanISBN'), + 'issn' => $driver->tryMethod('getCleanISSN'), + 'issue' => $driver->tryMethod('getContainerIssue'), + 'language' => $driver->tryMethod('getLanguages'), + 'publisher' => $driver->tryMethod('getPublishers'), + 'startpage' => $driver->tryMethod('getContainerStartPage'), + 'title' => $driver->tryMethod('getTitle'), + 'volume' => $driver->tryMethod('getContainerVolume'), + ]; + } + + /** + * Perform mapping from generic data to vocabulary data + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + public function getMappedData(\VuFind\RecordDriver\AbstractBase $driver) + { + $genericData = $this->getGenericData($driver); + $mappedData = []; + + foreach ($this->vocabFieldToGenericFieldsMap as $vocabField => $genFields) { + foreach ((array)$genFields as $genericField) { + $genericValues = $genericData[$genericField] ?? []; + if ($genericValues) { + if (!is_array($genericValues)) { + $genericValues = [$genericValues]; + } + foreach ($genericValues as $genericValue) { + if (!isset($mappedData[$vocabField])) { + $mappedData[$vocabField] = []; + } + $mappedData[$vocabField][] = $genericValue; + } + } + } + } + + return $mappedData; + } +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/BEPress.php b/module/VuFind/src/VuFind/MetadataVocabulary/BEPress.php new file mode 100644 index 0000000000000000000000000000000000000000..0a467a596b422409425617374dc26a183f7c133c --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/BEPress.php @@ -0,0 +1,61 @@ +<?php +/** + * Metadata vocabulary implementation for BEPress + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary implementation for BEPress + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class BEPress extends AbstractBase +{ + /** + * Mapping from BEPress to VuFind fields; see http:// + * div.div1.com.au/div-thoughts/div-commentaries/66-div-commentary-metadata + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = [ + 'bepress_citation_author' => 'author', + 'bepress_citation_date' => 'date', + 'bepress_citation_doi' => 'doi', + 'bepress_citation_firstpage' => 'startpage', + 'bepress_citation_isbn' => 'isbn', + 'bepress_citation_issn' => 'issn', + 'bepress_citation_issue' => 'issue', + 'bepress_citation_journal_title' => 'container_title', + 'bepress_citation_lastpage' => 'endpage', + 'bepress_citation_publisher' => 'publisher', + 'bepress_citation_title' => 'title', + 'bepress_citation_volume' => 'volume', + ]; +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/DublinCore.php b/module/VuFind/src/VuFind/MetadataVocabulary/DublinCore.php new file mode 100644 index 0000000000000000000000000000000000000000..ee03d4c397c5317985229771d84798a76d8c2776 --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/DublinCore.php @@ -0,0 +1,59 @@ +<?php +/** + * Metadata vocabulary implementation for Dublin Core + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary implementation for Dublin Core + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DublinCore extends AbstractBase +{ + /** + * Mapping from Dublin Core to VuFind fields + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = [ + 'DC.citation.epage' => 'endpage', + 'DC.citation.issue' => 'issue', + 'DC.citation.spage' => 'startpage', + 'DC.citation.volume' => 'volume', + 'DC.creator' => 'author', + 'DC.identifier' => ['doi', 'isbn', 'issn'], + 'DC.issued' => 'date', + 'DC.language' => 'language', + 'DC.publisher' => 'publisher', + 'DC.relation.ispartof' => 'container_title', + 'DC.title' => 'title', + ]; +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/Eprints.php b/module/VuFind/src/VuFind/MetadataVocabulary/Eprints.php new file mode 100644 index 0000000000000000000000000000000000000000..8858bd1e777c9047019130a63548b538956f7836 --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/Eprints.php @@ -0,0 +1,81 @@ +<?php +/** + * Metadata vocabulary implementation for Eprints + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary implementation for Eprints + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Eprints extends AbstractBase +{ + /** + * Mapping from Eprints to VuFind fields + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = [ + 'eprints.creators_name' => 'author', + 'eprints.date' => 'date', + 'eprints.issn' => 'issn', + 'eprints.number' => 'volume', + 'eprints.publication' => 'container_title', + 'eprints.publisher' => 'publisher', + 'eprints.title' => 'title', + ]; + + /** + * Special implementation to combine start / end page in eprints.pagerange + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + public function getMappedData(\VuFind\RecordDriver\AbstractBase $driver) + { + $mappedData = parent::getMappedData($driver); + + // special handling for pagerange + $startpage = $driver->tryMethod('getContainerStartPage'); + + if ($startpage) { + $pagerange = $startpage; + $endpage = $driver->tryMethod('getContainerEndPage'); + if ($endpage != '' && $endpage != $startpage) { + $pagerange = $startpage . '-' . $endpage; + } + $mappedData['eprints.pagerange'] = [$pagerange]; + } + + return $mappedData; + } +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/HighwirePress.php b/module/VuFind/src/VuFind/MetadataVocabulary/HighwirePress.php new file mode 100644 index 0000000000000000000000000000000000000000..22bb4acc41d0fd76a130b7fe922a5e8611c2141a --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/HighwirePress.php @@ -0,0 +1,88 @@ +<?php +/** + * Metadata vocabulary implementation for Highwire Press + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary implementation for Highwire Press + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HighwirePress extends AbstractBase +{ + /** + * Mapping from Highwire Press to VuFind fields; see + * https://jira.duraspace.org/secure/attachment/13020/Invisible_institutional.pdf + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = [ + 'citation_author' => 'author', + 'citation_date' => 'date', + 'citation_doi' => 'doi', + 'citation_firstpage' => 'startpage', + 'citation_isbn' => 'isbn', + 'citation_issn' => 'issn', + 'citation_issue' => 'issue', + 'citation_journal_title' => 'container_title', + 'citation_language' => 'language', + 'citation_lastpage' => 'endpage', + 'citation_publisher' => 'publisher', + 'citation_title' => 'title', + 'citation_volume' => 'volume', + ]; + + /** + * Special implementation for date formats + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + public function getMappedData(\VuFind\RecordDriver\AbstractBase $driver) + { + $mappedData = parent::getMappedData($driver); + + // special handling for dates + if (isset($mappedData['citation_date'])) { + foreach ($mappedData['citation_date'] as $key => $date) { + // If we only have a year, leave it as-is + // If we have a date, we need to convert to MM-DD-YYYY or MM/DD/YYYY + if (!preg_match('"^\d+$"', $date)) { + $mappedData['citation_date'][$key] + = date('m/d/Y', strtotime($date)); + } + } + } + + return $mappedData; + } +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/MetadataVocabularyInterface.php b/module/VuFind/src/VuFind/MetadataVocabulary/MetadataVocabularyInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..edb0d94c0d67e2a33470b3bbf3de5852552de83f --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/MetadataVocabularyInterface.php @@ -0,0 +1,53 @@ +<?php +/** + * Metadata vocabulary interface + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary interface + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +interface MetadataVocabularyInterface +{ + /** + * Map data from RecordDriver to this vocabulary. + * + * Note that AbstractBase instead of DefaultRecord is used + * for higher flexibility. That's why all implementations must use + * "tryMethod" instead of calling the methods directly. + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + public function getMappedData(\VuFind\RecordDriver\AbstractBase $driver); +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/PRISM.php b/module/VuFind/src/VuFind/MetadataVocabulary/PRISM.php new file mode 100644 index 0000000000000000000000000000000000000000..3c4a9bef6f05194bc7fcd2669211bef7f8d4a2b1 --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/PRISM.php @@ -0,0 +1,55 @@ +<?php +/** + * Metadata vocabulary implementation for PRISM + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary implementation for PRISM + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PRISM extends AbstractBase +{ + /** + * Mapping from Highwire Press to VuFind fields + * see https://www.idealliance.org/prism-metadata + * + * @var array + */ + protected $vocabFieldToGenericFieldsMap = ['prism.doi' => 'doi', + 'prism.endingPage' => 'endpage', + 'prism.isbn' => 'isbn', + 'prism.issn' => 'issn', + 'prism.startingPage' => 'startpage', + 'prism.title' => 'title', + 'prism.volume' => 'volume', + ]; +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/PluginFactory.php b/module/VuFind/src/VuFind/MetadataVocabulary/PluginFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..aacf5c599687741a4db691c1c4c258d477bd95f2 --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/PluginFactory.php @@ -0,0 +1,48 @@ +<?php +/** + * Metadata vocabulary plugin factory + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary plugin factory + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory +{ + /** + * Constructor + */ + public function __construct() + { + $this->defaultNamespace = 'VuFind\MetadataVocabulary'; + } +} diff --git a/module/VuFind/src/VuFind/MetadataVocabulary/PluginManager.php b/module/VuFind/src/VuFind/MetadataVocabulary/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..6bb581a8460f8f0f29e3727273df8f2d8706860f --- /dev/null +++ b/module/VuFind/src/VuFind/MetadataVocabulary/PluginManager.php @@ -0,0 +1,67 @@ +<?php +/** + * Metadata vocabulary plugin manager + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\MetadataVocabulary; + +/** + * Metadata vocabulary plugin manager + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Constructor + * + * Make sure plugins are properly initialized. + * + * @param mixed $configOrContainerInstance Configuration or container instance + * @param array $v3config If $configOrContainerInstance is a + * container, this value will be passed + * to the parent constructor. + */ + public function __construct($configOrContainerInstance = null, + array $v3config = [] + ) { + $this->addAbstractFactory(PluginFactory::class); + parent::__construct($configOrContainerInstance, $v3config); + } + + /** + * Return the base class or interface that plug-ins must conform to. + * + * @return class + */ + protected function getExpectedInterface() + { + return MetadataVocabularyInterface::class; + } +} diff --git a/module/VuFind/src/VuFind/OAI/Server.php b/module/VuFind/src/VuFind/OAI/Server.php index 4afb1af989acbd33e7f8218c0e932920bf6bd7af..3710d07715ed3895b1e8ef46f85445552946e662 100644 --- a/module/VuFind/src/VuFind/OAI/Server.php +++ b/module/VuFind/src/VuFind/OAI/Server.php @@ -5,6 +5,7 @@ * PHP version 7 * * Copyright (C) Villanova University 2010. + * Copyright (C) The National Library of Finland 2018-2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -22,6 +23,7 @@ * @category VuFind * @package OAI_Server * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ @@ -40,6 +42,7 @@ use VuFindApi\Formatter\RecordFormatter; * @category VuFind * @package OAI_Server * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ @@ -171,7 +174,14 @@ class Server */ protected $setQueries = []; - /** + /* + * Default query used when a set is not specified + * + * @var string + */ + protected $defaultQuery = ''; + + /* * Record formatter * * @var RecordFormatter @@ -186,6 +196,21 @@ class Server */ protected $vufindApiFields = []; + /** + * Filter queries specific to the requested record format + * + * @var array + */ + protected $recordFormatFilters = []; + + /** + * Limit on display of deleted records (in days); older deleted records will not + * be returned by the server. Set to null for no limit. + * + * @var int + */ + protected $deleteLifetime = null; + /** * Constructor * @@ -193,19 +218,27 @@ class Server * retrieving records * @param \VuFind\Record\Loader $loader Record loader * @param \VuFind\Db\Table\PluginManager $tables Table manager - * @param \Zend\Config\Config $config VuFind configuration - * @param string $baseURL The base URL for the OAI - * server - * @param array $params The incoming OAI-PMH - * parameters (i.e. $_GET) */ public function __construct(\VuFind\Search\Results\PluginManager $results, - \VuFind\Record\Loader $loader, \VuFind\Db\Table\PluginManager $tables, - \Zend\Config\Config $config, $baseURL, $params + \VuFind\Record\Loader $loader, \VuFind\Db\Table\PluginManager $tables ) { $this->resultsManager = $results; $this->recordLoader = $loader; $this->tableManager = $tables; + } + + /** + * Initialize settings + * + * @param \Zend\Config\Config $config VuFind configuration + * @param string $baseURL The base URL for the OAI server + * @param array $params The incoming OAI-PMH parameters (i.e. + * $_GET) + * + * @return void + */ + public function init(\Zend\Config\Config $config, $baseURL, $params) + { $this->baseURL = $baseURL; $parts = parse_url($baseURL); $this->baseHostURL = $parts['scheme'] . '://' . $parts['host']; @@ -245,6 +278,21 @@ class Server $this->metadataFormats = []; } + /** + * Get the current UTC date/time in ISO 8601 format. + * + * @param string $time Time string to represent as UTC (default = 'now') + * + * @return string + */ + protected function getUTCDateTime($time = 'now') + { + // All times must be in UTC, so translate the current time to the + // appropriate time zone: + $utc = new \DateTime($time, new \DateTimeZone('UTC')); + return date_format($utc, $this->iso8601); + } + /** * Respond to the OAI-PMH request. * @@ -419,7 +467,7 @@ class Server // Get modification date: $date = $record->getLastIndexed(); if (empty($date)) { - $date = date($this->iso8601); + $date = $this->getUTCDateTime('now'); } // Set up header (inside or outside a <record> container depending on @@ -454,7 +502,8 @@ class Server } // Start building response - $xml = new SimpleXMLElement('<GetRecord />'); + $response = $this->createResponse(); + $xml = $response->addChild('GetRecord'); // Retrieve the record from the index if ($record = $this->loadRecord($this->params['identifier'])) { @@ -479,7 +528,7 @@ class Server } // Display the record: - return $this->showResponse($xml); + return $response->asXML(); } /** @@ -501,7 +550,8 @@ class Server */ protected function identify() { - $xml = new SimpleXMLElement('<Identify />'); + $response = $this->createResponse(); + $xml = $response->addChild('Identify'); $xml->repositoryName = $this->repositoryName; $xml->baseURL = $this->baseURL; $xml->protocolVersion = '2.0'; @@ -510,8 +560,7 @@ class Server $xml->deletedRecord = 'transient'; $xml->granularity = 'YYYY-MM-DDThh:mm:ssZ'; if (!empty($this->idNamespace)) { - $xml->addChild('description'); - $id = $xml->description->addChild( + $id = $xml->addChild('description')->addChild( 'oai-identifier', null, 'http://www.openarchives.org/OAI/2.0/oai-identifier' ); @@ -527,7 +576,7 @@ class Server $id->sampleIdentifier = 'oai:' . $this->idNamespace . ':123456'; } - return $this->showResponse($xml); + return $response->asXML(); } /** @@ -600,6 +649,11 @@ class Server $this->idNamespace = $config->OAI->identifier; } + // Override page size if configured: + if (isset($config->OAI->page_size)) { + $this->pageSize = $config->OAI->page_size; + } + // Use either OAI-specific or general email address; we must have SOMETHING. $this->adminEmail = isset($config->OAI->admin_email) ? $config->OAI->admin_email : $config->Site->email; @@ -614,12 +668,28 @@ class Server $this->setQueries = $config->OAI->set_query->toArray(); } + // Use a default query, if configured: + if (isset($config->OAI->default_query)) { + $this->defaultQuery = $config->OAI->default_query; + } + // Initialize VuFind API format fields: $this->vufindApiFields = array_filter( explode( ',', $config->OAI->vufind_api_format_fields ?? '' ) ); + + // Initialize filters specific to requested metadataPrefix: + if (isset($config->OAI->record_format_filters)) { + $this->recordFormatFilters + = $config->OAI->record_format_filters->toArray(); + } + + // Initialize delete lifetime, if set: + if (isset($config->OAI->delete_lifetime)) { + $this->deleteLifetime = intval($config->OAI->delete_lifetime); + } } /** @@ -643,7 +713,8 @@ class Server // the current context (all apply if $record is false, since that // means that no specific record ID was requested; otherwise, they only // apply if the current record driver supports them): - $xml = new SimpleXMLElement('<ListMetadataFormats />'); + $response = $this->createResponse(); + $xml = $response->addChild('ListMetadataFormats'); foreach ($this->getMetadataFormats() as $prefix => $details) { if ($record === false || $record->getXML($prefix) !== false @@ -661,7 +732,7 @@ class Server } // Display the response: - return $this->showResponse($xml); + return $response->asXML(); } /** @@ -699,51 +770,59 @@ class Server // separately from our initial position! $currentCursor = $params['cursor']; - // The template for displaying a single record varies based on the verb: - $xml = new SimpleXMLElement("<{$verb} />"); + $response = $this->createResponse(); + $xml = $response->addChild($verb); // The verb determines whether we're returning headers only or full records: $headersOnly = ($verb != 'ListRecords'); + // Apply the delete lifetime limit to the from date if necessary: + $deleteCutoff = $this->deleteLifetime + ? strtotime('-' . $this->deleteLifetime . ' days') : 0; + $deleteFrom = ($deleteCutoff < $from) ? $from : $deleteCutoff; + // Get deleted records in the requested range (if applicable): - $deleted = $this->listRecordsGetDeleted($from, $until)->toArray(); - $deletedCount = count($deleted); - if ($currentCursor < $deletedCount) { - $limit = $currentCursor + $this->pageSize; - $limit = $limit > $deletedCount ? $deletedCount : $limit; - for ($i = $currentCursor; $i < $limit; $i++) { - $this->attachDeleted($xml, $deleted[$i], $headersOnly); + $deletedCount = $this->listRecordsGetDeletedCount($deleteFrom, $until); + if ($deletedCount > 0 && $currentCursor < $deletedCount) { + $deleted = $this + ->listRecordsGetDeleted($deleteFrom, $until, $currentCursor); + foreach ($deleted as $current) { + $this->attachDeleted($xml, $current, $headersOnly); $currentCursor++; } } - // Figure out how many Solr records we need to display (and where to start): - if ($currentCursor >= $deletedCount) { - $solrOffset = $currentCursor - $deletedCount; - } else { - $solrOffset = 0; - } - $solrLimit = ($params['cursor'] + $this->pageSize) - $currentCursor; + // Figure out how many non-deleted records we need to display: + $recordLimit = ($params['cursor'] + $this->pageSize) - $currentCursor; + $cursorMark = $params['cursorMark'] ?? ''; + $format = $params['metadataPrefix']; // Get non-deleted records from the Solr index: $set = $params['set'] ?? ''; $result = $this->listRecordsGetNonDeleted( - $from, $until, $solrOffset, $solrLimit, $set + $from, + $until, + $cursorMark, + $recordLimit, + $format, + $set ); $nonDeletedCount = $result->getResultTotal(); - $format = $params['metadataPrefix']; foreach ($result->getResults() as $doc) { - if (!$this->attachNonDeleted($xml, $doc, $format, $headersOnly, $set)) { - $this->unexpectedError('Cannot load document'); - } + $this->attachNonDeleted($xml, $doc, $format, $headersOnly, $set); $currentCursor++; } + $nextCursorMark = $result->getCursorMark(); // If our cursor didn't reach the last record, we need a resumption token! $listSize = $deletedCount + $nonDeletedCount; - if ($listSize > $currentCursor) { - $this->saveResumptionToken($xml, $params, $currentCursor, $listSize); - } elseif ($solrOffset > 0) { + if ($listSize > $currentCursor + && ('' === $cursorMark || $nextCursorMark !== $cursorMark) + ) { + $this->saveResumptionToken( + $xml, $params, $currentCursor, $listSize, $nextCursorMark + ); + } elseif ($params['cursor'] > 0) { // If we reached the end of the list but there is more than one page, we // still need to display an empty <resumptionToken> tag: $token = $xml->addChild('resumptionToken'); @@ -751,7 +830,7 @@ class Server $token->addAttribute('cursor', $params['cursor']); } - return $this->showResponse($xml); + return $response->asXML(); } /** @@ -774,7 +853,8 @@ class Server } // Begin building XML: - $xml = new SimpleXMLElement('<ListSets />'); + $response = $this->createResponse(); + $xml = $response->addChild('ListSets'); // Load set field if applicable: if (null !== $this->setField) { @@ -804,44 +884,69 @@ class Server if (!empty($this->setQueries)) { foreach ($this->setQueries as $setName => $solrQuery) { $set = $xml->addChild('set'); - $set->setSpec = $solrQuery; - $set->setName = $setName; + $set->setName = $set->setSpec = $setName; + $set->setDescription = $solrQuery; } } // Display the list: - return $this->showResponse($xml); + return $response->asXML(); } /** - * Get an object to list deleted records in the specified range. + * Get an object containing the next page of deleted records from the specified + * date range. * - * @param int $from Start date. - * @param int $until End date. + * @param int $from Start date. + * @param int $until End date. + * @param int $currentCursor Offset into result set * * @return \Zend\Db\ResultSet\AbstractResultSet */ - protected function listRecordsGetDeleted($from, $until) + protected function listRecordsGetDeleted($from, $until, $currentCursor) { $tracker = $this->tableManager->get('ChangeTracker'); return $tracker->retrieveDeleted( - $this->core, date('Y-m-d H:i:s', $from), date('Y-m-d H:i:s', $until) + $this->core, + date('Y-m-d H:i:s', $from), + date('Y-m-d H:i:s', $until), + $currentCursor, + $this->pageSize + ); + } + + /** + * Get a count of all deleted records in the specified date range. + * + * @param int $from Start date. + * @param int $until End date. + * + * @return int + */ + protected function listRecordsGetDeletedCount($from, $until) + { + $tracker = $this->tableManager->get('ChangeTracker'); + return $tracker->retrieveDeletedCount( + $this->core, + date('Y-m-d H:i:s', $from), + date('Y-m-d H:i:s', $until) ); } /** * Get an array of information on non-deleted records in the specified range. * - * @param int $from Start date. - * @param int $until End date. - * @param int $offset First record to obtain in full detail. - * @param int $limit Max number of full records to return. - * @param string $set Set to limit to (empty string for none). + * @param int $from Start date. + * @param int $until End date. + * @param string $cursorMark cursorMark for the position in the full result list. + * @param int $limit Max number of full records to return. + * @param string $format Requested record format + * @param string $set Set to limit to (empty string for none). * * @return \VuFind\Search\Base\Results Search result object. */ - protected function listRecordsGetNonDeleted($from, $until, $offset, $limit, - $set = '' + protected function listRecordsGetNonDeleted($from, $until, $cursorMark, $limit, + $format, $set = '' ) { // Set up search parameters: $results = $this->resultsManager->get($this->searchClassId); @@ -849,7 +954,7 @@ class Server $params->setLimit($limit); $params->getOptions()->disableHighlighting(); $params->getOptions()->spellcheckEnabled(false); - $params->setSort('last_indexed asc', true); + $params->setSort('last_indexed asc, id asc', true); // Construct a range query based on last indexed time: $params->setOverrideQuery( @@ -868,10 +973,19 @@ class Server $this->setField . ':"' . addcslashes($set, '"') . '"' ); } + } elseif ($this->defaultQuery) { + // Put parentheses around the query so that it does not get + // parsed as a simple field:value filter. + $params->addFilter('(' . $this->defaultQuery . ')'); + } + + if (!empty($this->recordFormatFilters[$format])) { + $params->addFilter($this->recordFormatFilters[$format]); } // Perform a Solr search: - $results->overrideStartRecord($offset + 1); + $results->overrideStartRecord(1); + $results->setCursorMark($cursorMark); // Return our results: return $results; @@ -915,7 +1029,7 @@ class Server } } if (empty($params['until'])) { - $params['until'] = date($this->iso8601); + $params['until'] = $this->getUTCDateTime('now +1 day'); if (strlen($params['until']) > strlen($params['from'])) { $params['until'] = substr($params['until'], 0, 10); } @@ -933,6 +1047,13 @@ class Server throw new \Exception('noSetHierarchy:Sets not supported'); } + // Validate set parameter: + if (!empty($params['set']) && null === $this->setField + && !isset($this->setQueries[$params['set']]) + ) { + throw new \Exception('badArgument:Invalid set specified'); + } + if (!isset($params['metadataPrefix'])) { throw new \Exception('badArgument:Missing metadataPrefix'); } @@ -1104,15 +1225,19 @@ class Server * @param int $currentCursor Current cursor position in search * results. * @param int $listSize Total size of search results. + * @param string $cursorMark cursorMark for the position in the full + * results list. * * @return void */ - protected function saveResumptionToken($xml, $params, $currentCursor, $listSize) - { + protected function saveResumptionToken($xml, $params, $currentCursor, $listSize, + $cursorMark + ) { // Save the old cursor position before overwriting it for storage in the // database! $oldCursor = $params['cursor']; $params['cursor'] = $currentCursor; + $params['cursorMark'] = $cursorMark; // Save everything to the database: $search = $this->tableManager->get('OaiResumption'); @@ -1136,28 +1261,27 @@ class Server */ protected function showError($code, $message) { - $xml = new SimpleXMLElement( - '<error>' . htmlspecialchars($message) . '</error>' - ); + // Certain errors should not echo parameters: + $echoParams = !($code == 'badVerb' || $code == 'badArgument'); + $response = $this->createResponse($echoParams); + + $xml = $response->addChild('error', htmlspecialchars($message)); if (!empty($code)) { $xml['code'] = $code; } - // Certain errors should not echo parameters: - $echoParams = !($code == 'badVerb' || $code == 'badArgument'); - return $this->showResponse($xml, $echoParams); + return $response->asXML(); } /** - * Display an OAI-PMH response (shared support method used by various + * Create an OAI-PMH response (shared support method used by various * response-specific methods). * - * @param SimpleXMLElement $body Main body of response. - * @param bool $echoParams Include params in <request> tag? + * @param bool $echoParams Include params in <request> tag? * - * @return string + * @return SimpleXMLElement */ - protected function showResponse($body, $echoParams = true) + protected function createResponse($echoParams = true) { // Set up standard response wrapper: $xml = simplexml_load_string( @@ -1169,7 +1293,7 @@ class Server . 'http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd', 'http://www.w3.org/2001/XMLSchema-instance' ); - $xml->responseDate = date($this->iso8601); + $xml->responseDate = $this->getUTCDateTime('now'); $xml->request = $this->baseURL; if ($echoParams) { foreach ($this->params as $key => $value) { @@ -1177,10 +1301,7 @@ class Server } } - // Attach main body: - SimpleXML::appendElement($xml, $body); - - return $xml->asXml(); + return $xml; } /** diff --git a/module/VuFind/src/VuFind/OAI/Server/Auth.php b/module/VuFind/src/VuFind/OAI/Server/Auth.php index 4211191e688c809df0e0ce6b1909db1a1a1c3142..cc2cbb6a30c40ba91c5102b41385efd444b345cf 100644 --- a/module/VuFind/src/VuFind/OAI/Server/Auth.php +++ b/module/VuFind/src/VuFind/OAI/Server/Auth.php @@ -49,17 +49,11 @@ class Auth extends Base * retrieving records * @param \VuFind\Record\Loader $loader Record loader * @param \VuFind\Db\Table\PluginManager $tables Table manager - * @param \Zend\Config\Config $config VuFind configuration - * @param string $baseURL The base URL for the OAI - * server - * @param array $params The incoming OAI-PMH - * parameters (i.e. $_GET) */ public function __construct(\VuFind\Search\Results\PluginManager $results, - \VuFind\Record\Loader $loader, \VuFind\Db\Table\PluginManager $tables, - \Zend\Config\Config $config, $baseURL, $params + \VuFind\Record\Loader $loader, \VuFind\Db\Table\PluginManager $tables ) { - parent::__construct($results, $loader, $tables, $config, $baseURL, $params); + parent::__construct($results, $loader, $tables); $this->core = 'authority'; $this->searchClassId = 'SolrAuth'; } diff --git a/module/VuFind/src/VuFind/OAI/ServerFactory.php b/module/VuFind/src/VuFind/OAI/ServerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4d04b6cfa1ef148b15ef6e74045bcd5810fba8da --- /dev/null +++ b/module/VuFind/src/VuFind/OAI/ServerFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * OAI Server factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package OAI_Server + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\OAI; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * OAI Server factory. + * + * @category VuFind + * @package OAI_Server + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ServerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Search\Results\PluginManager::class), + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Db\Table\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/QRCode/Loader.php b/module/VuFind/src/VuFind/QRCode/Loader.php index 56a5b6d2d81870e838b1af2f5425fdddf5113d18..6fee252f096bc5df789a34d3d1ff9aed9d8203f0 100644 --- a/module/VuFind/src/VuFind/QRCode/Loader.php +++ b/module/VuFind/src/VuFind/QRCode/Loader.php @@ -170,9 +170,10 @@ class Loader extends \VuFind\ImageLoader $code = new QrCode($text); $code->setWriterByName('png'); $code->setMargin($margin); - $code->setErrorCorrectionLevel($level); + $code->setErrorCorrectionLevel(new ErrorCorrectionLevel($level)); $code->setSize($size); $code->setEncoding('UTF-8'); + $code->setRoundBlockSize(false); // Save the values. $this->contentType = $code->getContentType(); diff --git a/module/VuFind/src/VuFind/QRCode/LoaderFactory.php b/module/VuFind/src/VuFind/QRCode/LoaderFactory.php index 67d5db90f84b7016fd88f6a0da36adb24524365f..4155d3f58f6112739afee266c6b312e84f210fa9 100644 --- a/module/VuFind/src/VuFind/QRCode/LoaderFactory.php +++ b/module/VuFind/src/VuFind/QRCode/LoaderFactory.php @@ -62,8 +62,8 @@ class LoaderFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('config'), - $container->get('VuFindTheme\ThemeInfo') + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFindTheme\ThemeInfo::class) ); } } diff --git a/module/VuFind/src/VuFind/Recommend/AbstractFacets.php b/module/VuFind/src/VuFind/Recommend/AbstractFacets.php index 200378721ed2936236b56386b04b2cfa2e4e3ce8..403b2874d7983f913462dc38d603ae989a0de114 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractFacets.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractFacets.php @@ -148,7 +148,7 @@ abstract class AbstractFacets implements RecommendInterface } // Which facets are ORed? - if (isset($config->Results_Settings->orFacets)) { + if (isset($config->$section->orFacets)) { $this->orFacets = ($config->$section->orFacets === '*') ? $allFacets : array_map('trim', explode(',', $config->$section->orFacets)); diff --git a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php index b71764cce7bbe947a7e5429953369efb94fafb25..e04a062b3a421123a3a4ed21f6b4e564527d02a1 100644 --- a/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/AbstractSummonRecommendDeferred.php @@ -118,7 +118,7 @@ class AbstractSummonRecommendDeferred implements RecommendInterface // Collect the best possible search term(s): $lookforParam = empty($settings[0]) ? 'lookfor' : $settings[0]; - $this->lookfor = $request->get($lookforParam, ''); + $this->lookfor = $request->get($lookforParam, ''); if (empty($this->lookfor) && is_object($params)) { $this->lookfor = $params->getQuery()->getAllTerms(); } diff --git a/module/VuFind/src/VuFind/Recommend/AuthorInfoFactory.php b/module/VuFind/src/VuFind/Recommend/AuthorInfoFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..831a2fd42a9a0902e216fc8b8d8282646fc91bce --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/AuthorInfoFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * AuthorInfo recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; +use VuFind\Config\PluginManager as ConfigManager; + +/** + * AuthorInfo recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class AuthorInfoFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(ConfigManager::class)->get('config'); + return new $requestedName( + $container->get(\VuFind\Search\Results\PluginManager::class), + $container->get(\VuFindHttp\HttpService::class)->createClient(), + $config->Content->authors ?? '' + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php b/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php index 9928edea8552ec7cccaaec258c4e909ad4ade236..08c62ef023411ad8faede55ec0e309ab366ed671 100644 --- a/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php +++ b/module/VuFind/src/VuFind/Recommend/AuthorityRecommend.php @@ -100,6 +100,13 @@ class AuthorityRecommend implements RecommendInterface */ protected $mode = '*'; + /** + * Header to use in the user interface. + * + * @var string + */ + protected $header = 'See also'; + /** * Constructor * @@ -126,6 +133,8 @@ class AuthorityRecommend implements RecommendInterface $this->resultLimit = intval($params[$i + 1]); } elseif ($params[$i] == '__mode__') { $this->mode = strtolower($params[$i + 1]); + } elseif ($params[$i] == '__header__') { + $this->header = $params[$i + 1]; } else { $this->filters[] = $params[$i] . ':' . $params[$i + 1]; } @@ -272,6 +281,11 @@ class AuthorityRecommend implements RecommendInterface { $this->results = $results; + // empty searches such as New Items will return blank + if ($this->lookfor == null) { + return; + } + // function will return blank on Advanced Search if ($results->getParams()->getSearchType() == 'advanced') { return; @@ -295,6 +309,16 @@ class AuthorityRecommend implements RecommendInterface } } + /** + * Get the header to display in the user interface. + * + * @return string + */ + public function getHeader() + { + return $this->header; + } + /** * Get recommendations (for use in the view). * diff --git a/module/VuFind/src/VuFind/Recommend/CollectionSideFacetsFactory.php b/module/VuFind/src/VuFind/Recommend/CollectionSideFacetsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7593eee0c53a78acc672dc155ae11e57bb622484 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/CollectionSideFacetsFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * CollectionSideFacets recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * CollectionSideFacets recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CollectionSideFacetsFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/DPLATerms.php b/module/VuFind/src/VuFind/Recommend/DPLATerms.php index 34404018ee61c3fca05d519e9a30d98f0fbdd2a8..7fb045d285b1c5825bfa1ff06e858fcc74efced6 100644 --- a/module/VuFind/src/VuFind/Recommend/DPLATerms.php +++ b/module/VuFind/src/VuFind/Recommend/DPLATerms.php @@ -183,7 +183,7 @@ class DPLATerms implements RecommendInterface { // Extract the first search term from the search object: $search = $this->searchObject->getParams()->getQuery(); - $filters = $this->searchObject->getParams()->getFilters(); + $filters = $this->searchObject->getParams()->getRawFilters(); $lookfor = ($search instanceof \VuFindSearch\Query\Query) ? $search->getString() : ''; diff --git a/module/VuFind/src/VuFind/Recommend/DPLATermsFactory.php b/module/VuFind/src/VuFind/Recommend/DPLATermsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5288c802f2eed0a1322482c71173895d9b017741 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/DPLATermsFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * DPLATerms recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * DPLATerms recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DPLATermsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + if (!isset($config->DPLA->apiKey)) { + throw new \Exception('DPLA API key missing from configuration.'); + } + return new $requestedName( + $config->DPLA->apiKey, + $container->get(\VuFindHttp\HttpService::class)->createClient() + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php b/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php index f163c352dd59c5e01411561b7cd7ae83ea956f41..a8fdf54ed80ecf473d37a0638e2590f3cc91d577 100644 --- a/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php +++ b/module/VuFind/src/VuFind/Recommend/EuropeanaResults.php @@ -196,7 +196,7 @@ class EuropeanaResults implements RecommendInterface, public function init($params, $request) { // Collect the best possible search term(s): - $this->lookfor = $request->get('lookfor', ''); + $this->lookfor = $request->get('lookfor', ''); if (empty($this->lookfor) && is_object($params)) { $this->lookfor = $params->getQuery()->getAllTerms(); } @@ -233,7 +233,7 @@ class EuropeanaResults implements RecommendInterface, $resultsProcessed[] = [ 'title' => $value->getTitle(), 'link' => $link, - 'enclosure' => $value->getEnclosure()['url'] + 'enclosure' => $value->getEnclosure()['url'] ?? null ]; } if (count($resultsProcessed) == $this->limit) { diff --git a/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php index 9be50bdf5fa6af55677cdd3ab85c60b964951c64..d26cf3ce5f4ff8c75816fa390415cce556599358 100644 --- a/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsDeferred.php @@ -100,7 +100,7 @@ class EuropeanaResultsDeferred implements RecommendInterface } // Collect the best possible search term(s): - $this->lookfor = $request->get('lookfor', ''); + $this->lookfor = $request->get('lookfor', ''); if (empty($this->lookfor) && is_object($params)) { $this->lookfor = $params->getQuery()->getAllTerms(); } diff --git a/module/VuFind/src/VuFind/Recommend/EuropeanaResultsFactory.php b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..982be20c17e1a98edab2dca9dbb3d4603f690a92 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/EuropeanaResultsFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * EuropeanaResults recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * EuropeanaResults recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class EuropeanaResultsFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + if (!isset($config->Content->europeanaAPI)) { + throw new \Exception('Europeana API key missing from configuration.'); + } + return new $requestedName($config->Content->europeanaAPI); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/ExpandFacetsFactory.php b/module/VuFind/src/VuFind/Recommend/ExpandFacetsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..18d461d87b212feaafb9aadd3fb022d14012007c --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/ExpandFacetsFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * ExpandFacets recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * ExpandFacets recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ExpandFacetsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Search\Results\PluginManager::class)->get('Solr') + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/ExternalSearch.php b/module/VuFind/src/VuFind/Recommend/ExternalSearch.php new file mode 100644 index 0000000000000000000000000000000000000000..04233927846a4ac7b82a7a02977bcdbca4af5dca --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/ExternalSearch.php @@ -0,0 +1,129 @@ +<?php +/** + * ExternalSearch Recommendation Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Mark Triggs <vufind-tech@lists.sourceforge.net> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFind\Recommend; + +/** + * ExternalSearch Recommendation Module + * + * @category VuFind + * @package Recommendations + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class ExternalSearch implements RecommendInterface +{ + /** + * Link text + * + * @var string + */ + protected $linkText; + + /** + * URL template string + * + * @var string + */ + protected $template; + + /** + * Search query + * + * @var string + */ + protected $lookfor; + + /** + * Store the configuration of the recommendation module. + * + * @param string $settingsStr Settings from searches.ini. + * + * @return void + */ + public function setConfig($settingsStr) + { + // Parse the settings: + $settings = explode(':', $settingsStr, 2); + $this->linkText = $settings[0] ?? null; + $this->template = $settings[1] ?? null; + } + + /** + * Called at the end of the Search Params objects' initFromRequest() method. + * This method is responsible for setting search parameters needed by the + * recommendation module and for reading any existing search parameters that may + * be needed. + * + * @param \VuFind\Search\Base\Params $params Search parameter object + * @param \Zend\StdLib\Parameters $request Parameter object representing user + * request. + * + * @return void + */ + public function init($params, $request) + { + $this->lookfor = $request->get('lookfor'); + } + + /** + * Called after the Search Results object has performed its main search. This + * may be used to extract necessary information from the Search Results object + * or to perform completely unrelated processing. + * + * @param \VuFind\Search\Base\Results $results Search results object + * + * @return void + */ + public function process($results) + { + // no action needed + } + + /** + * Get the link text. + * + * @return string + */ + public function getLinkText() + { + return $this->linkText; + } + + /** + * Get the link URL. + * + * @return string + */ + public function getUrl() + { + return (false === strpos($this->template, '%%lookfor%%')) + ? $this->template . urlencode($this->lookfor) + : str_replace('%%lookfor%%', urlencode($this->lookfor), $this->template); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/Factory.php b/module/VuFind/src/VuFind/Recommend/Factory.php deleted file mode 100644 index de419acea35f3e801dc421f75be7e11a3340b3f9..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Recommend/Factory.php +++ /dev/null @@ -1,367 +0,0 @@ -<?php -/** - * Recommendation Module Factory Class - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Recommendations - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Recommend; - -use Zend\ServiceManager\ServiceManager; - -/** - * Recommendation Module Factory Class - * - * @category VuFind - * @package Recommendations - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for AuthorFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return AuthorFacets - */ - public static function getAuthorFacets(ServiceManager $sm) - { - return new AuthorFacets( - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Factory for AuthorInfo module. - * - * @param ServiceManager $sm Service manager. - * - * @return AuthorInfo - */ - public static function getAuthorInfo(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - return new AuthorInfo( - $sm->get('VuFind\Search\Results\PluginManager'), - $sm->get('VuFindHttp\HttpService')->createClient(), - isset($config->Content->authors) ? $config->Content->authors : '' - ); - } - - /** - * Factory for AuthorityRecommend module. - * - * @param ServiceManager $sm Service manager. - * - * @return AuthorityRecommend - */ - public static function getAuthorityRecommend(ServiceManager $sm) - { - return new AuthorityRecommend( - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Factory for CatalogResults module. - * - * @param ServiceManager $sm Service manager. - * - * @return CatalogResults - */ - public static function getCatalogResults(ServiceManager $sm) - { - return new CatalogResults($sm->get('VuFind\Search\SearchRunner')); - } - - /** - * Factory for CollectionSideFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return CollectionSideFacets - */ - public static function getCollectionSideFacets(ServiceManager $sm) - { - return new CollectionSideFacets( - $sm->get('VuFind\Config\PluginManager'), - $sm->get('VuFind\Search\Solr\HierarchicalFacetHelper') - ); - } - - /** - * Factory for DPLA Terms module. - * - * @param ServiceManager $sm Service manager. - * - * @return DPLATerms - */ - public static function getDPLATerms(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - if (!isset($config->DPLA->apiKey)) { - throw new \Exception('DPLA API key missing from configuration.'); - } - return new DPLATerms( - $config->DPLA->apiKey, - $sm->get('VuFindHttp\HttpService')->createClient() - ); - } - - /** - * Factory for EuropeanaResults module. - * - * @param ServiceManager $sm Service manager. - * - * @return EuropeanaResults - */ - public static function getEuropeanaResults(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - return new EuropeanaResults( - $config->Content->europeanaAPI - ); - } - - /** - * Factory for ExpandFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return ExpandFacets - */ - public static function getExpandFacets(ServiceManager $sm) - { - return new ExpandFacets( - $sm->get('VuFind\Config\PluginManager'), - $sm->get('VuFind\Search\Results\PluginManager') - ->get('Solr') - ); - } - - /** - * Factory for FavoriteFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return FavoriteFacets - */ - public static function getFavoriteFacets(ServiceManager $sm) - { - return new FavoriteFacets( - $sm->get('VuFind\Config\PluginManager'), - null, - $sm->get('VuFind\Config\AccountCapabilities')->getTagSetting() - ); - } - - /** - * Factory for MapSelection module. - * - * @param ServiceManager $sm Service manager. - * - * @return MapSelection - */ - public function getMapSelection(ServiceManager $sm) - { - $backend = $sm->get('VuFind\Search\BackendManager'); - $solr = $backend->get('Solr'); - - // add basemap options - $basemapConfig = $sm->get('VuFind\GeoFeatures\BasemapConfig'); - $basemapOptions = $basemapConfig->getBasemap('MapSelection'); - - // get MapSelection options - $mapSelectionConfig = $sm->get('VuFind\GeoFeatures\MapSelectionConfig'); - $mapSelectionOptions = $mapSelectionConfig->getMapSelectionOptions(); - - return new MapSelection($solr, $basemapOptions, $mapSelectionOptions); - } - - /** - * Factory for Random Recommendations. - * - * @param ServiceManager $sm Service manager. - * - * @return RandomRecommend - */ - public static function getRandomRecommend(ServiceManager $sm) - { - return new RandomRecommend( - $sm->get('VuFindSearch\Service'), - $sm->get('VuFind\Search\Params\PluginManager') - ); - } - - /** - * Factory for SideFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return SideFacets - */ - public static function getSideFacets(ServiceManager $sm) - { - return new SideFacets( - $sm->get('VuFind\Config\PluginManager'), - $sm->get('VuFind\Search\Solr\HierarchicalFacetHelper') - ); - } - - /** - * Factory for SideFacetsDeferred module. - * - * @param ServiceManager $sm Service manager. - * - * @return SideFacets - */ - public static function getSideFacetsDeferred(ServiceManager $sm) - { - return new SideFacetsDeferred($sm->get('VuFind\Config\PluginManager')); - } - - /** - * Factory for SummonBestBets module. - * - * @param ServiceManager $sm Service manager. - * - * @return SummonBestBets - */ - public static function getSummonBestBets(ServiceManager $sm) - { - return new SummonBestBets( - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Factory for SummonDatabases module. - * - * @param ServiceManager $sm Service manager. - * - * @return SummonDatabases - */ - public static function getSummonDatabases(ServiceManager $sm) - { - return new SummonDatabases( - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Factory for SummonResults module. - * - * @param ServiceManager $sm Service manager. - * - * @return SummonResults - */ - public static function getSummonResults(ServiceManager $sm) - { - return new SummonResults($sm->get('VuFind\Search\SearchRunner')); - } - - /** - * Factory for SummonTopics module. - * - * @param ServiceManager $sm Service manager. - * - * @return SummonTopics - */ - public static function getSummonTopics(ServiceManager $sm) - { - return new SummonTopics( - $sm->get('VuFind\Search\Results\PluginManager') - ); - } - - /** - * Factory for SwitchQuery module. - * - * @param ServiceManager $sm Service manager. - * - * @return SwitchQuery - */ - public static function getSwitchQuery(ServiceManager $sm) - { - return new SwitchQuery( - $sm->get('VuFind\Search\BackendManager') - ); - } - - /** - * Factory for TopFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return TopFacets - */ - public static function getTopFacets(ServiceManager $sm) - { - return new TopFacets( - $sm->get('VuFind\Config\PluginManager') - ); - } - - /** - * Factory for VisualFacets module. - * - * @param ServiceManager $sm Service manager. - * - * @return VisualFacets - */ - public static function getVisualFacets(ServiceManager $sm) - { - return new VisualFacets( - $sm->get('VuFind\Config\PluginManager') - ); - } - - /** - * Factory for WebResults module. - * - * @param ServiceManager $sm Service manager. - * - * @return WebResults - */ - public static function getWebResults(ServiceManager $sm) - { - return new WebResults($sm->get('VuFind\Search\SearchRunner')); - } - - /** - * Factory for WorldCatIdentities module. - * - * @param ServiceManager $sm Service manager. - * - * @return WorldCatIdentities - */ - public static function getWorldCatIdentities(ServiceManager $sm) - { - return new WorldCatIdentities($sm->get('VuFind\Connection\WorldCatUtils')); - } -} diff --git a/module/VuFind/src/VuFind/Recommend/FavoriteFacetsFactory.php b/module/VuFind/src/VuFind/Recommend/FavoriteFacetsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..6dd3b5fdf8ae980c6836350f9e17de578993bc79 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/FavoriteFacetsFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * FavoriteFacets recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * FavoriteFacets recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class FavoriteFacetsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class), + null, + $capabilities->getTagSetting() + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/InjectConfigManagerFactory.php b/module/VuFind/src/VuFind/Recommend/InjectConfigManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..664628b85a26d4ab90c49159a06a11cee90df739 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/InjectConfigManagerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for instantiating recommendation modules with config plugin manager. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; +use VuFind\Config\PluginManager as ConfigManager; + +/** + * Factory for instantiating recommendation modules with config plugin manager. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class InjectConfigManagerFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(ConfigManager::class)); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/InjectResultsManagerFactory.php b/module/VuFind/src/VuFind/Recommend/InjectResultsManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7eb3f7b38ddd0315307cd27f2d61d72628672244 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/InjectResultsManagerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for instantiating recommendation modules with Results plugin manager. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; +use VuFind\Search\Results\PluginManager as ResultsManager; + +/** + * Factory for instantiating recommendation modules with Results plugin manager. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class InjectResultsManagerFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(ResultsManager::class)); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/InjectSearchRunnerFactory.php b/module/VuFind/src/VuFind/Recommend/InjectSearchRunnerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..602a1ababdca45633f96042a87893cba0865396e --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/InjectSearchRunnerFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for instantiating recommendation modules with search runner. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; +use VuFind\Search\SearchRunner; + +/** + * Factory for instantiating recommendation modules with search runner. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class InjectSearchRunnerFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(SearchRunner::class)); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/MapSelection.php b/module/VuFind/src/VuFind/Recommend/MapSelection.php index 219907c3b446c14188dea9b0a12fcb7954b98be1..e9578445b784f1feecdb2173289493c097a5462a 100644 --- a/module/VuFind/src/VuFind/Recommend/MapSelection.php +++ b/module/VuFind/src/VuFind/Recommend/MapSelection.php @@ -218,7 +218,7 @@ class MapSelection implements \VuFind\Recommend\RecommendInterface, public function process($results) { $reorder_coords = []; - $filters = $results->getParams()->getFilters(); + $filters = $results->getParams()->getRawFilters(); foreach ($filters as $key => $value) { if ($key == $this->geoField) { $match = []; diff --git a/module/VuFind/src/VuFind/Recommend/MapSelectionFactory.php b/module/VuFind/src/VuFind/Recommend/MapSelectionFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..123bb91b3048f9e178b7f3a45483c2283af0c4e7 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/MapSelectionFactory.php @@ -0,0 +1,79 @@ +<?php +/** + * MapSelection recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * MapSelection recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MapSelectionFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $backend = $container->get(\VuFind\Search\BackendManager::class); + $solr = $backend->get('Solr'); + + // add basemap options + $basemapConfig = $container->get(\VuFind\GeoFeatures\BasemapConfig::class); + $basemapOptions = $basemapConfig->getBasemap('MapSelection'); + + // get MapSelection options + $mapSelectionConfig + = $container->get(\VuFind\GeoFeatures\MapSelectionConfig::class); + $mapSelectionOptions = $mapSelectionConfig->getMapSelectionOptions(); + + return new $requestedName($solr, $basemapOptions, $mapSelectionOptions); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php index 12b144da0232a186ba2847227599ddb1c2ad17dd..7704b10ef23d3e54ae5bdbc74d68bb19e5179648 100644 --- a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php +++ b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjects.php @@ -147,7 +147,7 @@ class OpenLibrarySubjects implements RecommendInterface, public function init($params, $request) { // Get and normalise $requestParam - $this->subject = $request->get($this->requestParam); + $this->subject = $request->get($this->requestParam); // Set up the published date range if it has not already been provided: if (empty($this->publishedIn) && $this->pubFilter) { @@ -205,7 +205,7 @@ class OpenLibrarySubjects implements RecommendInterface, if (null !== $from && null !== $to) { $range = ['from' => $from, 'to' => $to]; } elseif (is_object($params)) { - $currentFilters = $params->getFilters(); + $currentFilters = $params->getRawFilters(); if (isset($currentFilters[$field][0])) { $range = SolrUtils::parseRange($currentFilters[$field][0]); } diff --git a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php index 081460d039baf0909867ffaea56613bb7b05a24e..a673407cc72d1986a9e0d3cd2528cdc83225e4e5 100644 --- a/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php +++ b/module/VuFind/src/VuFind/Recommend/OpenLibrarySubjectsDeferred.php @@ -112,7 +112,7 @@ class OpenLibrarySubjectsDeferred extends OpenLibrarySubjects $this->processedParams = implode(':', $settings); // Collect the best possible search term(s): - $this->subject = $request->get($this->requestParam); + $this->subject = $request->get($this->requestParam); if (empty($this->subject) && is_object($params)) { $this->subject = $params->getQuery()->getAllTerms(); } diff --git a/module/VuFind/src/VuFind/Recommend/PluginManager.php b/module/VuFind/src/VuFind/Recommend/PluginManager.php index 3a4cdee15c61bc94793c94a1747915d0773a2928..5ec9f3aee1bd0cf95d73114468a7fa1d67350d7f 100644 --- a/module/VuFind/src/VuFind/Recommend/PluginManager.php +++ b/module/VuFind/src/VuFind/Recommend/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Recommend; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Recommendation module plugin manager * @@ -44,46 +46,47 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'alphabrowselink' => 'VuFind\Recommend\AlphaBrowseLink', - 'authorfacets' => 'VuFind\Recommend\AuthorFacets', - 'authorinfo' => 'VuFind\Recommend\AuthorInfo', - 'authorityrecommend' => 'VuFind\Recommend\AuthorityRecommend', - 'catalogresults' => 'VuFind\Recommend\CatalogResults', - 'channels' => 'VuFind\Recommend\Channels', - 'collectionsidefacets' => 'VuFind\Recommend\CollectionSideFacets', - 'doi' => 'VuFind\Recommend\DOI', - 'dplaterms' => 'VuFind\Recommend\DPLATerms', - 'europeanaresults' => 'VuFind\Recommend\EuropeanaResults', - 'europeanaresultsdeferred' => 'VuFind\Recommend\EuropeanaResultsDeferred', - 'expandfacets' => 'VuFind\Recommend\ExpandFacets', - 'facetcloud' => 'VuFind\Recommend\FacetCloud', - 'favoritefacets' => 'VuFind\Recommend\FavoriteFacets', - 'libraryh3lp' => 'VuFind\Recommend\Libraryh3lp', - 'mapselection' => 'VuFind\Recommend\MapSelection', - 'sidefacets' => 'VuFind\Recommend\SideFacets', - 'sidefacetsdeferred' => 'VuFind\Recommend\SideFacetsDeferred', - 'openlibrarysubjects' => 'VuFind\Recommend\OpenLibrarySubjects', - 'openlibrarysubjectsdeferred' => - 'VuFind\Recommend\OpenLibrarySubjectsDeferred', - 'pubdatevisajax' => 'VuFind\Recommend\PubDateVisAjax', - 'randomrecommend' => 'VuFind\Recommend\RandomRecommend', - 'removefilters' => 'VuFind\Recommend\RemoveFilters', - 'resultgooglemapajax' => 'VuFind\Recommend\Deprecated', - 'spellingsuggestions' => 'VuFind\Recommend\SpellingSuggestions', - 'summonbestbets' => 'VuFind\Recommend\SummonBestBets', - 'summonbestbetsdeferred' => 'VuFind\Recommend\SummonBestBetsDeferred', - 'summondatabases' => 'VuFind\Recommend\SummonDatabases', - 'summondatabasesdeferred' => 'VuFind\Recommend\SummonDatabasesDeferred', - 'summonresults' => 'VuFind\Recommend\SummonResults', - 'summonresultsdeferred' => 'VuFind\Recommend\SummonResultsDeferred', - 'summontopics' => 'VuFind\Recommend\SummonTopics', - 'switchquery' => 'VuFind\Recommend\SwitchQuery', - 'switchtype' => 'VuFind\Recommend\SwitchType', - 'topfacets' => 'VuFind\Recommend\TopFacets', - 'visualfacets' => 'VuFind\Recommend\VisualFacets', - 'webresults' => 'VuFind\Recommend\WebResults', - 'worldcatidentities' => 'VuFind\Recommend\WorldCatIdentities', - 'worldcatterms' => 'VuFind\Recommend\Deprecated', + 'alphabrowselink' => AlphaBrowseLink::class, + 'authorfacets' => AuthorFacets::class, + 'authorinfo' => AuthorInfo::class, + 'authorityrecommend' => AuthorityRecommend::class, + 'catalogresults' => CatalogResults::class, + 'channels' => Channels::class, + 'collectionsidefacets' => CollectionSideFacets::class, + 'doi' => DOI::class, + 'dplaterms' => DPLATerms::class, + 'europeanaresults' => EuropeanaResults::class, + 'europeanaresultsdeferred' => EuropeanaResultsDeferred::class, + 'expandfacets' => ExpandFacets::class, + 'externalsearch' => ExternalSearch::class, + 'facetcloud' => FacetCloud::class, + 'favoritefacets' => FavoriteFacets::class, + 'libraryh3lp' => Libraryh3lp::class, + 'mapselection' => MapSelection::class, + 'sidefacets' => SideFacets::class, + 'sidefacetsdeferred' => SideFacetsDeferred::class, + 'openlibrarysubjects' => OpenLibrarySubjects::class, + 'openlibrarysubjectsdeferred' => OpenLibrarySubjectsDeferred::class, + 'pubdatevisajax' => PubDateVisAjax::class, + 'randomrecommend' => RandomRecommend::class, + 'recommendlinks' => RecommendLinks::class, + 'removefilters' => RemoveFilters::class, + 'resultgooglemapajax' => Deprecated::class, + 'spellingsuggestions' => SpellingSuggestions::class, + 'summonbestbets' => SummonBestBets::class, + 'summonbestbetsdeferred' => SummonBestBetsDeferred::class, + 'summondatabases' => SummonDatabases::class, + 'summondatabasesdeferred' => SummonDatabasesDeferred::class, + 'summonresults' => SummonResults::class, + 'summonresultsdeferred' => SummonResultsDeferred::class, + 'summontopics' => SummonTopics::class, + 'switchquery' => SwitchQuery::class, + 'switchtype' => SwitchType::class, + 'topfacets' => TopFacets::class, + 'visualfacets' => VisualFacets::class, + 'webresults' => WebResults::class, + 'worldcatidentities' => WorldCatIdentities::class, + 'worldcatterms' => Deprecated::class, ]; /** @@ -92,75 +95,46 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Recommend\AlphaBrowseLink' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\AuthorFacets' => - 'VuFind\Recommend\Factory::getAuthorFacets', - 'VuFind\Recommend\AuthorInfo' => 'VuFind\Recommend\Factory::getAuthorInfo', - 'VuFind\Recommend\AuthorityRecommend' => - 'VuFind\Recommend\Factory::getAuthorityRecommend', - 'VuFind\Recommend\CatalogResults' => - 'VuFind\Recommend\Factory::getCatalogResults', - 'VuFind\Recommend\Channels' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\CollectionSideFacets' => - 'VuFind\Recommend\Factory::getCollectionSideFacets', - 'VuFind\Recommend\Deprecated' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\DOI' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\DPLATerms' => 'VuFind\Recommend\Factory::getDPLATerms', - 'VuFind\Recommend\EuropeanaResults' => - 'VuFind\Recommend\Factory::getEuropeanaResults', - 'VuFind\Recommend\EuropeanaResultsDeferred' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\ExpandFacets' => - 'VuFind\Recommend\Factory::getExpandFacets', - 'VuFind\Recommend\FacetCloud' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\FavoriteFacets' => - 'VuFind\Recommend\Factory::getFavoriteFacets', - 'VuFind\Recommend\Libraryh3lp' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\MapSelection' => - 'VuFind\Recommend\Factory::getMapSelection', - 'VuFind\Recommend\SideFacets' => 'VuFind\Recommend\Factory::getSideFacets', - 'VuFind\Recommend\SideFacetsDeferred' - => 'VuFind\Recommend\Factory::getSideFacetsDeferred', - 'VuFind\Recommend\OpenLibrarySubjects' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\OpenLibrarySubjectsDeferred' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\PubDateVisAjax' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\RandomRecommend' => - 'VuFind\Recommend\Factory::getRandomRecommend', - 'VuFind\Recommend\RemoveFilters' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\SpellingSuggestions' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\SummonBestBets' => - 'VuFind\Recommend\Factory::getSummonBestBets', - 'VuFind\Recommend\SummonBestBetsDeferred' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\SummonDatabases' => - 'VuFind\Recommend\Factory::getSummonDatabases', - 'VuFind\Recommend\SummonDatabasesDeferred' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\SummonResults' => - 'VuFind\Recommend\Factory::getSummonResults', - 'VuFind\Recommend\SummonResultsDeferred' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\SummonTopics' => - 'VuFind\Recommend\Factory::getSummonTopics', - 'VuFind\Recommend\SwitchQuery' => 'VuFind\Recommend\Factory::getSwitchQuery', - 'VuFind\Recommend\SwitchType' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Recommend\TopFacets' => 'VuFind\Recommend\Factory::getTopFacets', - 'VuFind\Recommend\VisualFacets' => - 'VuFind\Recommend\Factory::getVisualFacets', - 'VuFind\Recommend\WebResults' => 'VuFind\Recommend\Factory::getWebResults', - 'VuFind\Recommend\WorldCatIdentities' => - 'VuFind\Recommend\Factory::getWorldCatIdentities', + AlphaBrowseLink::class => InvokableFactory::class, + AuthorFacets::class => InjectResultsManagerFactory::class, + AuthorInfo::class => AuthorInfoFactory::class, + AuthorityRecommend::class => InjectResultsManagerFactory::class, + CatalogResults::class => InjectSearchRunnerFactory::class, + Channels::class => InvokableFactory::class, + CollectionSideFacets::class => CollectionSideFacetsFactory::class, + Deprecated::class => InvokableFactory::class, + DOI::class => InvokableFactory::class, + DPLATerms::class => DPLATermsFactory::class, + EuropeanaResults::class => EuropeanaResultsFactory::class, + EuropeanaResultsDeferred::class => InvokableFactory::class, + ExpandFacets::class => ExpandFacetsFactory::class, + ExternalSearch::class => InvokableFactory::class, + FacetCloud::class => InvokableFactory::class, + FavoriteFacets::class => FavoriteFacetsFactory::class, + Libraryh3lp::class => InvokableFactory::class, + MapSelection::class => MapSelectionFactory::class, + OpenLibrarySubjects::class => InvokableFactory::class, + OpenLibrarySubjectsDeferred::class => InvokableFactory::class, + PubDateVisAjax::class => InvokableFactory::class, + RandomRecommend::class => RandomRecommendFactory::class, + RecommendLinks::class => InjectConfigManagerFactory::class, + RemoveFilters::class => InvokableFactory::class, + SideFacets::class => SideFacetsFactory::class, + SideFacetsDeferred::class => InjectConfigManagerFactory::class, + SpellingSuggestions::class => InvokableFactory::class, + SummonBestBets::class => InjectResultsManagerFactory::class, + SummonBestBetsDeferred::class => InvokableFactory::class, + SummonDatabases::class => InjectResultsManagerFactory::class, + SummonDatabasesDeferred::class => InvokableFactory::class, + SummonResults::class => InjectSearchRunnerFactory::class, + SummonResultsDeferred::class => InvokableFactory::class, + SummonTopics::class => InjectResultsManagerFactory::class, + SwitchQuery::class => SwitchQueryFactory::class, + SwitchType::class => InvokableFactory::class, + TopFacets::class => InjectConfigManagerFactory::class, + VisualFacets::class => InjectConfigManagerFactory::class, + WebResults::class => InjectSearchRunnerFactory::class, + WorldCatIdentities::class => WorldCatIdentitiesFactory::class, ]; /** @@ -179,7 +153,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager // we are building a brand new object. $this->sharedByDefault = false; - $this->addAbstractFactory('VuFind\Recommend\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -191,6 +165,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Recommend\RecommendInterface'; + return RecommendInterface::class; } } diff --git a/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php b/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php index bf4c1da89a8efb356c29c96715547600840a24a3..894939a2e7f1d951f8d30969efb710999b824dab 100644 --- a/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php +++ b/module/VuFind/src/VuFind/Recommend/PubDateVisAjax.php @@ -134,7 +134,7 @@ class PubDateVisAjax implements RecommendInterface return []; } return $this->processDateFacets( - $this->searchObject->getParams()->getFilters() + $this->searchObject->getParams()->getRawFilters() ); } diff --git a/module/VuFind/src/VuFind/Recommend/RandomRecommendFactory.php b/module/VuFind/src/VuFind/Recommend/RandomRecommendFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4072c585429708bfb2b8a35adc2efcee30ca9e04 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/RandomRecommendFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * RandomRecommend recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * RandomRecommend recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class RandomRecommendFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFindSearch\Service::class), + $container->get(\VuFind\Search\Params\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/RecommendLinks.php b/module/VuFind/src/VuFind/Recommend/RecommendLinks.php new file mode 100644 index 0000000000000000000000000000000000000000..4e7c40a79f7ee7b40e8ad56695c5252440e64957 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/RecommendLinks.php @@ -0,0 +1,129 @@ +<?php +/** + * RecommendLinks Recommendations Module + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Vaclav Rosecky <xrosecky@gmail.com> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +namespace VuFind\Recommend; + +/** + * RecommendLinks Recommendations Module + * + * This class recommends links to services, that user may try. + * + * @category VuFind + * @package Recommendations + * @author Vaclav Rosecky <xrosecky@gmail.com> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:recommendation_modules Wiki + */ +class RecommendLinks implements RecommendInterface +{ + /** + * Links to show + * + * @var array + */ + protected $links = []; + + /** + * Configuration loader + * + * @var \VuFind\Config\PluginManager + */ + protected $configLoader; + + /** + * Constructor + * + * @param \VuFind\Config\PluginManager $configLoader Configuration loader + */ + public function __construct(\VuFind\Config\PluginManager $configLoader) + { + $this->configLoader = $configLoader; + } + + /** + * Store the configuration of the recommendation module. + * + * RecommendLinks:[ini section]:[ini name] + * Display a list of recommended links, taken from [ini section] in + * [ini name], where the section is a mapping of label => URL. [ini name] + * defaults to searches.ini, and [ini section] defaults to RecommendLinks. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $settings = explode(':', $settings); + $mainSection = empty($settings[0]) ? 'RecommendLinks' : $settings[0]; + $iniName = $settings[1] ?? 'searches'; + $config = $this->configLoader->get($iniName); + $this->links = isset($config->$mainSection) + ? $config->$mainSection->toArray() : []; + } + + /** + * Called at the end of the Search Params objects' initFromRequest() method. + * This method is responsible for setting search parameters needed by the + * recommendation module and for reading any existing search parameters that may + * be needed. + * + * @param \VuFind\Search\Base\Params $params Search parameter object + * @param \Zend\StdLib\Parameters $request Parameter object representing user + * request. + * + * @return void + */ + public function init($params, $request) + { + // No action needed. + } + + /** + * Called after the Search Results object has performed its main search. This + * may be used to extract necessary information from the Search Results object + * or to perform completely unrelated processing. + * + * @param \VuFind\Search\Base\Results $results Search results object + * + * @return void + */ + public function process($results) + { + // No action needed. + } + + /** + * Get array of links with title as key and value as link + * + * @return \VuFind\Search\Base\Results + */ + public function getLinks() + { + return $this->links; + } +} diff --git a/module/VuFind/src/VuFind/Recommend/SideFacets.php b/module/VuFind/src/VuFind/Recommend/SideFacets.php index 58471426d6cd073b1e8875f2ae184953e6effd17..a0f63f664afeb9c6e32733f6b405a1714f9a9936 100644 --- a/module/VuFind/src/VuFind/Recommend/SideFacets.php +++ b/module/VuFind/src/VuFind/Recommend/SideFacets.php @@ -247,6 +247,17 @@ class SideFacets extends AbstractFacets } } + /** + * Get checkbox facet information from the search results. + * + * @return array + */ + public function getCheckboxFacetSet() + { + return $this->results->getParams() + ->getCheckboxFacets(array_keys($this->checkboxFacets)); + } + /** * Get facet information from the search results. * @@ -353,7 +364,7 @@ class SideFacets extends AbstractFacets if (empty($this->collapsedFacets)) { return []; } elseif ($this->collapsedFacets == '*') { - return array_keys($this->getFacetSet()); + return array_keys($this->mainFacets); } return array_map('trim', explode(',', $this->collapsedFacets)); } @@ -401,39 +412,6 @@ class SideFacets extends AbstractFacets return 'more'; } - /** - * Get the list of filters to display - * - * @param array $extraFilters Extra filters to add to the list. - * - * @return array - */ - public function getVisibleFilters($extraFilters = []) - { - // Merge extras into main list: - $filterList = array_merge( - $this->results->getParams()->getFilterList(true), $extraFilters - ); - - // Filter out suppressed values: - $final = []; - foreach ($filterList as $field => $filters) { - $current = []; - foreach ($filters as $filter) { - if (!isset($filter['suppressDisplay']) - || !$filter['suppressDisplay'] - ) { - $current[] = $filter; - } - } - if (!empty($current)) { - $final[$field] = $current; - } - } - - return $final; - } - /** * Return range facet information in a format processed for use in the view. * @@ -443,7 +421,7 @@ class SideFacets extends AbstractFacets */ protected function getRangeFacets($property) { - $filters = $this->results->getParams()->getFilters(); + $filters = $this->results->getParams()->getRawFilters(); $result = []; if (isset($this->$property) && is_array($this->$property)) { foreach ($this->$property as $current) { diff --git a/module/VuFind/src/VuFind/Recommend/SideFacetsFactory.php b/module/VuFind/src/VuFind/Recommend/SideFacetsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dfc32c1fb9d9290caccebe74c698cbe931404b18 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/SideFacetsFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * SideFacets recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * SideFacets recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SideFacetsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/SwitchQueryFactory.php b/module/VuFind/src/VuFind/Recommend/SwitchQueryFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8fe0d2e0bbd020b36ea1b13f2f72294ed671f514 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/SwitchQueryFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * SwitchQuery recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * SwitchQuery recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SwitchQueryFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Search\BackendManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Recommend/WorldCatIdentitiesFactory.php b/module/VuFind/src/VuFind/Recommend/WorldCatIdentitiesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8e6607456314a64387bc305be5dc4f1ca231c4a0 --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/WorldCatIdentitiesFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * WorldCatIdentities recommendation module factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Recommend; + +use Interop\Container\ContainerInterface; + +/** + * WorldCatIdentities recommendation module factory. + * + * @category VuFind + * @package Recommendations + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class WorldCatIdentitiesFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Connection\WorldCatUtils::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Record/CacheFactory.php b/module/VuFind/src/VuFind/Record/CacheFactory.php index c63a9bc9a64103d4be50d650d70f9d940b2d65a9..40d3a1effcdf94b34cd8f19902e89f323495395d 100644 --- a/module/VuFind/src/VuFind/Record/CacheFactory.php +++ b/module/VuFind/src/VuFind/Record/CacheFactory.php @@ -62,9 +62,9 @@ class CacheFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\RecordDriver\PluginManager'), - $container->get('VuFind\Config\PluginManager')->get('RecordCache'), - $container->get('VuFind\Db\Table\PluginManager')->get('Record') + $container->get(\VuFind\RecordDriver\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class)->get('RecordCache'), + $container->get(\VuFind\Db\Table\PluginManager::class)->get('Record') ); } } diff --git a/module/VuFind/src/VuFind/Record/FallbackLoader/PluginManager.php b/module/VuFind/src/VuFind/Record/FallbackLoader/PluginManager.php index eb9db19b3bb96506ef35aa943ef5f8ed5e94ee97..c453db96449c47c1fc5080e43c89de2faa782867 100644 --- a/module/VuFind/src/VuFind/Record/FallbackLoader/PluginManager.php +++ b/module/VuFind/src/VuFind/Record/FallbackLoader/PluginManager.php @@ -44,7 +44,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'summon' => 'VuFind\Record\FallbackLoader\Summon', + 'summon' => Summon::class, ]; /** @@ -53,8 +53,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Record\FallbackLoader\Summon' => - 'VuFind\Record\FallbackLoader\SummonFactory', + Summon::class => SummonFactory::class, ]; /** @@ -65,6 +64,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Record\FallbackLoader\FallbackLoaderInterface'; + return FallbackLoaderInterface::class; } } diff --git a/module/VuFind/src/VuFind/Record/FallbackLoader/SummonFactory.php b/module/VuFind/src/VuFind/Record/FallbackLoader/SummonFactory.php index c3d0d04930cd0577b56c03c0fc2722f4a12ef0dc..b954b68553b69ef8f599956086e5402659ff201e 100644 --- a/module/VuFind/src/VuFind/Record/FallbackLoader/SummonFactory.php +++ b/module/VuFind/src/VuFind/Record/FallbackLoader/SummonFactory.php @@ -61,9 +61,9 @@ class SummonFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $backendManager = $container->get('VuFind\Search\BackendManager'); + $backendManager = $container->get(\VuFind\Search\BackendManager::class); return new $requestedName( - $container->get('VuFind\Db\Table\PluginManager')->get('resource'), + $container->get(\VuFind\Db\Table\PluginManager::class)->get('resource'), $backendManager->get('Summon') ); } diff --git a/module/VuFind/src/VuFind/Record/Loader.php b/module/VuFind/src/VuFind/Record/Loader.php index 106588a3fceee6a8ae303af5d1d5428459b5fc97..dc752afe86c651a0ac88641dc71b109b0a7ea4b2 100644 --- a/module/VuFind/src/VuFind/Record/Loader.php +++ b/module/VuFind/src/VuFind/Record/Loader.php @@ -32,6 +32,8 @@ namespace VuFind\Record; use VuFind\Exception\RecordMissing as RecordMissingException; use VuFind\Record\FallbackLoader\PluginManager as FallbackLoader; use VuFind\RecordDriver\PluginManager as RecordFactory; +use VuFindSearch\Backend\Exception\BackendException; +use VuFindSearch\ParamBag; use VuFindSearch\Service as SearchService; /** @@ -97,16 +99,17 @@ class Loader implements \Zend\Log\LoggerAwareInterface /** * Given an ID and record source, load the requested record object. * - * @param string $id Record ID - * @param string $source Record source - * @param bool $tolerateMissing Should we load a "Missing" placeholder + * @param string $id Record ID + * @param string $source Record source + * @param bool $tolerateMissing Should we load a "Missing" placeholder * instead of throwing an exception if the record cannot be found? + * @param ParamBag $params Search backend parameters * * @throws \Exception * @return \VuFind\RecordDriver\AbstractBase */ public function load($id, $source = DEFAULT_SEARCH_BACKEND, - $tolerateMissing = false + $tolerateMissing = false, ParamBag $params = null ) { if (null !== $id && '' !== $id) { $results = []; @@ -116,8 +119,14 @@ class Loader implements \Zend\Log\LoggerAwareInterface $results = $this->recordCache->lookup($id, $source); } if (empty($results)) { - $results = $this->searchService->retrieve($source, $id) - ->getRecords(); + try { + $results = $this->searchService->retrieve($source, $id, $params) + ->getRecords(); + } catch (BackendException $e) { + if (!$tolerateMissing) { + throw $e; + } + } } if (empty($results) && null !== $this->recordCache && $this->recordCache->isFallback($source) @@ -128,6 +137,24 @@ class Loader implements \Zend\Log\LoggerAwareInterface if (!empty($results)) { return $results[0]; } + + if ($this->fallbackLoader + && $this->fallbackLoader->has($source) + ) { + try { + $fallbackRecords = $this->fallbackLoader->get($source) + ->load([$id]); + } catch (BackendException $e) { + if (!$tolerateMissing) { + throw $e; + } + $fallbackRecords = []; + } + + if (count($fallbackRecords) == 1) { + return $fallbackRecords[0]; + } + } } if ($tolerateMissing) { $record = $this->recordFactory->get('Missing'); @@ -144,17 +171,18 @@ class Loader implements \Zend\Log\LoggerAwareInterface * Given an array of IDs and a record source, load a batch of records for * that source. * - * @param array $ids Record IDs - * @param string $source Record source - * @param bool $tolerateBackendExceptions Whether to tolerate backend + * @param array $ids Record IDs + * @param string $source Record source + * @param bool $tolerateBackendExceptions Whether to tolerate backend * exceptions that may be caused by e.g. connection issues or changes in * subcscriptions + * @param ParamBag $params Search backend parameters * * @throws \Exception * @return array */ public function loadBatchForSource($ids, $source = DEFAULT_SEARCH_BACKEND, - $tolerateBackendExceptions = false + $tolerateBackendExceptions = false, ParamBag $params = null ) { $list = new Checklist($ids); $cachedRecords = []; @@ -172,8 +200,9 @@ class Loader implements \Zend\Log\LoggerAwareInterface if ($list->hasUnchecked()) { try { $genuineRecords = $this->searchService - ->retrieveBatch($source, $list->getUnchecked())->getRecords(); - } catch (\VuFindSearch\Backend\Exception\BackendException $e) { + ->retrieveBatch($source, $list->getUnchecked(), $params) + ->getRecords(); + } catch (BackendException $e) { if (!$tolerateBackendExceptions) { throw $e; } @@ -192,8 +221,19 @@ class Loader implements \Zend\Log\LoggerAwareInterface if ($list->hasUnchecked() && $this->fallbackLoader && $this->fallbackLoader->has($source) ) { - $fallbackRecords = $this->fallbackLoader->get($source) - ->load($list->getUnchecked()); + try { + $fallbackRecords = $this->fallbackLoader->get($source) + ->load($list->getUnchecked()); + } catch (BackendException $e) { + if (!$tolerateBackendExceptions) { + throw $e; + } + $fallbackRecords = []; + $this->logWarning( + 'Exception when trying to retrieve fallback records from ' + . $source . ': ' . $e->getMessage() + ); + } foreach ($fallbackRecords as $record) { $retVal[] = $record; if (!$list->check($record->getUniqueId())) { @@ -241,28 +281,32 @@ class Loader implements \Zend\Log\LoggerAwareInterface * separated source|id strings), load all of the requested records in the * requested order. * - * @param array $ids Array of associative arrays with + * @param array $ids Array of associative arrays with * id/source keys or strings in source|id format. In associative array formats, * there is also an optional "extra_fields" key which can be used to pass in data * formatted as if it belongs to the Solr schema; this is used to create * a mock driver object if the real data source is unavailable. - * @param bool $tolerateBackendExceptions Whether to tolerate backend + * @param bool $tolerateBackendExceptions Whether to tolerate backend * exceptions that may be caused by e.g. connection issues or changes in * subcscriptions + * @param ParamBag[] $params Associative array of search + * backend parameters keyed with source key * * @throws \Exception * @return array Array of record drivers */ - public function loadBatch($ids, $tolerateBackendExceptions = false) - { + public function loadBatch( + $ids, $tolerateBackendExceptions = false, $params = [] + ) { // Create a SourceAndIdList object to help sort the IDs by source: $list = new SourceAndIdList($ids); // Retrieve the records and put them back in order: $retVal = []; foreach ($list->getIdsBySource() as $source => $currentIds) { + $sourceParams = $params[$source] ?? null; $records = $this->loadBatchForSource( - $currentIds, $source, $tolerateBackendExceptions + $currentIds, $source, $tolerateBackendExceptions, $sourceParams ); foreach ($records as $current) { $position = $list->getRecordPosition($current); diff --git a/module/VuFind/src/VuFind/Record/LoaderFactory.php b/module/VuFind/src/VuFind/Record/LoaderFactory.php index 8adb957982c8644f1e96cd00f564b0b2063e1088..3dd2c87003eae3026c6d5a63f334ef3e2e34753f 100644 --- a/module/VuFind/src/VuFind/Record/LoaderFactory.php +++ b/module/VuFind/src/VuFind/Record/LoaderFactory.php @@ -62,10 +62,10 @@ class LoaderFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFindSearch\Service'), - $container->get('VuFind\RecordDriver\PluginManager'), - $container->get('VuFind\Record\Cache'), - $container->get('VuFind\Record\FallbackLoader\PluginManager') + $container->get(\VuFindSearch\Service::class), + $container->get(\VuFind\RecordDriver\PluginManager::class), + $container->get(\VuFind\Record\Cache::class), + $container->get(\VuFind\Record\FallbackLoader\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/Record/Router.php b/module/VuFind/src/VuFind/Record/Router.php index 8102c57459c7d1598690616f31b90515100c6be9..ecd986213dbe77cbd7daa231afb09c89c2992278 100644 --- a/module/VuFind/src/VuFind/Record/Router.php +++ b/module/VuFind/src/VuFind/Record/Router.php @@ -38,13 +38,6 @@ namespace VuFind\Record; */ class Router { - /** - * Record loader - * - * @var \VuFind\Record\Loader - */ - protected $loader; - /** * VuFind configuration * @@ -55,13 +48,10 @@ class Router /** * Constructor * - * @param \VuFind\Record\Loader $loader Record loader - * @param \Zend\Config\Config $config VuFind configuration + * @param \Zend\Config\Config $config VuFind configuration */ - public function __construct(\VuFind\Record\Loader $loader, - \Zend\Config\Config $config - ) { - $this->loader = $loader; + public function __construct(\Zend\Config\Config $config) + { $this->config = $config; } @@ -85,28 +75,41 @@ class Router * @param \VuFind\RecordDriver\AbstractBase|string $driver Record driver * representing record to link to, or source|id pipe-delimited string * @param string $tab Action to access + * @param array $query Optional query params * * @return array */ - public function getTabRouteDetails($driver, $tab = null) + public function getTabRouteDetails($driver, $tab = null, $query = []) { $route = $this->getRouteDetails( $driver, '', empty($tab) ? [] : ['tab' => $tab] ); + // Add the options and query elements only if we need a query to avoid + // an empty element in the route definition: + if ($query) { + $route['options']['query'] = $query; + } // If collections are active and the record route was selected, we need // to check if the driver is actually a collection; if so, we should switch // routes. - if ('record' == $route['route']) { - if (isset($this->config->Collections->collections) - && $this->config->Collections->collections - ) { + if ($this->config->Collections->collections ?? false) { + $routeConfig = isset($this->config->Collections->route) + ? $this->config->Collections->route->toArray() : []; + $collectionRoutes + = array_merge( + ['record' => 'collection', + 'search2record' => 'search2collection'], + $routeConfig + ); + $routeName = $route['route']; + if ($collectionRoute = ($collectionRoutes[$routeName] ?? null)) { if (!is_object($driver)) { - list($source, $id) = $this->extractSourceAndId($driver); - $driver = $this->loader->load($id, $source); - } - if (true === $driver->tryMethod('isCollection')) { - $route['route'] = 'collection'; + // Avoid loading the driver. Set a flag so that if the link is + // used, record controller will check for redirection. + $route['options']['query']['checkRoute'] = 1; + } elseif (true === $driver->tryMethod('isCollection')) { + $route['route'] = $collectionRoute; } } } @@ -145,8 +148,16 @@ class Router $routeBase = ($source == DEFAULT_SEARCH_BACKEND) ? 'record' : strtolower($source . 'record'); + // Disable path normalization since it can unencode e.g. encoded slashes in + // record id's + $options = [ + 'normalize_path' => false + ]; + return [ - 'params' => $params, 'route' => $routeBase . $routeSuffix + 'params' => $params, + 'route' => $routeBase . $routeSuffix, + 'options' => $options ]; } diff --git a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php index cd0d9da2f0799937ec91f636a67922c9bc937dba..c834ad362c7d69741457961ff45938829d04b961 100644 --- a/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php +++ b/module/VuFind/src/VuFind/RecordDriver/AbstractBase.php @@ -262,18 +262,6 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface, ); } - /** - * Get the source value used to identify resources of this type in the database. - * - * @return string - * - * @deprecated Obsolete as of VuFind 3.0; use getSourceIdentifier() instead. - */ - public function getResourceSource() - { - return $this->getSourceIdentifier(); - } - /** * Set the source backend identifier. * @@ -346,25 +334,20 @@ abstract class AbstractBase implements \VuFind\Db\Table\DbTableAwareInterface, */ public function getCitationFormats() { + $formatSetting = $this->mainConfig->Record->citation_formats ?? true; + // Default behavior: use all supported options. - if (!isset($this->mainConfig->Record->citation_formats) - || $this->mainConfig->Record->citation_formats === true - || $this->mainConfig->Record->citation_formats === 'true' - ) { + if ($formatSetting === true || $formatSetting === 'true') { return $this->getSupportedCitationFormats(); } // Citations disabled: - if ($this->mainConfig->Record->citation_formats === false - || $this->mainConfig->Record->citation_formats === 'false' - ) { + if ($formatSetting === false || $formatSetting === 'false') { return []; } // Whitelist: - $whitelist = array_map( - 'trim', explode(',', $this->mainConfig->Record->citation_formats) - ); + $whitelist = array_map('trim', explode(',', $formatSetting)); return array_intersect($whitelist, $this->getSupportedCitationFormats()); } diff --git a/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php b/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php index da058162ec3194c91b2b9a19785b09da16ed6f2d..7b3fb3b8e901023b3653cd59abf4175b770e5fb1 100644 --- a/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/AbstractBaseFactory.php @@ -58,7 +58,8 @@ class AbstractBaseFactory implements FactoryInterface public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $mainConfig = $container->get('VuFind\Config\PluginManager')->get('config'); + $mainConfig = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($mainConfig, ...($options ?: [])); } } diff --git a/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php b/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php index 716647b2217944880744fb0468e47bc7bce28f10..ddd3a3e842c31f75d68670c0c464969dcfab415f 100644 --- a/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php +++ b/module/VuFind/src/VuFind/RecordDriver/DefaultRecord.php @@ -321,6 +321,27 @@ class DefaultRecord extends AbstractBase return empty($nums) ? false : $nums[0]; } + /** + * Get just the first listed national bibliography number (or false if none + * available). + * + * @return mixed + */ + public function getCleanNBN() + { + return false; + } + + /** + * Get just the base portion of the first listed ISMN (or false if no ISSMs). + * + * @return mixed + */ + public function getCleanISMN() + { + return false; + } + /** * Get the main corporate authors (if any) for the record. * @@ -829,13 +850,14 @@ class DefaultRecord extends AbstractBase // of databases if they do not cover the exact date provided! unset($params['rft.date']); - // If we're working with the SFX resolver, we should add a + // If we're working with the SFX or Alma resolver, we should add a // special parameter to ensure that electronic holdings links // are shown even though no specific date or issue is specified: - if (isset($this->mainConfig->OpenURL->resolver) - && strtolower($this->mainConfig->OpenURL->resolver) == 'sfx' - ) { + $resolver = strtolower($this->mainConfig->OpenURL->resolver ?? ''); + if ('sfx' === $resolver) { $params['sfx.ignore_date_threshold'] = 1; + } elseif ('alma' === $resolver) { + $params['u.ignore_date_coverage'] = 'true'; } return $params; } @@ -1229,10 +1251,17 @@ class DefaultRecord extends AbstractBase if ($upc = $this->getCleanUPC()) { $arr['upc'] = $upc; } + if ($nbn = $this->getCleanNBN()) { + $arr['nbn'] = $nbn['nbn']; + } + if ($ismn = $this->getCleanISMN()) { + $arr['ismn'] = $ismn; + } + // If an ILS driver has injected extra details, check for IDs in there // to fill gaps: if ($ilsDetails = $this->getExtraDetail('ils_details')) { - foreach (['isbn', 'issn', 'oclc', 'upc'] as $key) { + foreach (['isbn', 'issn', 'oclc', 'upc', 'nbn', 'ismn'] as $key) { if (!isset($arr[$key]) && isset($ilsDetails[$key])) { $arr[$key] = $ilsDetails[$key]; } diff --git a/module/VuFind/src/VuFind/RecordDriver/EDS.php b/module/VuFind/src/VuFind/RecordDriver/EDS.php index bd3b2e9a7b0369cbca5e99fda48dd44d66151f6c..eba329676a45f93fea5a3c497572438a06fa68f5 100644 --- a/module/VuFind/src/VuFind/RecordDriver/EDS.php +++ b/module/VuFind/src/VuFind/RecordDriver/EDS.php @@ -535,7 +535,7 @@ class EDS extends DefaultRecord { $groupsToReplace = ['au','su']; if (in_array($group, $groupsToReplace)) { - $br = '/<br \/>/'; + $br = '/<br \/>/'; $comma = ', '; return preg_replace($br, $comma, $data); } @@ -569,4 +569,21 @@ class EDS extends DefaultRecord // EDS is not export-friendly; disable all formats. return true; } + + /** + * Return the first valid DOI found in the record (false if none). + * + * @return mixed + */ + public function getCleanDOI() + { + if (isset($this->fields['Items'])) { + foreach ($this->fields['Items'] as $item) { + if ('DOI' == $item['Name']) { + return $item['Data']; + } + } + } + return false; + } } diff --git a/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php b/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php index 2804c99cde64443a1a26f19ef442c6f3a31eb577..b04387b817c527816fb3602ceb1d7977e1251747 100644 --- a/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php +++ b/module/VuFind/src/VuFind/RecordDriver/HierarchyAwareTrait.php @@ -188,6 +188,7 @@ trait HierarchyAwareTrait case 'Top': return isset($this->fields['is_hierarchy_title']) && isset($this->fields['is_hierarchy_id']) + && isset($this->fields['hierarchy_top_id']) && in_array( $this->fields['is_hierarchy_id'], $this->fields['hierarchy_top_id'] diff --git a/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php b/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php index de9a28fca9b2ae089d4ce095e0f0bf3aab004e91..32e92d25abac5c4d15fcfe6a84b0ce17a034cc0f 100644 --- a/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/IlsAwareDelegatorFactory.php @@ -60,11 +60,11 @@ class IlsAwareDelegatorFactory implements DelegatorFactoryInterface // Attach the ILS if at least one backend supports it: $ilsBackends = $this->getIlsBackends($container); - if (!empty($ilsBackends) && $container->has('VuFind\ILS\Connection')) { + if (!empty($ilsBackends) && $container->has(\VuFind\ILS\Connection::class)) { $driver->attachILS( - $container->get('VuFind\ILS\Connection'), - $container->get('VuFind\ILS\Logic\Holds'), - $container->get('VuFind\ILS\Logic\TitleHolds') + $container->get(\VuFind\ILS\Connection::class), + $container->get(\VuFind\ILS\Logic\Holds::class), + $container->get(\VuFind\ILS\Logic\TitleHolds::class) ); $driver->setIlsBackends($ilsBackends); } @@ -84,7 +84,8 @@ class IlsAwareDelegatorFactory implements DelegatorFactoryInterface // Get a list of ILS-compatible backends. static $ilsBackends = null; if (!is_array($ilsBackends)) { - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $settings = isset($config->Catalog) ? $config->Catalog->toArray() : []; // If the setting is missing, default to the default backend; if it diff --git a/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php index a470005933dbc05a523d39888aef80c78642a74f..0efebdddf1dbf9e2deb354d83ac4dd728001b387 100644 --- a/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php +++ b/module/VuFind/src/VuFind/RecordDriver/MarcAdvancedTrait.php @@ -196,6 +196,8 @@ trait MarcAdvancedTrait return "Collection"; case 'D': // Collection Part return "CollectionPart"; + case 'I': // Integrating Resource + return "IntegratingResource"; default: return "Unknown"; } @@ -903,4 +905,47 @@ trait MarcAdvancedTrait { return $this->getFieldArray('035', 'a', true); } + + /** + * Return first ISMN found for this record, or false if no one fonund + * + * @return mixed + */ + public function getCleanISMN() + { + $fields024 = $this->getMarcRecord()->getFields('024'); + $ismn = null; + foreach ($fields024 as $field) { + if ($field->getIndicator(1) == 2 + && $subfield = $field->getSubfield('a') + ) { + $ismn = $subfield->getData(); + break; + } + } + return $ismn ?? false; + } + + /** + * Return first national bibliography number found, or false if not found + * + * @return mixed + */ + public function getCleanNBN() + { + $field = $this->getMarcRecord()->getField('015'); + $nbn = false; + if ($field) { + $subfields = $this->getSubfieldArray($field, ['a', '7'], false); + if (!empty($subfields)) { + $nbn = [ + 'nbn' => $subfields[0], + ]; + if (isset($subfields[1])) { + $nbn['source'] = $subfields[1]; + } + } + } + return $nbn; + } } diff --git a/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php b/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php index 845b40741d5c8d51ccf4db8591d09c7c265b5b06..f69c90d98c2dc93525113436c34f5c4c523248ea 100644 --- a/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/NameBasedConfigFactory.php @@ -62,7 +62,8 @@ class NameBasedConfigFactory extends AbstractBaseFactory } $parts = explode('\\', $requestedName); $configName = array_pop($parts); - $config = $container->get('VuFind\Config\PluginManager')->get($configName); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get($configName); $finalOptions = [$config, $config]; return parent::__invoke($container, $requestedName, $finalOptions); } diff --git a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php index deb4d000bb714ac016c4727874a8622ea135baff..d623a7edd0706142218bccf6f98c5b2ebe4c7a5f 100644 --- a/module/VuFind/src/VuFind/RecordDriver/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordDriver/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\RecordDriver; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Record driver plugin manager * @@ -44,23 +46,25 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'browzine' => 'VuFind\RecordDriver\BrowZine', - 'eds' => 'VuFind\RecordDriver\EDS', - 'eit' => 'VuFind\RecordDriver\EIT', - 'libguides' => 'VuFind\RecordDriver\LibGuides', - 'missing' => 'VuFind\RecordDriver\Missing', - 'pazpar2' => 'VuFind\RecordDriver\Pazpar2', - 'primo' => 'VuFind\RecordDriver\Primo', - 'solrauth' => 'VuFind\RecordDriver\SolrAuthMarc', // legacy name - 'solrauthdefault' => 'VuFind\RecordDriver\SolrAuthDefault', - 'solrauthmarc' => 'VuFind\RecordDriver\SolrAuthMarc', - 'solrdefault' => 'VuFind\RecordDriver\SolrDefault', - 'solrmarc' => 'VuFind\RecordDriver\SolrMarc', - 'solrmarcremote' => 'VuFind\RecordDriver\SolrMarcRemote', - 'solrreserves' => 'VuFind\RecordDriver\SolrReserves', - 'solrweb' => 'VuFind\RecordDriver\SolrWeb', - 'summon' => 'VuFind\RecordDriver\Summon', - 'worldcat' => 'VuFind\RecordDriver\WorldCat', + 'browzine' => BrowZine::class, + 'eds' => EDS::class, + 'eit' => EIT::class, + 'libguides' => LibGuides::class, + 'missing' => Missing::class, + 'pazpar2' => Pazpar2::class, + 'primo' => Primo::class, + 'search2default' => Search2Default::class, + 'solrauth' => SolrAuthMarc::class, // legacy name + 'solrauthdefault' => SolrAuthDefault::class, + 'solrauthmarc' => SolrAuthMarc::class, + 'solrdefault' => SolrDefault::class, + 'solrmarc' => SolrMarc::class, + 'solrmarcremote' => SolrMarcRemote::class, + 'solroverdrive' => SolrOverdrive::class, + 'solrreserves' => SolrReserves::class, + 'solrweb' => SolrWeb::class, + 'summon' => Summon::class, + 'worldcat' => WorldCat::class, ]; /** @@ -69,10 +73,8 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var string[][]|\Zend\ServiceManager\Factory\DelegatorFactoryInterface[][] */ protected $delegators = [ - 'VuFind\RecordDriver\SolrMarc' => - ['VuFind\RecordDriver\IlsAwareDelegatorFactory'], - 'VuFind\RecordDriver\SolrMarcRemote' => - ['VuFind\RecordDriver\IlsAwareDelegatorFactory'], + SolrMarc::class => [IlsAwareDelegatorFactory::class], + SolrMarcRemote::class => [IlsAwareDelegatorFactory::class], ]; /** @@ -81,31 +83,24 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\RecordDriver\BrowZine' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\RecordDriver\EDS' => 'VuFind\RecordDriver\NameBasedConfigFactory', - 'VuFind\RecordDriver\EIT' => 'VuFind\RecordDriver\NameBasedConfigFactory', - 'VuFind\RecordDriver\LibGuides' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\RecordDriver\Missing' => 'VuFind\RecordDriver\AbstractBaseFactory', - 'VuFind\RecordDriver\Pazpar2' => - 'VuFind\RecordDriver\NameBasedConfigFactory', - 'VuFind\RecordDriver\Primo' => 'VuFind\RecordDriver\NameBasedConfigFactory', - 'VuFind\RecordDriver\SolrAuthDefault' => - 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', - 'VuFind\RecordDriver\SolrAuthMarc' => - 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', - 'VuFind\RecordDriver\SolrDefault' => - 'VuFind\RecordDriver\SolrDefaultFactory', - 'VuFind\RecordDriver\SolrMarc' => 'VuFind\RecordDriver\SolrDefaultFactory', - 'VuFind\RecordDriver\SolrMarcRemote' => - 'VuFind\RecordDriver\SolrDefaultFactory', - 'VuFind\RecordDriver\SolrReserves' => - 'VuFind\RecordDriver\SolrDefaultWithoutSearchServiceFactory', - 'VuFind\RecordDriver\SolrWeb' => 'VuFind\RecordDriver\SolrWebFactory', - 'VuFind\RecordDriver\Summon' => 'VuFind\RecordDriver\SummonFactory', - 'VuFind\RecordDriver\WorldCat' => - 'VuFind\RecordDriver\NameBasedConfigFactory', + BrowZine::class => InvokableFactory::class, + EDS::class => NameBasedConfigFactory::class, + EIT::class => NameBasedConfigFactory::class, + LibGuides::class => InvokableFactory::class, + Missing::class => AbstractBaseFactory::class, + Pazpar2::class => NameBasedConfigFactory::class, + Primo::class => NameBasedConfigFactory::class, + Search2Default::class => SolrDefaultFactory::class, + SolrAuthDefault::class => SolrDefaultWithoutSearchServiceFactory::class, + SolrAuthMarc::class => SolrDefaultWithoutSearchServiceFactory::class, + SolrDefault::class => SolrDefaultFactory::class, + SolrMarc::class => SolrDefaultFactory::class, + SolrMarcRemote::class => SolrDefaultFactory::class, + SolrOverdrive::class => SolrOverdriveFactory::class, + SolrReserves::class => SolrDefaultWithoutSearchServiceFactory::class, + SolrWeb::class => SolrWebFactory::class, + Summon::class => SummonFactory::class, + WorldCat::class => NameBasedConfigFactory::class, ]; /** @@ -124,7 +119,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager // we are building a brand new object. $this->sharedByDefault = false; - $this->addAbstractFactory('VuFind\RecordDriver\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); @@ -134,9 +129,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager if ($hasHierarchyType && is_callable([$instance, 'setHierarchyDriverManager']) ) { - if ($sm && $sm->has('VuFind\Hierarchy\Driver\PluginManager')) { + if ($sm && $sm->has(\VuFind\Hierarchy\Driver\PluginManager::class)) { $instance->setHierarchyDriverManager( - $sm->get('VuFind\Hierarchy\Driver\PluginManager') + $sm->get(\VuFind\Hierarchy\Driver\PluginManager::class) ); } } @@ -152,7 +147,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\RecordDriver\AbstractBase'; + return AbstractBase::class; } /** @@ -178,6 +173,19 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager return $driver; } + /** + * Convenience method to retrieve a populated Search2 record driver. + * + * @param array $data Raw Solr data + * @param string $defaultKeySuffix Default key suffix + * + * @return AbstractBase + */ + public function getSearch2Record($data, $defaultKeySuffix = 'Default') + { + return $this->getSolrRecord($data, 'Search2', $defaultKeySuffix); + } + /** * Convenience method to retrieve a populated Solr authority record driver. * diff --git a/module/VuFind/src/VuFind/RecordDriver/Search2Default.php b/module/VuFind/src/VuFind/RecordDriver/Search2Default.php new file mode 100644 index 0000000000000000000000000000000000000000..1db3bf0fc8fa3ca4b8d46e64daea98f370e7a998 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/Search2Default.php @@ -0,0 +1,65 @@ +<?php +/** + * Default model for Search2 records -- used when a more specific model based on + * the record_format field cannot be found. + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +/** + * Default model for Search2 records -- used when a more specific model based on + * the record_format field cannot be found. + * + * This should be used as the base class for all Solr-based record models. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + * + * @SuppressWarnings(PHPMD.ExcessivePublicCount) + */ +class Search2Default extends SolrDefault +{ + /** + * Used for identifying search backends + * + * @var string + */ + protected $sourceIdentifier = 'Search2'; + + /** + * Get the Hierarchy Type (false if none) + * + * @return string|bool + */ + public function getHierarchyType() + { + return parent::getHierarchyType() ? 'search2' : false; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php index 8ac6607393a41011dfc47771b22285edcad1e49c..ab5c367796b72107382bb7a293eec96df93c4d44 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php @@ -1,7 +1,7 @@ <?php /** * Default model for Solr records -- used when a more specific model based on - * the recordtype field cannot be found. + * the record_format field cannot be found. * * PHP version 7 * @@ -30,7 +30,7 @@ namespace VuFind\RecordDriver; /** * Default model for Solr records -- used when a more specific model based on - * the recordtype field cannot be found. + * the record_format field cannot be found. * * This should be used as the base class for all Solr-based record models. * @@ -135,6 +135,16 @@ class SolrDefault extends DefaultRecord parent::__construct($mainConfig, $recordConfig, $searchSettings); } + /** + * Get the date this record was first indexed (if set). + * + * @return string + */ + public function getFirstIndexed() + { + return $this->fields['first_indexed'] ?? ''; + } + /** * Get highlighting details from the object. * diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php index 80dca9410831c0abdedfc7c830672f5456743932..ddb58cb3a225b3e07aa7224b9728252b68e5357c 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultFactory.php @@ -58,7 +58,7 @@ class SolrDefaultFactory extends SolrDefaultWithoutSearchServiceFactory array $options = null ) { $driver = parent::__invoke($container, $requestedName, $options); - $driver->attachSearchService($container->get('VuFindSearch\Service')); + $driver->attachSearchService($container->get(\VuFindSearch\Service::class)); return $driver; } } diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php index c24019b7dab7ba03784b5b4b60dd0492386cdbe1..efa45299551996a69c90b91a7a6329a0fe3a0107 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefaultWithoutSearchServiceFactory.php @@ -60,7 +60,8 @@ class SolrDefaultWithoutSearchServiceFactory extends AbstractBaseFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('searches'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('searches'); $finalOptions = [null, $config]; return parent::__invoke($container, $requestedName, $finalOptions); } diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrOverdrive.php b/module/VuFind/src/VuFind/RecordDriver/SolrOverdrive.php new file mode 100644 index 0000000000000000000000000000000000000000..ecb21505f0141d8869484aaa3c1947f83799c3cd --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrOverdrive.php @@ -0,0 +1,665 @@ +<?php +/** + * VuFind Record Driver for SolrOverdrive Records + * + * PHP version 5 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +namespace VuFind\RecordDriver; + +use VuFind\DigitalContent\OverdriveConnector; +use Zend\Config\Config; +use Zend\Log\LoggerAwareInterface; + +/** + * VuFind Record Driver for SolrOverdrive Records + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development:plugins:record_drivers Wiki + */ +class SolrOverdrive extends SolrMarc implements LoggerAwareInterface +{ + use \VuFind\Log\LoggerAwareTrait { + logError as error; + } + + /** + * Overdrive Connector + * + * @var OverdriveConnector $connector Overdrive Connector + */ + protected $connector; + + /** + * Overdrive Configuration Object + * + * @var object + */ + protected $config; + + /** + * Constructor + * + * @param Config $mainConfig VuFind main configuration + * @param Config $recordConfig Record-specific configuration + * @param OverdriveConnector $connector Overdrive Connector + */ + public function __construct( + Config $mainConfig = null, $recordConfig = null, + OverdriveConnector $connector = null + ) { + $this->connector = $connector; + $this->config = $connector->getConfig(); + parent::__construct($mainConfig, $recordConfig, null); + + $this->debug("SolrOverdrive Rec Driver constructed"); + } + + /** + * Supports OpenURL + * + * @return bool + */ + public function supportsOpenUrl() + { + return false; + } + + /** + * Supports coins OpenURL + * + * @return bool + */ + public function supportsCoinsOpenUrl() + { + return false; + } + + /** + * Get Available Digital Formats + * + * Return the digital download formats that are available for linking to. + * + * @return array + * @throws \Exception + */ + public function getAvailableDigitalFormats() + { + $formats = []; + $formatNames = $this->connector->getFormatNames(); + $od_id = $this->getOverdriveID(); + + if ($checkout = $this->connector->getCheckout($od_id, false)) { + //if we are already locked in, then we need free ones and locked in ones. + if ($checkout->isFormatLockedIn) { + foreach ($checkout->formats as $format) { + $formatType = $format->formatType; + $formats[$formatType] = $formatNames[$formatType]; + } + //if we aren't locked in, we can show all formats + } else { + foreach ($this->getDigitalFormats() as $format) { + $formats[$format->id] = $formatNames[$format->id]; + } + } + } + return $formats; + } + + /** + * Get Formats + * + * Returns an array of digital formats for this resource. + * + * @return array Array of formats. + * @throws \Exception + */ + public function getDigitalFormats() + { + $formats = []; + $formatNames = $this->connector->getFormatNames(); + if ($this->config->isMarc) { + $od_id = $this->getOverdriveID(); + $fulldata = $this->connector->getMetadata([$od_id]); + $data = $fulldata[strtolower($od_id)]; + } else { + $jsonData = $this->fields['fullrecord']; + $data = json_decode($jsonData, false); + } + + foreach ($data->formats as $format) { + $format->name = $formatNames[$format->id]; + $formats[$format->id] = $format; + } + + return $formats; + } + + /** + * Get an array of all the formats associated with the record with metadata + * associated with it. This array is designed to be used in a template. + * The key for each entry is the translatable token for the format name + * + * @return array + * @throws \Exception + */ + public function getFormattedDigitalFormats() + { + $results = []; + foreach ($this->getDigitalFormats() as $key=>$format) { + $tmpresults = []; + if ($format->fileSize > 0) { + if ($format->fileSize > 1000000) { + $size = round($format->fileSize / 1000000); + $size .= " GB"; + } elseif ($format->fileSize > 1000) { + $size = round($format->fileSize / 1000); + $size .= " MB"; + } else { + $size = $format->fileSize; + $size .= " KB"; + } + $tmpresults["File Size"] = $size; + } + if ($format->partCount) { + $tmpresults["Parts"] = $format->partCount; + } + if ($format->identifiers) { + foreach ($format->identifiers as $id) { + if (in_array($id->type, ["ISBN", "ASIN"])) { + $tmpresults[$id->type] = $id->value; + } + } + } + if ($format->onSaleDate) { + $tmpresults["Release Date"] = $format->onSaleDate; + } + $results[$format->name] = $tmpresults; + } + + return $results; + } + + /** + * Returns links for showing previews + * + * @return array an array of links + * @throws \Exception + */ + public function getPreviewLinks() + { + $results = []; + if ($this->getIsMarc()) { + $od_id = $this->getOverdriveID(); + $fulldata = $this->connector->getMetadata([$od_id]); + $data = $fulldata[strtolower($od_id)]; + } else { + $jsonData = $this->fields['fullrecord']; + $data = json_decode($jsonData, false); + } + + if (isset($data->formats[0]->samples[0])) { + foreach ($data->formats[0]->samples as $format) { + if ($format->formatType == 'audiobook-overdrive' + || $format->formatType == 'ebook-overdrive' + ) { + $results = $format; + } + } + } + $this->debug("previewlinks:" . print_r($results, true)); + return $results; + } + + /** + * Returns true if the record supports real-time AJAX status lookups. + * + * @return bool + */ + public function supportsAjaxStatus() + { + //Future: add this as an overdrive configuration to turn it off + return true; + } + + /** + * Get Overdrive Access + * + * Pass-through to the connector to determine whether logged-in user + * has access to Overdrive actions + * + * @return boolean Whether the logged-in user has access to Overdrive. + */ + public function getOverdriveAccess() + { + return $this->connector->getAccess(); + } + + /** + * Is Logged in + * + * Returns whether the current user is logged in + * + * @return object|boolean User if logged in, false if not. + */ + public function isLoggedIn() + { + return $this->connector->getUser(); + } + + /** + * Get Overdrive ID + * + * Returns the Overdrive ID (or resource ID) for the current item. Note: for + * records in marc format, this may be different than the Solr Record ID + * + * @return string OverdriveID + * @throws \Exception + */ + public function getOverdriveID() + { + $result = 0; + + if ($this->config) { + if ($this->config->isMarc) { + $field = $this->config->idField; + $subfield = $this->confif->idSubfield; + $result = strtolower( + $this->getFieldArray($field, $subfield)[0] + ); + } else { + $result = strtolower($this->getUniqueID()); + } + } + $this->debug("odid: $result"); + return $result; + } + + /** + * Returns the availability for the current record + * + * @return object|bool returns an object with the info in it (see URL above) + * or false if there was a problem. + * @throws \Exception + */ + public function getOverdriveAvailability() + { + $overDriveId = $this->getOverdriveID(); + return $this->connector->getAvailability($overDriveId); + } + + /** + * Is Checked Out + * + * Is this resource already checked out to the user? + * + * @return object Returns the checkout information if currently checked out + * by this user or false if not. + * @throws \Exception + */ + public function isCheckedOut() + { + $this->debug(" ischeckout", [], true); + $overdriveID = $this->getOverdriveID(); + $result = $this->connector->getCheckouts(true); + if ($result->status) { + $checkedout = false; + $checkouts = $result->data; + foreach ($checkouts as $checkout) { + if (strtolower($checkout->reserveId) == $overdriveID) { + $checkedout = true; + $result->status = true; + $result->data = $checkout; + } + } + if (!$checkedout) { + $result->data = false; + } + } + //if it didn't work, an error should be logged from the connector + return $result; + } + + /** + * Is Held + * Checks to see if the current record is on hold through Overcdrive. + * + * @return object|bool Returns the hold info if on hold or false if not. + * @throws \Exception + */ + public function isHeld() + { + $overDriveId = $this->getOverdriveID(); + $result = $this->connector->getHolds(true); + if ($result->status) { + $holds = $result->data; + foreach ($holds as $hold) { + if (strtolower($hold->reserveId) == $overDriveId) { + return $hold; + } + } + } + //if it didn't work, an error should be logged from the connector + return false; + } + + /** + * Get Bread Crumb + * + * @return string + */ + public function getBreadcrumb() + { + if (!$this->getShortTitle()) { + return $this->getTitle(); + } else { + return $this->getShortTitle(); + } + } + + /** + * Get Marc Record + * + * Override the base marc trait to return a fake marc obj. + * + * @return \File_MARCBASE + * @throws \File_MARC_Exception + */ + public function getMarcRecord() + { + if ($this->getIsMarc()) { + return parent::getMarcRecord(); + } else { + //return new fake marc class + return new class { + /** + * Get the field + * + * @param string $f Fieldname + * + * @return string + */ + public function getField($f) + { + return ""; + } + + /** + * Get the fields + * + * @param array $f Fieldnames + * + * @return array + */ + public function getFields($f) + { + return []; + } + }; + } + } + + /** + * Get Subtitle + * + * @return string + */ + public function getSubtitle() + { + if ($this->getIsMarc()) { + return parent::getSubtitle(); + } else { + return $this->fields['title_sub']; + } + } + + /** + * Get Title Section + * + * @return string + */ + public function getTitleSection() + { + if ($this->getIsMarc()) { + return parent::getTitleSection(); + } else { + //I don't think overdrive has this metadata + return ""; + } + } + + /** + * Get Short Title + * + * @return string + */ + public function getShortTitle() + { + if ($this->getIsMarc()) { + return parent::getShortTitle(); + } else { + return $this->fields['title_short']; + } + } + + /** + * Get general notes on the record. + * + * @return array + */ + public function getGeneralNotes() + { + if ($this->config->isMarc) { + return parent::getGeneralNotes(); + } else { + return []; + } + } + + /** + * Returns one of three things: a full URL to a thumbnail preview of the + * record if an image is available in an external system; an array of + * parameters to send to VuFind's internal cover generator if no fixed URL + * exists; or false if no thumbnail can be generated. + * + * @param string $size Size of thumbnail (small, medium or large -- small + * is + * default). + * + * @return string|array|bool + * @throws \Exception + */ + public function getThumbnail( + $size = 'small' + ) { + if ($size == 'large') { + $cover = "cover300Wide"; + } elseif ($size == 'medium') { + $cover = "cover150Wide"; + } elseif ($size == 'small') { + $cover = 'thumbnail'; + } else { + $cover = "cover"; + } + + //if the record is marc then the cover links probably aren't there. + if ($this->config->isMarc) { + $od_id = $this->getOverdriveID(); + $fulldata = $this->connector->getMetadata([$od_id]); + $data = $fulldata[strtolower($od_id)]; + } else { + $result = false; + $jsonData = $this->fields['fullrecord']; + $data = json_decode($jsonData, false); + } + + if (isset($data->images)) { + if (isset($data->images->{$cover})) { + $result = $data->images->{$cover}->href; + } + } + return $result; + } + + /** + * Get an array of summary strings for the record. + * + * @return array + */ + public function getSummary() + { + if ($this->config->isMarc) { + return parent::getSummary(); + } else { + $desc = $this->fields["description"]; + + $newDesc = preg_replace("/’/i", "", $desc); + $newDesc = strip_tags($newDesc); + return ["Summary" => $newDesc]; + } + } + + /** + * Retrieve raw data from object (primarily for use in staff view and + * autocomplete; avoid using whenever possible). + * + * @return mixed + */ + public function getRawData() + { + if ($this->config->isMarc) { + return parent::getRawData(); + } else { + $jsonData = $this->fields['fullrecord']; + $data = json_decode($jsonData, true); + return $data; + } + } + + /** + * Is Marc Based Record + * + * Return whether this is a marc-based record. + * + * @return bool + */ + public function getIsMarc() + { + return $this->config->isMarc; + } + + /** + * Get all subject headings associated with this record. Each heading is + * returned as an array of chunks, increasing from least specific to most + * specific. + * + * @param bool $extended Whether to return a keyed array with the following + * keys: + * - heading: the actual subject heading chunks + * - type: heading type + * - source: source vocabulary + * + * @return array + */ + public function getAllSubjectHeadings($extended = false) + { + if ($this->config) { + if ($this->config->isMarc) { + return parent::getAllSubjectHeadings($extended); + } else { + $headings = []; + foreach (['topic', 'geographic', 'genre', 'era'] as $field) { + if (isset($this->fields[$field])) { + $headings = array_merge( + $headings, $this->fields[$field] + ); + } + } + + // The default index schema doesn't currently store subject + // headings in a + // broken-down format, so we'll just send each value as a + // single chunk. + // Other record drivers (i.e. SolrMarc) can offer this data + // in a more granular format. + $callback = function ($i) use ($extended) { + return $extended + ? ['heading' => [$i], 'type' => '', 'source' => ''] + : [$i]; + }; + return array_map($callback, array_unique($headings)); + } + } else { + return []; + } + } + + /** + * Get Formatted Raw Data + * + * Returns the raw data formatted for staff display tab + * + * @return array Multidimensional array with data + */ + public function getFormattedRawData() + { + $result = []; + $jsonData = $this->fields['fullrecord']; + $data = json_decode($jsonData, true); + $c_arr = []; + foreach ($data['creators'] as $creator) { + $c_arr[] = "<strong>{$creator["role"]}<strong>: " + . $creator["name"]; + } + $data['creators'] = implode("<br/>", $c_arr); + + $this->debug("raw data:" . print_r($data, true)); + return $data; + } + + /** + * 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() + { + $od_id = $this->getOverdriveID(); + $rec_id = $this->getUniqueID(); + $urlDetails = [ + 'action' => 'Hold', + 'record' => $rec_id, + 'query' => "od_id=$od_id&rec_id=$rec_id", + 'anchor' => '' + ]; + return $urlDetails; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrOverdriveFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrOverdriveFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..57b974f2a7201a4ab2bd7b7523f301364daa2937 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordDriver/SolrOverdriveFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for Overdrive record drivers. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordDriver; + +use Interop\Container\ContainerInterface; + +/** + * Factory for Overdrive record drivers. + * + * @category VuFind + * @package RecordDrivers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SolrOverdriveFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $odConfig = $container->get('VuFind\Config\PluginManager')->get('Overdrive'); + $connector = $container->get('VuFind\DigitalContent\OverdriveConnector'); + return new $requestedName($config, $odConfig, $connector); + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php b/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php index 91cef38e71b86d113dba3849fce5eceeda3a7d35..517e0dfd72595eb13fd0e3928cd8aad4cc5a7a8f 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrWebFactory.php @@ -60,7 +60,8 @@ class SolrWebFactory extends AbstractBaseFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('website'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('website'); $finalOptions = [$config, $config]; return parent::__invoke($container, $requestedName, $finalOptions); } diff --git a/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php b/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php index f39a757bca64a2ae17e9e24234f4379ae87e4884..33fcc8f636272d0a50d3ed59f0d267c3a4c08e85 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php +++ b/module/VuFind/src/VuFind/RecordDriver/SummonFactory.php @@ -58,7 +58,7 @@ class SummonFactory extends NameBasedConfigFactory array $options = null ) { $driver = parent::__invoke($container, $requestedName, $options); - $driver->setDateConverter($container->get('VuFind\Date\Converter')); + $driver->setDateConverter($container->get(\VuFind\Date\Converter::class)); return $driver; } } diff --git a/module/VuFind/src/VuFind/RecordTab/AbstractContentFactory.php b/module/VuFind/src/VuFind/RecordTab/AbstractContentFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..645c2f38e239d90bbc815852c4473b1e607c7454 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/AbstractContentFactory.php @@ -0,0 +1,105 @@ +<?php +/** + * Abstract factory for building AbstractContent tabs. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\Content\PluginManager as ContentManager; + +/** + * Abstract factory for building AbstractContent tabs. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +abstract class AbstractContentFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * The name of the tab being constructed. + * + * @var string + */ + protected $tabName; + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(ConfigManager::class)->get('config'); + // Only instantiate the loader if the feature is enabled: + $loader = isset($config->Content->{$this->tabName}) + ? $container->get(ContentManager::class)->get($this->tabName) + : null; + return new $requestedName($loader, $this->getHideSetting($config)); + } + + /** + * Support method for construction of AbstractContent objects -- should we + * hide this tab if it is empty? + * + * @param \Zend\Config\Config $config VuFind configuration + * + * @return bool + */ + protected function getHideSetting(\Zend\Config\Config $config) + { + $setting = $config->Content->hide_if_empty ?? false; + if ($setting === true || $setting === false + || $setting === 1 || $setting === 0 + ) { + return (bool)$setting; + } + if ($setting === 'true' || $setting === '1') { + return true; + } + $hide = array_map('trim', array_map('strtolower', explode(',', $setting))); + return in_array(strtolower($this->tabName), $hide); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTreeFactory.php b/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTreeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3a03cabe156d8a507336fdd1d70686944cd34dce --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/CollectionHierarchyTreeFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for building the CollectionHierarchyTree tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the CollectionHierarchyTree tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CollectionHierarchyTreeFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class)->get('config'), + $container->get(\VuFind\Record\Loader::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/CollectionList.php b/module/VuFind/src/VuFind/RecordTab/CollectionList.php index f01983acdd0acf2b1698c026565e09080da6133d..6656f0d73eedcc585186f56aa4f3e2bd90de267a 100644 --- a/module/VuFind/src/VuFind/RecordTab/CollectionList.php +++ b/module/VuFind/src/VuFind/RecordTab/CollectionList.php @@ -63,6 +63,13 @@ class CollectionList extends AbstractBase */ protected $recommendManager; + /** + * Search class id + * + * @var string + */ + protected $searchClassId = 'SolrCollection'; + /** * Constructor * @@ -116,21 +123,11 @@ class CollectionList extends AbstractBase $listener->attach($runner->getEventManager()->getSharedManager()); }; $this->results - = $this->runner->run($request, 'SolrCollection', $cb); + = $this->runner->run($request, $this->searchClassId, $cb); } return $this->results; } - /** - * Get side recommendations. - * - * @return array - */ - public function getSideRecommendations() - { - return $this->getResults()->getRecommendations('side'); - } - /** * Can this tab be loaded via AJAX? * @@ -138,7 +135,7 @@ class CollectionList extends AbstractBase */ public function supportsAjax() { - // No, special sidebar needed. + // No, search parameters from the URL are needed. return false; } } diff --git a/module/VuFind/src/VuFind/RecordTab/CollectionListFactory.php b/module/VuFind/src/VuFind/RecordTab/CollectionListFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a2f5df7b98ac22dc9a47f0c093f4cc7ee821a314 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/CollectionListFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for building the CollectionList tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the CollectionList tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class CollectionListFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Search\SearchRunner::class), + $container->get(\VuFind\Recommend\PluginManager::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/ComponentParts.php b/module/VuFind/src/VuFind/RecordTab/ComponentParts.php new file mode 100644 index 0000000000000000000000000000000000000000..876252d22a7ad28aeafc34ae138d31c9b0cff9ff --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/ComponentParts.php @@ -0,0 +1,133 @@ +<?php +/** + * Component parts display tab + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +/** + * Component parts display tab + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class ComponentParts extends AbstractBase +{ + /** + * Similar records + * + * @var array + */ + protected $results; + + /** + * Maximum results to display + * + * @var int + */ + protected $maxResults = 100; + + /** + * Search service + * + * @var \VuFindSearch\Service + */ + protected $searchService; + + /** + * Constructor + * + * @param \VuFindSearch\Service $search Search service + */ + public function __construct(\VuFindSearch\Service $search) + { + $this->searchService = $search; + } + + /** + * Get the on-screen description for this tab. + * + * @return string + */ + public function getDescription() + { + return 'child_records'; + } + + /** + * Is this tab active? + * + * @return bool + */ + public function isActive() + { + $children = $this->getRecordDriver()->tryMethod('getChildRecordCount'); + return $children !== null && $children > 0; + } + + /** + * Get the maximum result count. + * + * @return int + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Get the contents for display. + * + * @return array + */ + public function getResults() + { + $record = $this->getRecordDriver(); + $safeId = addcslashes($record->getUniqueId(), '"'); + $query = new \VuFindSearch\Query\Query( + 'hierarchy_parent_id:"' . $safeId . '"' + ); + $params = new \VuFindSearch\ParamBag( + [ + // Disable highlighting for efficiency; not needed here: + 'hl' => ['false'], + // Sort appropriately: + 'sort' => 'hierarchy_sequence ASC,title ASC', + ] + ); + return $this->searchService->search( + $record->getSourceIdentifier(), + $query, + 0, + // retrieve 1 more than max results, so we know when to + // display a "more" link: + $this->maxResults + 1, + $params + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/ComponentPartsFactory.php b/module/VuFind/src/VuFind/RecordTab/ComponentPartsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5dbc08b0610e86c9db44451e2d6e91ccaefa83f8 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/ComponentPartsFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for building the ComponentParts tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the ComponentParts tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ComponentPartsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(\VuFindSearch\Service::class)); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/ExcerptFactory.php b/module/VuFind/src/VuFind/RecordTab/ExcerptFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..da6a01ba6c7e34f8ff6a9598923a28797b827452 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/ExcerptFactory.php @@ -0,0 +1,47 @@ +<?php +/** + * Factory for building Excerpt tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +/** + * Factory for building Excerpt tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ExcerptFactory extends AbstractContentFactory +{ + /** + * The name of the tab being constructed. + * + * @var string + */ + protected $tabName = 'excerpts'; +} diff --git a/module/VuFind/src/VuFind/RecordTab/Factory.php b/module/VuFind/src/VuFind/RecordTab/Factory.php deleted file mode 100644 index 2725d068153a057dcb3f4d8809fe1c7c83af2260..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/RecordTab/Factory.php +++ /dev/null @@ -1,288 +0,0 @@ -<?php -/** - * Record Tab Factory Class - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package RecordDrivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\RecordTab; - -use Zend\ServiceManager\ServiceManager; - -/** - * Record Tab Factory Class - * - * @category VuFind - * @package RecordDrivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for CollectionHierarchyTree tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return CollectionHierarchyTree - */ - public static function getCollectionHierarchyTree(ServiceManager $sm) - { - return new CollectionHierarchyTree( - $sm->get('VuFind\Config\PluginManager')->get('config'), - $sm->get('VuFind\Record\Loader') - ); - } - - /** - * Factory for CollectionList tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return CollectionList - */ - public static function getCollectionList(ServiceManager $sm) - { - return new CollectionList( - $sm->get('VuFind\Search\SearchRunner'), - $sm->get('VuFind\Recommend\PluginManager') - ); - } - - /** - * Factory for Excerpt tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Excerpt - */ - public static function getExcerpt(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - // Only instantiate the loader if the feature is enabled: - if (isset($config->Content->excerpts)) { - $loader = $sm->get('VuFind\Content\PluginManager') - ->get('excerpts'); - } else { - $loader = null; - } - return new Excerpt($loader, static::getHideSetting($config, 'excerpts')); - } - - /** - * Support method for construction of AbstractContent objects -- should we - * hide this tab if it is empty? - * - * @param \Zend\Config\Config $config VuFind configuration - * @param string $tab Name of tab to check config for - * - * @return bool - */ - protected static function getHideSetting(\Zend\Config\Config $config, $tab) - { - // TODO: can we move this code out of the factory so it's more easily reused? - $setting = isset($config->Content->hide_if_empty) - ? $config->Content->hide_if_empty : false; - if ($setting === true || $setting === false - || $setting === 1 || $setting === 0 - ) { - return (bool)$setting; - } - if ($setting === 'true' || $setting === '1') { - return true; - } - $hide = array_map('trim', array_map('strtolower', explode(',', $setting))); - return in_array(strtolower($tab), $hide); - } - - /** - * Factory for HierarchyTree tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return HierarchyTree - */ - public static function getHierarchyTree(ServiceManager $sm) - { - return new HierarchyTree( - $sm->get('VuFind\Config\PluginManager')->get('config') - ); - } - - /** - * Factory for HoldingsILS tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return HoldingsILS - */ - public static function getHoldingsILS(ServiceManager $sm) - { - // If VuFind is configured to suppress the holdings tab when the - // ILS driver specifies no holdings, we need to pass in a connection - // object: - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $catalog = ($config->Site->hideHoldingsTabWhenEmpty ?? false) - ? $sm->get('VuFind\ILS\Connection') : null; - return new HoldingsILS( - $catalog, - (string)($config->Site->holdingsTemplate ?? 'standard') - ); - } - - /** - * Factory for HoldingsWorldCat tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return HoldingsWorldCat - */ - public static function getHoldingsWorldCat(ServiceManager $sm) - { - $bm = $sm->get('VuFind\Search\BackendManager'); - return new HoldingsWorldCat($bm->get('WorldCat')->getConnector()); - } - - /** - * Factory for Map tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Map - */ - public static function getMap(ServiceManager $sm) - { - // get Map Tab config options - $mapTabConfig = $sm->get('VuFind\GeoFeatures\MapTabConfig'); - $mapTabOptions = $mapTabConfig->getMapTabOptions(); - $mapTabDisplay = $mapTabOptions['recordMap']; - - // add basemap options - $basemapConfig = $sm->get('VuFind\GeoFeatures\BasemapConfig'); - $basemapOptions = $basemapConfig->getBasemap('MapTab'); - - return new Map($mapTabDisplay, $basemapOptions, $mapTabOptions); - } - - /** - * Factory for Preview tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Preview - */ - public static function getPreview(ServiceManager $sm) - { - $cfg = $sm->get('VuFind\Config\PluginManager')->get('config'); - // currently only active if config [content] [previews] contains google - // and googleoptions[tab] is not empty. - $active = false; - if (isset($cfg->Content->previews)) { - $previews = array_map( - 'trim', explode(',', strtolower($cfg->Content->previews)) - ); - if (in_array('google', $previews) - && isset($cfg->Content->GoogleOptions['tab']) - && strlen(trim($cfg->Content->GoogleOptions['tab'])) > 0 - ) { - $active = true; - } - } - return new Preview($active); - } - - /** - * Factory for SimilarItems tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return SimilarItemsCarousel - */ - public static function getSimilarItemsCarousel(ServiceManager $sm) - { - return new SimilarItemsCarousel($sm->get('VuFindSearch\Service')); - } - - /** - * Factory for Reviews tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return Reviews - */ - public static function getReviews(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - // Only instantiate the loader if the feature is enabled: - if (isset($config->Content->reviews)) { - $loader = $sm->get('VuFind\Content\PluginManager') - ->get('reviews'); - } else { - $loader = null; - } - return new Reviews($loader, static::getHideSetting($config, 'reviews')); - } - - /** - * Factory for TOC tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return TablesOfContents - */ - public static function getTOC(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - // Only instantiate the loader if the feature is enabled: - if (isset($config->Content->toc)) { - $loader = $sm->get('VuFind\Content\PluginManager') - ->get('toc'); - } else { - $loader = null; - } - return new TOC($loader, static::getHideSetting($config, 'toc')); - } - - /** - * Factory for UserComments tab plugin. - * - * @param ServiceManager $sm Service manager. - * - * @return UserComments - */ - public static function getUserComments(ServiceManager $sm) - { - $capabilities = $sm->get('VuFind\Config\AccountCapabilities'); - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $useRecaptcha = isset($config->Captcha) && isset($config->Captcha->forms) - && (trim($config->Captcha->forms) === '*' - || strpos($config->Captcha->forms, 'userComments') !== false); - return new UserComments( - 'enabled' === $capabilities->getCommentSetting(), - $useRecaptcha - ); - } -} diff --git a/module/VuFind/src/VuFind/RecordTab/Formats.php b/module/VuFind/src/VuFind/RecordTab/Formats.php new file mode 100644 index 0000000000000000000000000000000000000000..1e154443319765dddcc28ad13f4cd9be86b45775 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/Formats.php @@ -0,0 +1,79 @@ +<?php +/** + * Digital Content Formats tab + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +/** + * Digital Content Formats + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class Formats extends AbstractBase +{ + /** + * Is this tab enabled? + * + * @var bool + */ + protected $enabled; + + /** + * Constructor + * + * @param bool $enabled is this tab enabled? + * @param bool $urc use recaptcha? + */ + public function __construct($enabled = true, $urc = false) + { + $this->enabled = $enabled; + } + + /** + * Is this tab active? + * + * @return bool + */ + public function isActive() + { + return $this->enabled; + } + + /** + * Get the on-screen description for this tab. + * + * @return string + */ + public function getDescription() + { + return 'Formats'; + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/HierarchyTreeFactory.php b/module/VuFind/src/VuFind/RecordTab/HierarchyTreeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..998bfbac2297753bc2af9420f813cd06394705d4 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/HierarchyTreeFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for building the HierarchyTree tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the HierarchyTree tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HierarchyTreeFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class)->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php b/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php index fd760e0aaf60b704210a7ba1fa992a02c494d352..f0c6424a8a5431ac21814cff2922ac8aa6d8e3d3 100644 --- a/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php +++ b/module/VuFind/src/VuFind/RecordTab/HoldingsILS.php @@ -54,17 +54,29 @@ class HoldingsILS extends AbstractBase */ protected $template; + /** + * Whether the holdings tab should be hidden when empty or not. + * + * @var bool + */ + protected $hideWhenEmpty; + /** * Constructor * - * @param \VuFind\ILS\Connection|bool $catalog ILS connection to use to check - * for holdings before displaying the tab; set to null if no check is needed - * @param string $template Holdings template to use + * @param \VuFind\ILS\Connection|bool $catalog ILS connection to use to + * check for holdings before displaying the tab; may be set to null if no check + * is needed. + * @param string $template Holdings template to use + * @param bool $hideWhenEmpty Whether the + * holdings tab should be hidden when empty or not */ - public function __construct(Connection $catalog = null, $template = null) - { + public function __construct(Connection $catalog = null, $template = null, + $hideWhenEmpty = false + ) { $this->catalog = $catalog; $this->template = $template ?? 'standard'; + $this->hideWhenEmpty = $hideWhenEmpty; } /** @@ -104,10 +116,8 @@ class HoldingsILS extends AbstractBase */ public function isActive() { - if ($this->catalog) { - return $this->catalog->hasHoldings($this->driver->getUniqueID()); - } - return true; + return ($this->catalog && $this->hideWhenEmpty) + ? $this->catalog->hasHoldings($this->driver->getUniqueID()) : true; } /** @@ -119,4 +129,33 @@ class HoldingsILS extends AbstractBase { return $this->template; } + + /** + * Getting a paginator for the items list. + * + * @param int $totalItemCount Total count of items for a bib record + * @param int $page Currently selected page of the items paginator + * @param int $itemLimit Max. no of items per page + * + * @return \Zend\Paginator\Paginator + */ + public function getPaginator($totalItemCount, $page, $itemLimit) + { + // Return if a paginator is not needed or not supported ($itemLimit = null) + if (!$itemLimit || $totalItemCount < $itemLimit) { + return; + } + + // Create the paginator + $nullAdapter = new \Zend\Paginator\Adapter\NullFill($totalItemCount); + $paginator = new \Zend\Paginator\Paginator($nullAdapter); + + // Some settings for the paginator + $paginator + ->setCurrentPageNumber($page) + ->setItemCountPerPage($itemLimit) + ->setPageRange(10); + + return $paginator; + } } diff --git a/module/VuFind/src/VuFind/RecordTab/HoldingsILSFactory.php b/module/VuFind/src/VuFind/RecordTab/HoldingsILSFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8bee0526391cbe690eb8b65a463c6dce5dcc1451 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/HoldingsILSFactory.php @@ -0,0 +1,77 @@ +<?php +/** + * Factory for building the HoldingsILS tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the HoldingsILS tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HoldingsILSFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // If VuFind is configured to suppress the holdings tab when the + // ILS driver specifies no holdings, we need to pass in a connection + // object: + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $catalog = $container->get(\VuFind\ILS\Connection::class); + return new $requestedName( + $catalog, + (string)($config->Site->holdingsTemplate ?? 'standard'), + (string)($config->Site->hideHoldingsTabWhenEmpty ?? false) + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCatFactory.php b/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCatFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2552e4bc70d37ee8a5aefdd955a49827ece007a8 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/HoldingsWorldCatFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for building the HoldingsWorldCat tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the HoldingsWorldCat tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HoldingsWorldCatFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $bm = $container->get(\VuFind\Search\BackendManager::class); + return new $requestedName($bm->get('WorldCat')->getConnector()); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/MapFactory.php b/module/VuFind/src/VuFind/RecordTab/MapFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e3b5a5175f2cc56ac9b7e6d10c925467e0edcc22 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/MapFactory.php @@ -0,0 +1,76 @@ +<?php +/** + * Factory for building the Map tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the Map tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MapFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + // get Map Tab config options + $mapTabConfig = $container->get(\VuFind\GeoFeatures\MapTabConfig::class); + $mapTabOptions = $mapTabConfig->getMapTabOptions(); + $mapTabDisplay = $mapTabOptions['recordMap']; + + // add basemap options + $basemapConfig = $container->get(\VuFind\GeoFeatures\BasemapConfig::class); + $basemapOptions = $basemapConfig->getBasemap('MapTab'); + + return new $requestedName($mapTabDisplay, $basemapOptions, $mapTabOptions); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/PluginManager.php b/module/VuFind/src/VuFind/RecordTab/PluginManager.php index 99e674a4e0c3b3f88418bd2f2d21a32a37e52baf..dc4e6c52f666b83791138ca81300420cc0661d61 100644 --- a/module/VuFind/src/VuFind/RecordTab/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordTab/PluginManager.php @@ -27,7 +27,7 @@ */ namespace VuFind\RecordTab; -use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; +use Zend\ServiceManager\Factory\InvokableFactory; /** * Record tab plugin manager @@ -46,21 +46,25 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'collectionhierarchytree' => 'VuFind\RecordTab\CollectionHierarchyTree', - 'collectionlist' => 'VuFind\RecordTab\CollectionList', - 'description' => 'VuFind\RecordTab\Description', - 'excerpt' => 'VuFind\RecordTab\Excerpt', - 'hierarchytree' => 'VuFind\RecordTab\HierarchyTree', - 'holdingsils' => 'VuFind\RecordTab\HoldingsILS', - 'holdingsworldcat' => 'VuFind\RecordTab\HoldingsWorldCat', - 'map' => 'VuFind\RecordTab\Map', - 'preview' => 'VuFind\RecordTab\Preview', - 'reviews' => 'VuFind\RecordTab\Reviews', - 'similaritemscarousel' => 'VuFind\RecordTab\SimilarItemsCarousel', - 'staffviewarray' => 'VuFind\RecordTab\StaffViewArray', - 'staffviewmarc' => 'VuFind\RecordTab\StaffViewMARC', - 'toc' => 'VuFind\RecordTab\TOC', - 'usercomments' => 'VuFind\RecordTab\UserComments', + 'collectionhierarchytree' => CollectionHierarchyTree::class, + 'collectionlist' => CollectionList::class, + 'componentparts' => ComponentParts::class, + 'description' => Description::class, + 'excerpt' => Excerpt::class, + 'formats' => Formats::class, + 'hierarchytree' => HierarchyTree::class, + 'holdingsils' => HoldingsILS::class, + 'holdingsworldcat' => HoldingsWorldCat::class, + 'map' => Map::class, + 'preview' => Preview::class, + 'reviews' => Reviews::class, + 'search2collectionlist' => Search2CollectionList::class, + 'similaritemscarousel' => SimilarItemsCarousel::class, + 'staffviewarray' => StaffViewArray::class, + 'staffviewmarc' => StaffViewMARC::class, + 'staffviewoverdrive' => StaffViewOverdrive::class, + 'toc' => TOC::class, + 'usercomments' => UserComments::class, ]; /** @@ -69,30 +73,25 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\RecordTab\CollectionHierarchyTree' => - 'VuFind\RecordTab\Factory::getCollectionHierarchyTree', - 'VuFind\RecordTab\CollectionList' => - 'VuFind\RecordTab\Factory::getCollectionList', - 'VuFind\RecordTab\Description' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\RecordTab\Excerpt' => 'VuFind\RecordTab\Factory::getExcerpt', - 'VuFind\RecordTab\HierarchyTree' => - 'VuFind\RecordTab\Factory::getHierarchyTree', - 'VuFind\RecordTab\HoldingsILS' => 'VuFind\RecordTab\Factory::getHoldingsILS', - 'VuFind\RecordTab\HoldingsWorldCat' => - 'VuFind\RecordTab\Factory::getHoldingsWorldCat', - 'VuFind\RecordTab\Map' => 'VuFind\RecordTab\Factory::getMap', - 'VuFind\RecordTab\Preview' => 'VuFind\RecordTab\Factory::getPreview', - 'VuFind\RecordTab\Reviews' => 'VuFind\RecordTab\Factory::getReviews', - 'VuFind\RecordTab\SimilarItemsCarousel' => - 'VuFind\RecordTab\Factory::getSimilarItemsCarousel', - 'VuFind\RecordTab\StaffViewArray' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\RecordTab\StaffViewMARC' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\RecordTab\TOC' => 'VuFind\RecordTab\Factory::getTOC', - 'VuFind\RecordTab\UserComments' => - 'VuFind\RecordTab\Factory::getUserComments', + CollectionHierarchyTree::class => CollectionHierarchyTreeFactory::class, + CollectionList::class => CollectionListFactory::class, + ComponentParts::class => ComponentPartsFactory::class, + Description::class => InvokableFactory::class, + Excerpt::class => ExcerptFactory::class, + Formats::class => InvokableFactory::class, + HierarchyTree::class => HierarchyTreeFactory::class, + HoldingsILS::class => HoldingsILSFactory::class, + HoldingsWorldCat::class => HoldingsWorldCatFactory::class, + Map::class => MapFactory::class, + Preview::class => PreviewFactory::class, + Reviews::class => ReviewsFactory::class, + Search2CollectionList::class => CollectionListFactory::class, + SimilarItemsCarousel::class => SimilarItemsCarouselFactory::class, + StaffViewArray::class => InvokableFactory::class, + StaffViewMARC::class => InvokableFactory::class, + StaffViewOverdrive::class => InvokableFactory::class, + TOC::class => TOCFactory::class, + UserComments::class => UserCommentsFactory::class, ]; /** @@ -107,40 +106,11 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\RecordTab\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); $this->addInitializer('ZfcRbac\Initializer\AuthorizationServiceInitializer'); parent::__construct($configOrContainerInstance, $v3config); } - /** - * Load the specified key from the configuration array using the best - * available match to the class of the provided driver. Return the default - * value if no match is found. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param string $setting Key to load from configuration - * @param string $default Default to use if no setting found - * - * @return mixed - */ - protected function getConfigByClass(AbstractRecordDriver $driver, - array $config, $setting, $default - ) { - // Get the current record driver's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // setting. - $className = get_class($driver); - do { - if (isset($config[$className][$setting])) { - return $config[$className][$setting]; - } - } while ($className = get_parent_class($className)); - // No setting found... - return $default; - } - /** * Return the name of the base class or interface that plug-ins must conform * to. @@ -149,127 +119,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\RecordTab\TabInterface'; - } - - /** - * Get an array of service names by looking up the provided record driver in - * the provided tab configuration array. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (associative array - * including 'tabs' array mapping driver class => tab service name) - * - * @return array - */ - protected function getTabServiceNames(AbstractRecordDriver $driver, - array $config - ) { - return $this->getConfigByClass($driver, $config, 'tabs', []); - } - - /** - * Get an array of tabs names configured to load via AJAX in the background - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (associative array - * including 'tabs' array mapping driver class => tab service name) - * - * @return array - */ - public function getBackgroundTabNames(AbstractRecordDriver $driver, - array $config - ) { - return $this->getConfigByClass($driver, $config, 'backgroundLoadedTabs', []); - } - - /** - * Get a default tab by looking up the provided record driver in the tab - * configuration array. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param array $tabs Details on available tabs (returned - * from getTabsForRecord()). - * @param string $fallback Fallback to use if no tab specified - * or matched. - * - * @return string - */ - public function getDefaultTabForRecord(AbstractRecordDriver $driver, - array $config, array $tabs, $fallback = null - ) { - // Load default from module configuration: - $default = $this->getConfigByClass($driver, $config, 'defaultTab', null); - - // Missing/invalid record driver configuration? Fall back to provided - // default: - if ((!$default || !isset($tabs[$default])) && isset($tabs[$fallback])) { - $default = $fallback; - } - - // Is configured tab still invalid? If so, pick first existing tab: - if ((!$default || !isset($tabs[$default])) && !empty($tabs)) { - $keys = array_keys($tabs); - $default = $keys[0]; - } - - return $default; - } - - /** - * Convenience method to load tab information, including default, in a - * single pass. Returns an associative array with 'tabs' and 'default' keys. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param \Zend\Http\Request $request User request (optional) - * @param string $fallback Fallback default tab to use if no - * tab specified or matched. - * - * @return array - */ - public function getTabDetailsForRecord(AbstractRecordDriver $driver, - array $config, $request = null, $fallback = null - ) { - $tabs = $this->getTabsForRecord($driver, $config, $request); - $default = $this->getDefaultTabForRecord($driver, $config, $tabs, $fallback); - return compact('tabs', 'default'); - } - - /** - * Get an array of valid tabs for the provided record driver. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param \Zend\Http\Request $request User request (optional) - * - * @return array service name => tab object - */ - public function getTabsForRecord(AbstractRecordDriver $driver, - array $config, $request = null - ) { - $tabs = []; - foreach ($this->getTabServiceNames($driver, $config) as $tabKey => $svc) { - if (!$this->has($svc)) { - continue; - } - $newTab = $this->get($svc); - if (method_exists($newTab, 'setRecordDriver')) { - $newTab->setRecordDriver($driver); - } - if ($request instanceof \Zend\Http\Request - && method_exists($newTab, 'setRequest') - ) { - $newTab->setRequest($request); - } - if ($newTab->isActive()) { - $tabs[$tabKey] = $newTab; - } - } - return $tabs; + return TabInterface::class; } } diff --git a/module/VuFind/src/VuFind/RecordTab/PreviewFactory.php b/module/VuFind/src/VuFind/RecordTab/PreviewFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9e77a5bcd538b0f737aaaef03facaaf2e1d72e16 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/PreviewFactory.php @@ -0,0 +1,81 @@ +<?php +/** + * Factory for building the Preview tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the Preview tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PreviewFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $cfg = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + // currently only active if config [content] [previews] contains google + // and googleoptions[tab] is not empty. + $active = false; + if (isset($cfg->Content->previews)) { + $previews = array_map( + 'trim', explode(',', strtolower($cfg->Content->previews)) + ); + if (in_array('google', $previews) + && strlen(trim($cfg->Content->GoogleOptions['tab'] ?? '')) > 0 + ) { + $active = true; + } + } + return new $requestedName($active); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/ReviewsFactory.php b/module/VuFind/src/VuFind/RecordTab/ReviewsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..baeb44580bbf6ab76cd54759a53955b133520a0c --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/ReviewsFactory.php @@ -0,0 +1,47 @@ +<?php +/** + * Factory for building Reviews tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +/** + * Factory for building Reviews tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ReviewsFactory extends AbstractContentFactory +{ + /** + * The name of the tab being constructed. + * + * @var string + */ + protected $tabName = 'reviews'; +} diff --git a/module/VuFind/src/VuFind/RecordTab/Search2CollectionList.php b/module/VuFind/src/VuFind/RecordTab/Search2CollectionList.php new file mode 100644 index 0000000000000000000000000000000000000000..e9b7c5a2dcad98d70719dcefde06a0ff293c67c1 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/Search2CollectionList.php @@ -0,0 +1,47 @@ +<?php +/** + * Search 2 Collection list tab + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +/** + * Search 2 Collection list tab + * + * @category VuFind + * @package RecordTabs + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class Search2CollectionList extends CollectionList +{ + /** + * Backend id + * + * @var string + */ + protected $searchClassId = 'Search2Collection'; +} diff --git a/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php index 31c9151f666353a9d0554af8e3b8db679316dbbf..f774f4fb8b7e575d4330694cfb9ede05c9a66662 100644 --- a/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php +++ b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarousel.php @@ -1,6 +1,6 @@ <?php /** - * Staff view (array dump) tab + * Similar items carousel tab. * * PHP version 7 * @@ -28,7 +28,7 @@ namespace VuFind\RecordTab; /** - * Staff view (array dump) tab + * Similar items carousel tab. * * @category VuFind * @package RecordTabs diff --git a/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarouselFactory.php b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarouselFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ea0e50c1fb34bd404864e3791798b2b6dd92a060 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/SimilarItemsCarouselFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for building the SimilarItemsCarousel tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the SimilarItemsCarousel tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class SimilarItemsCarouselFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(\VuFindSearch\Service::class)); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/StaffViewOverdrive.php b/module/VuFind/src/VuFind/RecordTab/StaffViewOverdrive.php new file mode 100644 index 0000000000000000000000000000000000000000..b2c3cf2caf5ece2afa0cd4c1c00ce16ec596b2b5 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/StaffViewOverdrive.php @@ -0,0 +1,58 @@ +<?php +/** + * Staff view (array dump) tab + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +/** + * Staff view (array dump) tab + * + * @category VuFind + * @package RecordTabs + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class StaffViewOverdrive extends AbstractBase +{ + /** + * Constructor + */ + public function __construct() + { + $this->accessPermission = 'access.StaffViewTab'; + } + + /** + * Get the on-screen description for this tab. + * + * @return string + */ + public function getDescription() + { + return 'Staff View'; + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/TOCFactory.php b/module/VuFind/src/VuFind/RecordTab/TOCFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..19d3e94e8166588b109196c2593d13cea72ca776 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/TOCFactory.php @@ -0,0 +1,47 @@ +<?php +/** + * Factory for building TOC tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +/** + * Factory for building TOC tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TOCFactory extends AbstractContentFactory +{ + /** + * The name of the tab being constructed. + * + * @var string + */ + protected $tabName = 'toc'; +} diff --git a/module/VuFind/src/VuFind/RecordTab/TabManager.php b/module/VuFind/src/VuFind/RecordTab/TabManager.php new file mode 100644 index 0000000000000000000000000000000000000000..760af483bd17f9ccfa384c950012901edcdedbd8 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/TabManager.php @@ -0,0 +1,283 @@ +<?php +/** + * Record tab manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; + +/** + * Record tab manager + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class TabManager +{ + /** + * Settings for different tab contexts. + * + * @var array + */ + protected $contextSettings = [ + 'record' => [ + 'configFile' => 'RecordTabs', + 'legacyConfigSection' => 'recorddriver_tabs', + ], + 'collection' => [ + 'configFile' => 'CollectionTabs', + 'legacyConfigSection' => 'recorddriver_collection_tabs', + ], + ]; + + /** + * Tab configurations + * + * @var array + */ + protected $config = []; + + /** + * Configuration plugin manager + * + * @var ConfigManager + */ + protected $configManager; + + /** + * RecordTab plugin manager + * + * @var PluginManager + */ + protected $pluginManager; + + /** + * Overall framework configuration + * + * @var array + */ + protected $zendConfig; + + /** + * Current active context (defaults to 'record') + * + * @var string + */ + protected $context = 'record'; + + /** + * Constructor + * + * @param PluginManager $pm RecordTab plugin manager + * @param ConfigManager $cm Configuration plugin manager + * @param array $zendConfig Zend Framework configuration + */ + public function __construct(PluginManager $pm, ConfigManager $cm, + $zendConfig = [] + ) { + $this->pluginManager = $pm; + $this->configManager = $cm; + $this->zendConfig = $zendConfig; + + // Initialize default context. + $this->initializeCurrentContext(); + } + + /** + * Set and (if necessary) initialize the context. + * + * @param string $context Context to initialize + * + * @return void + * @throws \Exception + */ + public function setContext($context) + { + if (!in_array($context, array_keys($this->contextSettings))) { + throw new \Exception("Unsupported context: $context"); + } + $this->context = $context; + $this->initializeCurrentContext(); + } + + /** + * Initialize the current context (if not already initialized). + * + * @return void + */ + protected function initializeCurrentContext() + { + if (!isset($this->config[$this->context])) { + $key = $this->contextSettings[$this->context]['legacyConfigSection'] + ?? 'recorddriver_tabs'; + $legacyConfig = $this->zendConfig['vufind'][$key] ?? []; + $iniConfig = $this->configManager->get( + $this->contextSettings[$this->context]['configFile'] + )->toArray(); + $this->config[$this->context] = array_merge($legacyConfig, $iniConfig); + } + } + + /** + * Load the specified key from the configuration array using the best + * available match to the class of the provided driver. Return the default + * value if no match is found. + * + * @param AbstractRecordDriver $driver Record driver + * @param string $setting Key to load from configuration + * @param string $default Default to use if no setting found + * + * @return mixed + */ + protected function getConfigByClass(AbstractRecordDriver $driver, + $setting, $default + ) { + // Get the current record driver's class name, then start a loop + // in case we need to use a parent class' name to find the appropriate + // setting. + $className = get_class($driver); + do { + if (isset($this->config[$this->context][$className][$setting])) { + return $this->config[$this->context][$className][$setting]; + } + } while ($className = get_parent_class($className)); + // No setting found... + return $default; + } + + /** + * Get an array of service names by looking up the provided record driver in + * the provided tab configuration array. + * + * @param AbstractRecordDriver $driver Record driver + * + * @return array + */ + protected function getTabServiceNames(AbstractRecordDriver $driver) + { + return $this->getConfigByClass($driver, 'tabs', []); + } + + /** + * Get an array of tabs names configured to load via AJAX in the background + * + * @param AbstractRecordDriver $driver Record driver + * + * @return array + */ + public function getBackgroundTabNames(AbstractRecordDriver $driver) + { + return $this->getConfigByClass($driver, 'backgroundLoadedTabs', []); + } + + /** + * Get a default tab by looking up the provided record driver in the tab + * configuration array. + * + * @param AbstractRecordDriver $driver Record driver + * @param array $tabs Details on available tabs (returned + * from getTabsForRecord()). + * @param string $fallback Fallback to use if no tab specified + * or matched. + * + * @return string + */ + public function getDefaultTabForRecord(AbstractRecordDriver $driver, + array $tabs, $fallback = null + ) { + // Load default from module configuration: + $default = $this->getConfigByClass($driver, 'defaultTab', null); + + // Missing/invalid record driver configuration? Fall back to provided + // default: + if ((!$default || !isset($tabs[$default])) && isset($tabs[$fallback])) { + $default = $fallback; + } + + // Is configured tab still invalid? If so, pick first existing tab: + if ((!$default || !isset($tabs[$default])) && !empty($tabs)) { + $keys = array_keys($tabs); + $default = $keys[0]; + } + + return $default; + } + + /** + * Convenience method to load tab information, including default, in a + * single pass. Returns an associative array with 'tabs' and 'default' keys. + * + * @param AbstractRecordDriver $driver Record driver + * @param \Zend\Http\Request $request User request (optional) + * @param string $fallback Fallback default tab to use if no + * tab specified or matched. + * + * @return array + */ + public function getTabDetailsForRecord(AbstractRecordDriver $driver, + $request = null, $fallback = null + ) { + $tabs = $this->getTabsForRecord($driver, $request); + $default = $this->getDefaultTabForRecord($driver, $tabs, $fallback); + return compact('tabs', 'default'); + } + + /** + * Get an array of valid tabs for the provided record driver. + * + * @param AbstractRecordDriver $driver Record driver + * @param \Zend\Http\Request $request User request (optional) + * + * @return array service name => tab object + */ + public function getTabsForRecord(AbstractRecordDriver $driver, + $request = null + ) { + $tabs = []; + foreach ($this->getTabServiceNames($driver) as $tabKey => $svc) { + if (!$this->pluginManager->has($svc)) { + continue; + } + $newTab = $this->pluginManager->get($svc); + if (method_exists($newTab, 'setRecordDriver')) { + $newTab->setRecordDriver($driver); + } + if ($request instanceof \Zend\Http\Request + && method_exists($newTab, 'setRequest') + ) { + $newTab->setRequest($request); + } + if ($newTab->isActive()) { + $tabs[$tabKey] = $newTab; + } + } + return $tabs; + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php b/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4f352736889f914b5aebab69761615b2ece5f897 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for building the TabManager. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the TabManager. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TabManagerFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $container->get('config') + ); + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/UserCommentsFactory.php b/module/VuFind/src/VuFind/RecordTab/UserCommentsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0863f8aff112fc8146b34aec652e5d99044e5c99 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/UserCommentsFactory.php @@ -0,0 +1,76 @@ +<?php +/** + * Factory for building the UserComments tab. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the UserComments tab. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UserCommentsFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $captchaConfig = $config->Captcha->forms ?? ''; + $useRecaptcha = trim($captchaConfig) === '*' + || strpos($captchaConfig, 'userComments') !== false; + return new $requestedName( + 'enabled' === $capabilities->getCommentSetting(), + $useRecaptcha + ); + } +} diff --git a/module/VuFind/src/VuFind/Related/PluginManager.php b/module/VuFind/src/VuFind/Related/PluginManager.php index 90e946f2871088e62d57e997d92f79b48c0e6847..2dfce28f279d50b84da4bd30f03edf44769229e1 100644 --- a/module/VuFind/src/VuFind/Related/PluginManager.php +++ b/module/VuFind/src/VuFind/Related/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Related; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Related record plugin manager * @@ -44,11 +46,11 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'channels' => 'VuFind\Related\Channels', - 'editions' => 'VuFind\Related\Deprecated', - 'similar' => 'VuFind\Related\Similar', - 'worldcateditions' => 'VuFind\Related\Deprecated', - 'worldcatsimilar' => 'VuFind\Related\WorldCatSimilar', + 'channels' => Channels::class, + 'editions' => Deprecated::class, + 'similar' => Similar::class, + 'worldcateditions' => Deprecated::class, + 'worldcatsimilar' => WorldCatSimilar::class, ]; /** @@ -57,11 +59,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Related\Channels' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Related\Deprecated' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Related\Similar' => 'VuFind\Related\SimilarFactory', - 'VuFind\Related\WorldCatSimilar' => 'VuFind\Related\SimilarFactory', + Channels::class => InvokableFactory::class, + Deprecated::class => InvokableFactory::class, + Similar::class => SimilarFactory::class, + WorldCatSimilar::class => SimilarFactory::class, ]; /** @@ -79,7 +80,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager // These objects are not meant to be shared -- every time we retrieve one, // we are building a brand new object. $this->sharedByDefault = false; - $this->addAbstractFactory('VuFind\Related\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -91,6 +92,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Related\RelatedInterface'; + return RelatedInterface::class; } } diff --git a/module/VuFind/src/VuFind/Related/SimilarFactory.php b/module/VuFind/src/VuFind/Related/SimilarFactory.php index 514134011793e59c52c2119d3b2b145062b72903..d2278baf28fc7ef1b5041ff57f09c42a7359b9ae 100644 --- a/module/VuFind/src/VuFind/Related/SimilarFactory.php +++ b/module/VuFind/src/VuFind/Related/SimilarFactory.php @@ -61,6 +61,6 @@ class SimilarFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - return new $requestedName($container->get('VuFindSearch\Service')); + return new $requestedName($container->get(\VuFindSearch\Service::class)); } } diff --git a/module/VuFind/src/VuFind/Resolver/Driver/AbstractBaseFactory.php b/module/VuFind/src/VuFind/Resolver/Driver/AbstractBaseFactory.php index 0b20abfada74d49920ba914c11425c5b214a07ff..0b26bf7d095f3577fd3203e098819ebe2decc9df 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/AbstractBaseFactory.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/AbstractBaseFactory.php @@ -58,7 +58,8 @@ class AbstractBaseFactory implements FactoryInterface public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName( $config->OpenURL->url, ...($options ?: []) diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Alma.php b/module/VuFind/src/VuFind/Resolver/Driver/Alma.php new file mode 100644 index 0000000000000000000000000000000000000000..e0d41cdf0d1d50016207a7025cf28293e48171c6 --- /dev/null +++ b/module/VuFind/src/VuFind/Resolver/Driver/Alma.php @@ -0,0 +1,199 @@ +<?php +/** + * Alma Link Resolver Driver + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Resolver_Drivers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki + */ +namespace VuFind\Resolver\Driver; + +/** + * Alma Link Resolver Driver + * + * @category VuFind + * @package Resolver_Drivers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:link_resolver_drivers Wiki + */ +class Alma extends AbstractBase +{ + /** + * HTTP client + * + * @var \Zend\Http\Client + */ + protected $httpClient; + + /** + * List of filter reasons that are ignored (displayed regardless of filtering) + * + * @var array + */ + protected $ignoredFilterReasons = ['Date Filter']; + + /** + * Constructor + * + * @param string $baseUrl Base URL for link resolver + * @param \Zend\Http\Client $httpClient HTTP client + */ + public function __construct($baseUrl, \Zend\Http\Client $httpClient) + { + parent::__construct($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) + { + // Make the call to Alma and load results + $url = $this->getResolverUrl( + 'svc_dat=CTO&response_type=xml&' . $openURL + ); + return $this->httpClient->setUri($url)->send()->getBody(); + } + + /** + * 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) + { + $records = []; // array to return + try { + $xml = new \SimpleXmlElement($xmlstr); + } catch (\Exception $e) { + return $records; + } + + foreach ($xml->context_services->children() as $service) { + $filtered = $this->getKeyWithId($service, 'Filtered'); + if ('true' === $filtered) { + $reason = $this->getKeyWithId($service, 'Filter reason'); + if (!in_array($reason, $this->ignoredFilterReasons)) { + continue; + } + } + $serviceType = $this->mapServiceType( + (string)$service->attributes()->service_type + ); + if (!$serviceType) { + continue; + } + if ('getWebService' === $serviceType) { + $title = $this->getKeyWithId($service, 'public_name'); + $href = $this->getKeyWithId($service, 'url'); + $access = ''; + } else { + $title = $this->getKeyWithId($service, 'package_public_name'); + $href = (string)$service->resolution_url; + $access = $this->getKeyWithId($service, 'Is_free') + ? 'open' : 'limited'; + } + if ($coverage = $this->getKeyWithId($service, 'Availability')) { + $coverage = $this->cleanupText($coverage); + } + if ($notes = $this->getKeyWithId($service, 'public_note')) { + $notes = $this->cleanupText($notes); + } + $authentication = $this->getKeyWithId($service, 'Authentication_note'); + if ($authentication) { + $authentication = $this->cleanupText($authentication); + } + + $record = compact( + 'title', 'coverage', 'access', 'href', 'notes', 'authentication' + ); + $record['service_type'] = $serviceType; + $records[] = $record; + } + return $records; + } + + /** + * Get a key with the specified id from the context_service element + * + * @param \SimpleXMLElement $service Service element + * @param string $id Key id + * + * @return string + */ + protected function getKeyWithId(\SimpleXMLElement $service, $id) + { + foreach ($service->keys->children() as $key) { + if ((string)$key->attributes()->id === $id) { + return (string)$key; + } + } + return ''; + } + + /** + * Map Alma service types to VuFind. Returns an empty string for an unmapped + * value. + * + * @param string $serviceType Alma service type + * + * @return string + */ + protected function mapServiceType($serviceType) + { + $map = [ + 'getFullTxt' => 'getFullTxt', + 'getHolding' => 'getHolding', + 'GeneralElectronicService' => 'getWebService', + 'DB' => 'getFullTxt', + 'Package' => 'getFullTxt', + ]; + return $map[$serviceType] ?? ''; + } + + /** + * Clean up textual information + * + * @param string $str Text + * + * @return string + */ + protected function cleanupText($str) + { + $str = trim(preg_replace('/<br\/?>/', ' ', $str)); + $str = strip_tags($str); + return $str; + } +} diff --git a/module/VuFind/src/VuFind/Resolver/Driver/Demo.php b/module/VuFind/src/VuFind/Resolver/Driver/Demo.php index af33fe2555c1622cdad7516951bcfac68eba239f..4a9a38a682fe3a4e3a614f2c4e45d5728b2afb08 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/Demo.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/Demo.php @@ -82,14 +82,17 @@ class Demo extends AbstractBase 'title' => 'Print', 'coverage' => 'fake1', 'service_type' => 'getHolding', - 'access' => 'unknown' + 'access' => 'unknown', + 'notes' => 'General notes', ], [ 'href' => 'https://vufind.org/wiki?' . $data . '#electronic', 'title' => 'Electronic', 'coverage' => 'fake2', 'service_type' => 'getFullTxt', - 'access' => 'open' + 'access' => 'open', + 'authentication' => 'Authentication notes', + 'notes' => 'General notes', ], ]; } diff --git a/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php b/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php index ecd7e14b920dfebc230a9c06231d0a6466bde1ef..fc115b94e9a342668c8aebdf4639990d58c3434e 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/DriverWithHttpClientFactory.php @@ -57,7 +57,7 @@ class DriverWithHttpClientFactory extends AbstractBaseFactory public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $client = $container->get('VuFindHttp\HttpService')->createClient(); + $client = $container->get(\VuFindHttp\HttpService::class)->createClient(); if ($options) { array_unshift($options, $client); } else { diff --git a/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php b/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php index 44380f9bfc67dce98a6efdbcec7343e973b0f496..a3b3ec2277dd83730b94bf173d78dee4416a7294 100644 --- a/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php +++ b/module/VuFind/src/VuFind/Resolver/Driver/PluginManager.php @@ -27,6 +27,8 @@ */ namespace VuFind\Resolver\Driver; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Resolver driver plugin manager * @@ -44,13 +46,14 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - '360link' => 'VuFind\Resolver\Driver\Threesixtylink', - 'demo' => 'VuFind\Resolver\Driver\Demo', - 'ezb' => 'VuFind\Resolver\Driver\Ezb', - 'sfx' => 'VuFind\Resolver\Driver\Sfx', - 'redi' => 'VuFind\Resolver\Driver\Redi', - 'threesixtylink' => 'VuFind\Resolver\Driver\Threesixtylink', - 'generic' => 'VuFind\Resolver\Driver\Generic', + '360link' => Threesixtylink::class, + 'alma' => Alma::class, + 'demo' => Demo::class, + 'ezb' => Ezb::class, + 'sfx' => Sfx::class, + 'redi' => Redi::class, + 'threesixtylink' => Threesixtylink::class, + 'generic' => Generic::class, 'other' => 'generic' ]; @@ -60,18 +63,13 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Resolver\Driver\Threesixtylink' => - 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', - 'VuFind\Resolver\Driver\Demo' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Resolver\Driver\Ezb' => - 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', - 'VuFind\Resolver\Driver\Sfx' => - 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', - 'VuFind\Resolver\Driver\Redi' => - 'VuFind\Resolver\Driver\DriverWithHttpClientFactory', - 'VuFind\Resolver\Driver\Generic' => - 'VuFind\Resolver\Driver\AbstractBaseFactory', + Alma::class => DriverWithHttpClientFactory::class, + Threesixtylink::class => DriverWithHttpClientFactory::class, + Demo::class => InvokableFactory::class, + Ezb::class => DriverWithHttpClientFactory::class, + Sfx::class => DriverWithHttpClientFactory::class, + Redi::class => DriverWithHttpClientFactory::class, + Generic::class => AbstractBaseFactory::class, ]; /** @@ -86,7 +84,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Resolver\Driver\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -98,6 +96,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Resolver\Driver\DriverInterface'; + return DriverInterface::class; } } diff --git a/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php b/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php index 2068df49cf47546f893eb899395341a61c2775d3..743f9c311441fa870222a0464e96766981ce7b43 100644 --- a/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php +++ b/module/VuFind/src/VuFind/Role/DynamicRoleProviderFactory.php @@ -96,7 +96,7 @@ class DynamicRoleProviderFactory implements FactoryInterface $config = $rbacConfig['role_provider']['VuFind\Role\DynamicRoleProvider']; // Load the permissions: - $configLoader = $serviceLocator->get('VuFind\Config\PluginManager'); + $configLoader = $serviceLocator->get(\VuFind\Config\PluginManager::class); $permissions = $configLoader->get('permissions')->toArray(); // If we're configured to map legacy settings, do so now: diff --git a/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php b/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php index e94d3b64d58be013ed34c07f87000a967181d5fe..fe16edf2eeb50bedf7ecf9310b194ba6d6a385e6 100644 --- a/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php +++ b/module/VuFind/src/VuFind/Role/PermissionDeniedManagerFactory.php @@ -61,8 +61,8 @@ class PermissionDeniedManagerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName( - $container->get('VuFind\Config\PluginManager')->get('permissionBehavior') - ); + $cfg = $container->get(\VuFind\Config\PluginManager::class) + ->get('permissionBehavior'); + return new $requestedName($cfg); } } diff --git a/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php b/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php index 794b33cdfab07a13fd06cd928deb3e0bbd043ff3..98689092e3606ab4dbfcca0e4b1120afe340b28f 100644 --- a/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php +++ b/module/VuFind/src/VuFind/Role/PermissionManagerFactory.php @@ -61,11 +61,11 @@ class PermissionManagerFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $permissions = $container->get('VuFind\Config\PluginManager') + $permissions = $container->get(\VuFind\Config\PluginManager::class) ->get('permissions')->toArray(); $permManager = new $requestedName($permissions); $permManager->setAuthorizationService( - $container->get('ZfcRbac\Service\AuthorizationService') + $container->get(\ZfcRbac\Service\AuthorizationService::class) ); return $permManager; } diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php deleted file mode 100644 index efcfbadf7bd84f1c1b3abfd1e3b8d67a617f6dea..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/Factory.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php -/** - * Permission Provider Factory Class - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Authorization - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace VuFind\Role\PermissionProvider; - -use Zend\ServiceManager\ServiceManager; - -/** - * Permission Provider Factory Class - * - * @category VuFind - * @package Authorization - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for IpRange - * - * @param ServiceManager $sm Service manager. - * - * @return IpRange - */ - public static function getIpRange(ServiceManager $sm) - { - return new IpRange( - $sm->get('Request'), - $sm->get('VuFind\Net\IpAddressUtils') - ); - } - - /** - * Factory for IpRegEx - * - * @param ServiceManager $sm Service manager. - * - * @return IpRegEx - */ - public static function getIpRegEx(ServiceManager $sm) - { - return new IpRegEx($sm->get('Request')); - } - - /** - * Factory for ServerParam - * - * @param ServiceManager $sm Service manager. - * - * @return ServerParam - */ - public static function getServerParam(ServiceManager $sm) - { - return new ServerParam($sm->get('Request')); - } - - /** - * Factory for Shibboleth - * - * @param ServiceManager $sm Service manager. - * - * @return Shibboleth - */ - public static function getShibboleth(ServiceManager $sm) - { - return new Shibboleth( - $sm->get('Request'), - $sm->get('VuFind\Config\PluginManager')->get('config') - ); - } - - /** - * Factory for Username - * - * @param ServiceManager $sm Service manager. - * - * @return Username - */ - public static function getUsername(ServiceManager $sm) - { - return new Username( - $sm->get('ZfcRbac\Service\AuthorizationService') - ); - } - - /** - * Factory for User - * - * @param ServiceManager $sm Service manager. - * - * @return User - */ - public static function getUser(ServiceManager $sm) - { - return new User( - $sm->get('ZfcRbac\Service\AuthorizationService') - ); - } -} diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/InjectAuthorizationServiceFactory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/InjectAuthorizationServiceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ddbc8beead653e187659e1282616d12713ad4e89 --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/InjectAuthorizationServiceFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for instantiating permission providers with authorization service. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; +use ZfcRbac\Service\AuthorizationService; + +/** + * Factory for instantiating permission providers with authorization service. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class InjectAuthorizationServiceFactory + implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get(AuthorizationService::class)); + } +} diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/InjectRequestFactory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/InjectRequestFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e290b3ceb68149b428f99d2d45d280918f741ca8 --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/InjectRequestFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for instantiating permission providers with request object. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; + +/** + * Factory for instantiating permission providers with request object. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class InjectRequestFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName($container->get('Request')); + } +} diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRangeFactory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRangeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7e681375bf8e11c66983a7ec52ac3c239791d5a8 --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRangeFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for instantiating IpRange permission provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; + +/** + * Factory for instantiating IpRange permission provider. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class IpRangeFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get('Request'), + $container->get(\VuFind\Net\IpAddressUtils::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php b/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php index be82fd86c0705daad77e4738627836c08860fbaa..fa0ff0cfd69bb2636291bedfdfd53524f8839ffe 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/PluginManager.php @@ -44,13 +44,13 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'ipRange' => 'VuFind\Role\PermissionProvider\IpRange', - 'ipRegEx' => 'VuFind\Role\PermissionProvider\IpRegEx', - 'role' => 'VuFind\Role\PermissionProvider\Role', - 'serverParam' => 'VuFind\Role\PermissionProvider\ServerParam', - 'shibboleth' => 'VuFind\Role\PermissionProvider\Shibboleth', - 'user' => 'VuFind\Role\PermissionProvider\User', - 'username' => 'VuFind\Role\PermissionProvider\Username', + 'ipRange' => IpRange::class, + 'ipRegEx' => IpRegEx::class, + 'role' => Role::class, + 'serverParam' => ServerParam::class, + 'shibboleth' => Shibboleth::class, + 'user' => User::class, + 'username' => Username::class, ]; /** @@ -59,20 +59,13 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Role\PermissionProvider\IpRange' => - 'VuFind\Role\PermissionProvider\Factory::getIpRange', - 'VuFind\Role\PermissionProvider\IpRegEx' => - 'VuFind\Role\PermissionProvider\Factory::getIpRegEx', - 'VuFind\Role\PermissionProvider\Role' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Role\PermissionProvider\ServerParam' => - 'VuFind\Role\PermissionProvider\Factory::getServerParam', - 'VuFind\Role\PermissionProvider\Shibboleth' => - 'VuFind\Role\PermissionProvider\Factory::getShibboleth', - 'VuFind\Role\PermissionProvider\User' => - 'VuFind\Role\PermissionProvider\Factory::getUser', - 'VuFind\Role\PermissionProvider\Username' => - 'VuFind\Role\PermissionProvider\Factory::getUsername', + IpRange::class => IpRangeFactory::class, + IpRegEx::class => InjectRequestFactory::class, + Role::class => \Zend\ServiceManager\Factory\InvokableFactory::class, + ServerParam::class => InjectRequestFactory::class, + Shibboleth::class => ShibbolethFactory::class, + User::class => InjectAuthorizationServiceFactory::class, + Username::class => InjectAuthorizationServiceFactory::class, ]; /** @@ -83,6 +76,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Role\PermissionProvider\PermissionProviderInterface'; + return PermissionProviderInterface::class; } } diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/ShibbolethFactory.php b/module/VuFind/src/VuFind/Role/PermissionProvider/ShibbolethFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9c99caf715629badd8918108b2981137a37327d2 --- /dev/null +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/ShibbolethFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for instantiating Shibboleth permission provider. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; + +/** + * Factory for instantiating Shibboleth permission provider. + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ShibbolethFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $cfg = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + return new $requestedName($container->get('Request'), $cfg); + } +} diff --git a/module/VuFind/src/VuFind/SMS/Factory.php b/module/VuFind/src/VuFind/SMS/Factory.php index 4fb0cffb54828930887ee7bdcc7c82f3ff04c45e..8344aaed638cd57f5697997dc3710a15b9dca8fd 100644 --- a/module/VuFind/src/VuFind/SMS/Factory.php +++ b/module/VuFind/src/VuFind/SMS/Factory.php @@ -58,8 +58,9 @@ class Factory implements FactoryInterface array $options = null ) { // Load configurations: - $mainConfig = $container->get('VuFind\Config\PluginManager')->get('config'); - $smsConfig = $container->get('VuFind\Config\PluginManager')->get('sms'); + $configManager = $container->get(\VuFind\Config\PluginManager::class); + $mainConfig = $configManager->get('config'); + $smsConfig = $configManager->get('sms'); // Determine SMS type: $type = isset($smsConfig->General->smsType) @@ -68,10 +69,11 @@ class Factory implements FactoryInterface // Initialize object based on requested type: switch (strtolower($type)) { case 'clickatell': - $client = $container->get('VuFindHttp\HttpService')->createClient(); + $client = $container->get(\VuFindHttp\HttpService::class) + ->createClient(); return new Clickatell($smsConfig, ['client' => $client]); case 'mailer': - $options = ['mailer' => $container->get('VuFind\Mailer\Mailer')]; + $options = ['mailer' => $container->get(\VuFind\Mailer\Mailer::class)]; if (isset($mainConfig->Site->email)) { $options['defaultFrom'] = $mainConfig->Site->email; } diff --git a/module/VuFind/src/VuFind/Search/BackendRegistry.php b/module/VuFind/src/VuFind/Search/BackendRegistry.php index fad454710ad714020e5abb73678b4d5d221f26db..5c79c3550413c012054a50208f7cd39b37baf4c4 100644 --- a/module/VuFind/src/VuFind/Search/BackendRegistry.php +++ b/module/VuFind/src/VuFind/Search/BackendRegistry.php @@ -66,6 +66,7 @@ class BackendRegistry extends \VuFind\ServiceManager\AbstractPluginManager 'Pazpar2' => 'VuFind\Search\Factory\Pazpar2BackendFactory', 'Primo' => 'VuFind\Search\Factory\PrimoBackendFactory', 'Search2' => 'VuFind\Search\Factory\Search2BackendFactory', + 'Search2Collection' => 'VuFind\Search\Factory\Search2BackendFactory', 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', 'SolrReserves' => 'VuFind\Search\Factory\SolrReservesBackendFactory', diff --git a/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php b/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php index a8286d6ff8d6bb1547448948a268e7a48961f528..fcb2add4033ca0696a666a84789a5716c577a196 100644 --- a/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php +++ b/module/VuFind/src/VuFind/Search/Base/FacetCacheFactory.php @@ -52,7 +52,8 @@ class FacetCacheFactory implements FactoryInterface */ protected function getResults(ContainerInterface $container, $name) { - return $container->get('VuFind\Search\Results\PluginManager')->get($name); + return $container->get(\VuFind\Search\Results\PluginManager::class) + ->get($name); } /** @@ -78,8 +79,8 @@ class FacetCacheFactory implements FactoryInterface $parts = explode('\\', $requestedName); $requestedNamespace = $parts[count($parts) - 2]; $results = $this->getResults($container, $requestedNamespace); - $cacheManager = $container->get('VuFind\Cache\Manager'); - $language = $container->get('Zend\Mvc\I18n\Translator')->getLocale(); + $cacheManager = $container->get(\VuFind\Cache\Manager::class); + $language = $container->get(\Zend\Mvc\I18n\Translator::class)->getLocale(); return new $requestedName($results, $cacheManager, $language); } } diff --git a/module/VuFind/src/VuFind/Search/Base/Options.php b/module/VuFind/src/VuFind/Search/Base/Options.php index 882024b6624c991d1a5d89a161d355216ea6742e..4fd0bdca7e6e892b403f184e069635e64c3d2085 100644 --- a/module/VuFind/src/VuFind/Search/Base/Options.php +++ b/module/VuFind/src/VuFind/Search/Base/Options.php @@ -28,6 +28,7 @@ namespace VuFind\Search\Base; use VuFind\I18n\Translator\TranslatorAwareInterface; +use Zend\Config\Config; /** * Abstract options search model. @@ -291,6 +292,20 @@ abstract class Options implements TranslatorAwareInterface { $this->limitOptions = [$this->defaultLimit]; $this->setConfigLoader($configLoader); + + $id = $this->getSearchClassId(); + $facetSettings = $configLoader->get($this->facetsIni); + if (isset($facetSettings->AvailableFacetSortOptions[$id])) { + foreach ($facetSettings->AvailableFacetSortOptions[$id]->toArray() + as $facet => $sortOptions + ) { + $this->facetSortOptions[$facet] = []; + foreach (explode(',', $sortOptions) as $fieldAndLabel) { + list($field, $label) = explode('=', $fieldAndLabel); + $this->facetSortOptions[$facet][$field] = $label; + } + } + } } /** @@ -472,13 +487,15 @@ abstract class Options implements TranslatorAwareInterface } /** - * Get an array of sort options for facets. + * Get an array of sort options for a facet. + * + * @param string $facet Facet * * @return array */ - public function getFacetSortOptions() + public function getFacetSortOptions($facet = '*') { - return $this->facetSortOptions; + return $this->facetSortOptions[$facet] ?? $this->facetSortOptions['*'] ?? []; } /** @@ -938,4 +955,31 @@ abstract class Options implements TranslatorAwareInterface { return $this->firstlastNavigation; } + + /** + * Does this search backend support scheduled searching? + * + * @return bool + */ + public function supportsScheduledSearch() + { + // Unsupported by default! + return false; + } + + /** + * Configure autocomplete preferences from an .ini file. + * + * @param Config $searchSettings Object representation of .ini file + * + * @return void + */ + protected function configureAutocomplete(Config $searchSettings = null) + { + // Only change settings from current values if they are defined in .ini: + $this->autocompleteEnabled = $searchSettings->Autocomplete->enabled + ?? $this->autocompleteEnabled; + $this->autocompleteAutoSubmit = $searchSettings->Autocomplete->auto_submit + ?? $this->autocompleteAutoSubmit; + } } diff --git a/module/VuFind/src/VuFind/Search/Base/Params.php b/module/VuFind/src/VuFind/Search/Base/Params.php index 144ce83e701d62338b1563244620cf48fb956cd4..b1461ff607d5841035bcefe3f9574f376d9ad7d2 100644 --- a/module/VuFind/src/VuFind/Search/Base/Params.php +++ b/module/VuFind/src/VuFind/Search/Base/Params.php @@ -130,6 +130,22 @@ class Params */ protected $extraFacetLabels = []; + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections = ['ExtraFacetLabels']; + + /** + * Config sections to search for checkbox facet labels if no override + * configuration is set. + * + * @var array + */ + protected $defaultFacetLabelCheckboxSections = []; + /** * Checkbox facet configuration * @@ -198,6 +214,24 @@ class Params // Make sure we have some sort of query object: $this->query = new Query(); + + // Set up facet label settings, to be used as fallbacks if specific facets + // are not already configured: + $config = $configLoader->get($options->getFacetsIni()); + $sections = $config->FacetLabels->labelSections + ?? $this->defaultFacetLabelSections; + foreach ($sections as $section) { + foreach ($config->$section ?? [] as $field => $label) { + $this->extraFacetLabels[$field] = $label; + } + } + + // Activate all relevant checkboxes, also important for labeling: + $checkboxSections = $config->FacetLabels->checkboxSections + ?? $this->defaultFacetLabelCheckboxSections; + foreach ($checkboxSections as $checkboxSection) { + $this->initCheckboxFacets($checkboxSection); + } } /** @@ -923,21 +957,22 @@ class Params // Extract the facet field name from the filter, then add the // relevant information to the array. list($fieldName) = explode(':', $filter); - $this->checkboxFacets[$fieldName][] + $this->checkboxFacets[$fieldName][$filter] = ['desc' => $desc, 'filter' => $filter]; } /** * Get a user-friendly string to describe the provided facet field. * - * @param string $field Facet field name. - * @param string $value Facet value. + * @param string $field Facet field name. + * @param string $value Facet value. + * @param string $default Default field name (null for default behavior). * - * @return string Human-readable description of field. + * @return string Human-readable description of field. * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getFacetLabel($field, $value = null) + public function getFacetLabel($field, $value = null, $default = null) { if (!isset($this->facetConfig[$field]) && !isset($this->extraFacetLabels[$field]) @@ -949,7 +984,8 @@ class Params return $this->facetConfig[$field]; } return isset($this->extraFacetLabels[$field]) - ? $this->extraFacetLabels[$field] : 'unrecognized_facet_label'; + ? $this->extraFacetLabels[$field] + : ($default ?: 'unrecognized_facet_label'); } /** @@ -977,11 +1013,22 @@ class Params * * @return array */ - public function getFilters() + public function getRawFilters() { return $this->filterList; } + /** + * Get the raw filter list. + * + * @return array + * @deprecated Obsolete since VuFind 6.1. Use getRawFilters() instead. + */ + public function getFilters() + { + return $this->getRawFilters(); + } + /** * Return an array structure containing information about all current filters. * @@ -1105,15 +1152,22 @@ class Params /** * Get information on the current state of the boolean checkbox facets. * + * @param array $whitelist Whitelist of checkbox filters to return (null for all) + * * @return array */ - public function getCheckboxFacets() + public function getCheckboxFacets(array $whitelist = null) { // Build up an array of checkbox facets with status booleans and // toggle URLs. $result = []; foreach ($this->checkboxFacets as $facets) { foreach ($facets as $facet) { + // If the current filter is not on the whitelist, skip it (but + // accept everything if the whitelist is empty). + if (!empty($whitelist) && !in_array($facet['filter'], $whitelist)) { + continue; + } $facet['selected'] = $this->hasFilter($facet['filter']); // Is this checkbox always visible, even if non-selected on the // "no results" screen? By default, no (may be overridden by @@ -1597,29 +1651,6 @@ class Params $this->query = QueryAdapter::deminify($minified->t); } - /** - * Load all available facet settings. This is mainly useful for showing - * appropriate labels when an existing search has multiple filters associated - * with it. - * - * @param string $preferredSection Section to favor when loading settings; - * if multiple sections contain the same facet, this section's description - * will be favored. - * - * @return void - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function activateAllFacets($preferredSection = false) - { - // By default, there is only 1 set of facet settings, so this function isn't - // really necessary. However, in the Search History screen, we need to - // use this for Solr-based Search Objects, so we need this dummy method to - // allow other types of Search Objects to co-exist with Solr-based ones. - // See the Solr Search Object for details of how this works if you need to - // implement context-sensitive facet settings in another module. - } - /** * Override the normal search behavior with an explicit array of IDs that must * be retrieved. @@ -1700,7 +1731,7 @@ class Params /** * Return search query object. * - * @return VuFindSearch\Query\AbstractQuery + * @return \VuFindSearch\Query\AbstractQuery */ public function getQuery() { @@ -1766,11 +1797,19 @@ class Params ) { $config = $this->configLoader ->get($cfgFile ?? $this->getOptions()->getFacetsIni()); - if (empty($config->$facetList)) { - return false; - } - foreach ($config->$facetList as $key => $value) { - $this->addCheckboxFacet($key, $value); + $retVal = false; + // If the section is in reverse order, the tilde will flag this: + if (substr($facetList, 0, 1) == '~') { + foreach ($config->{substr($facetList, 1)} ?? [] as $value => $key) { + $this->addCheckboxFacet($key, $value); + $retVal = true; + } + } else { + foreach ($config->$facetList ?? [] as $key => $value) { + $this->addCheckboxFacet($key, $value); + $retVal = true; + } } + return $retVal; } } diff --git a/module/VuFind/src/VuFind/Search/Base/Results.php b/module/VuFind/src/VuFind/Search/Base/Results.php index 2dd1b20d980b0b8ab97af47f4f591d60dfd107b7..032870307d04e8ce663446192cf4b669dfb557dd 100644 --- a/module/VuFind/src/VuFind/Search/Base/Results.php +++ b/module/VuFind/src/VuFind/Search/Base/Results.php @@ -74,6 +74,13 @@ abstract class Results */ protected $results = null; + /** + * Any errors reported by the search backend + * + * @var array + */ + protected $errors = null; + /** * An ID number for saving/retrieving search * @@ -144,6 +151,13 @@ abstract class Results */ protected $recordLoader; + /** + * URL query helper factory + * + * @var UrlQueryHelperFactory + */ + protected $urlQueryHelperFactory = null; + /** * Constructor * @@ -224,7 +238,7 @@ abstract class Results { // Set up URL helper: if (!isset($this->helpers['urlQuery'])) { - $factory = new UrlQueryHelperFactory(); + $factory = $this->getUrlQueryHelperFactory(); $this->helpers['urlQuery'] = $factory->fromParams( $this->getParams(), $this->getUrlQueryHelperOptions() ); @@ -257,6 +271,7 @@ abstract class Results $this->resultTotal = 0; $this->results = []; $this->suggestions = []; + $this->errors = []; // Run the search: $this->startQueryTimer(); @@ -375,6 +390,19 @@ abstract class Results return $this->results; } + /** + * Basic 'getter' for errors. + * + * @return array + */ + public function getErrors() + { + if (null === $this->errors) { + $this->performAndProcessSearch(); + } + return $this->errors; + } + /** * Basic 'getter' for ID of saved search. * @@ -574,6 +602,31 @@ abstract class Results ); } + /** + * Get URL query helper factory + * + * @return UrlQueryHelperFactory + */ + protected function getUrlQueryHelperFactory() + { + if (null === $this->urlQueryHelperFactory) { + $this->urlQueryHelperFactory = new UrlQueryHelperFactory(); + } + return $this->urlQueryHelperFactory; + } + + /** + * Set URL query helper factory + * + * @param UrlQueryHelperFactory $factory UrlQueryHelperFactory object + * + * @return void + */ + public function setUrlQueryHelperFactory(UrlQueryHelperFactory $factory) + { + $this->urlQueryHelperFactory = $factory; + } + /** * Get complete facet counts for several index fields * diff --git a/module/VuFind/src/VuFind/Search/Combined/Options.php b/module/VuFind/src/VuFind/Search/Combined/Options.php index d2dcf6098ea8ee92ce5f66fb0653b2f5dacae0f1..ad2289d75e2820cc867dcfddf839d5da7656b3d4 100644 --- a/module/VuFind/src/VuFind/Search/Combined/Options.php +++ b/module/VuFind/src/VuFind/Search/Combined/Options.php @@ -38,6 +38,22 @@ namespace VuFind\Search\Combined; */ class Options extends \VuFind\Search\Base\Options { + /** + * Constructor + * + * @param \VuFind\Config\PluginManager $configLoader Config loader + */ + public function __construct(\VuFind\Config\PluginManager $configLoader) + { + parent::__construct($configLoader); + $searchSettings = $this->configLoader->get('combined'); + if (isset($searchSettings->Basic_Searches)) { + foreach ($searchSettings->Basic_Searches as $key => $value) { + $this->basicHandlers[$key] = $value; + } + } + } + /** * Return the route name for the search results action. * diff --git a/module/VuFind/src/VuFind/Search/EDS/Options.php b/module/VuFind/src/VuFind/Search/EDS/Options.php index 5756231ac6be1159e4816f1cc62cdb9cdf5ce908..0b8c778302a38f951a81bcf1088e8911ba0964b6 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Options.php +++ b/module/VuFind/src/VuFind/Search/EDS/Options.php @@ -137,14 +137,6 @@ class Options extends \VuFind\Search\Base\Options $facetConf->Advanced_Facet_Settings->translated_facets->toArray() ); } - // Load autocomplete preferences: - if (isset($searchSettings->Autocomplete->enabled)) { - $this->autocompleteEnabled = $searchSettings->Autocomplete->enabled; - } - if (isset($searchSettings->Autocomplete->auto_submit)) { - $this->autocompleteAutoSubmit - = $searchSettings->Autocomplete->auto_submit; - } } /** @@ -406,6 +398,9 @@ class Options extends \VuFind\Search\Base\Options $this->setCommonSettings( $searchSettings, 'common_expanders', 'expanderOptions', 'commonExpanders' ); + + // Load autocomplete preferences: + $this->configureAutocomplete($searchSettings); } /** @@ -486,7 +481,7 @@ class Options extends \VuFind\Search\Base\Options if (isset($expander['DefaultOn']) && 'y' == $expander['DefaultOn'] ) { - $this->defaultExpanders[] = $expander['Id']; + $this->defaultExpanders[] = $expander['Id']; } } } diff --git a/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php b/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php index 3d32117ff0f987b553a69f800c270146f321639e..707c53d2e39c9cfe87425f1698a8690ad9251ead 100644 --- a/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php +++ b/module/VuFind/src/VuFind/Search/EDS/OptionsFactory.php @@ -28,6 +28,7 @@ namespace VuFind\Search\EDS; use Interop\Container\ContainerInterface; +use VuFindSearch\Backend\EDS\ApiException; /** * Factory for EDS search options objects. @@ -61,12 +62,18 @@ class OptionsFactory extends \VuFind\Search\Options\OptionsFactory throw new \Exception('Unexpected options sent to factory.'); } $session = new \Zend\Session\Container( - 'EBSCO', $container->get('Zend\Session\SessionManager') + 'EBSCO', $container->get(\Zend\Session\SessionManager::class) ); // No API info in session? Re-establish connection: if (!isset($session->info)) { - $backend = $container->get('VuFind\Search\BackendManager')->get('EDS'); - $backend->getSessionToken(); + $backend = $container->get(\VuFind\Search\BackendManager::class) + ->get('EDS'); + try { + $backend->getSessionToken(); + } catch (ApiException $e) { + // Retry once to work around occasional 106 errors: + $backend->getSessionToken(); + } } return parent::__invoke($container, $requestedName, [$session->info]); } diff --git a/module/VuFind/src/VuFind/Search/EDS/Params.php b/module/VuFind/src/VuFind/Search/EDS/Params.php index fe1b62460d31e7677239847b1d99e273ebffb5fc..2f8e92aefb99f0d4973b26e5b2a088e54ec92d4f 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Params.php +++ b/module/VuFind/src/VuFind/Search/EDS/Params.php @@ -55,6 +55,23 @@ class Params extends \VuFind\Search\Base\Params */ protected $extraFilterList = []; + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections + = ['Advanced_Facets', 'FacetsTop', 'Facets']; + + /** + * Config sections to search for checkbox facet labels if no override + * configuration is set. + * + * @var array + */ + protected $defaultFacetLabelCheckboxSections = ['CheckboxFacets']; + /** * Is the request using this parameters objects for setup only? * @@ -62,6 +79,19 @@ class Params extends \VuFind\Search\Base\Params */ public $isSetupOnly = false; + /** + * Constructor + * + * @param \VuFind\Search\Base\Options $options Options to use + * @param \VuFind\Config\PluginManager $configLoader Config loader + */ + public function __construct($options, \VuFind\Config\PluginManager $configLoader) + { + parent::__construct($options, $configLoader); + $this->addLimitersAsCheckboxFacets($options); + $this->addExpandersAsCheckboxFacets($options); + } + /** * Pull the search parameters * @@ -150,28 +180,6 @@ class Params extends \VuFind\Search\Base\Params } } } - $this->addLimitersAsCheckboxFacets($options); - $this->addExpandersAsCheckboxFacets($options); - } - - /** - * Return an array structure containing information about all current filters. - * - * @param bool $excludeCheckboxFilters Should we exclude checkbox filters from - * the list (to be used as a complement to getCheckboxFacets()). - * - * @return array Field, values and translation status - */ - public function getFilterList($excludeCheckboxFilters = false) - { - $filters = parent::getFilterList($excludeCheckboxFilters); - $label = $this->getFacetLabel('SEARCHMODE'); - if (isset($filters[$label])) { - foreach (array_keys($filters[$label]) as $i) { - $filters[$label][$i]['suppressDisplay'] = true; - } - } - return $filters; } /** @@ -287,25 +295,23 @@ class Params extends \VuFind\Search\Base\Params /** * Get a user-friendly string to describe the provided facet field. * - * @param string $field Facet field name. - * @param string $value Facet value. - * - * @return string Human-readable description of field. + * @param string $field Facet field name. + * @param string $value Facet value. + * @param string $default Default field name (null for default behavior). * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return string Human-readable description of field. */ - public function getFacetLabel($field, $value = null) + public function getFacetLabel($field, $value = null, $default = null) { - //Also store Limiter/Search Mode IDs/Values in the config file - $facetId = $field; + // Also store Limiter/Search Mode IDs/Values in the config file if (substr($field, 0, 6) == 'LIMIT|') { $facetId = substr($field, 6); - } - if (substr($field, 0, 11) == 'SEARCHMODE|') { + } elseif (substr($field, 0, 11) == 'SEARCHMODE|') { $facetId = substr($field, 11); + } else { + $facetId = $field; } - return isset($this->facetConfig[$facetId]) - ? $this->facetConfig[$facetId] : $facetId; + return parent::getFacetLabel($facetId, $value, $default ?: $facetId); } /** diff --git a/module/VuFind/src/VuFind/Search/EDS/Results.php b/module/VuFind/src/VuFind/Search/EDS/Results.php index 2207f8773a71736571de995bf86e0458d7371fc5..5e1cc555b68ce2ea99f603972aef65db2a46f091 100644 --- a/module/VuFind/src/VuFind/Search/EDS/Results.php +++ b/module/VuFind/src/VuFind/Search/EDS/Results.php @@ -90,7 +90,7 @@ class Results extends \VuFind\Search\Base\Results $facetResult = []; if (is_array($this->responseFacets)) { // Get the filter list -- we'll need to check it below: - $filterList = $this->getParams()->getFilters(); + $filterList = $this->getParams()->getRawFilters(); $translatedFacets = $this->getOptions()->getTranslatedFacets(); foreach ($this->responseFacets as $current) { // The "displayName" value is actually the name of the field on diff --git a/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php b/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php index ca902cb6675be571aeeeaf52ebadfd1f3bb20d17..99054358e130063214b87d38560ed10158e319b5 100644 --- a/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/FacetCache/PluginManager.php @@ -44,9 +44,9 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'search2' => 'VuFind\Search\Search2\FacetCache', - 'solr' => 'VuFind\Search\Solr\FacetCache', - 'summon' => 'VuFind\Search\Summon\FacetCache', + 'search2' => \VuFind\Search\Search2\FacetCache::class, + 'solr' => \VuFind\Search\Solr\FacetCache::class, + 'summon' => \VuFind\Search\Summon\FacetCache::class, ]; /** @@ -55,9 +55,12 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Search\Search2\FacetCache' => 'VuFind\Search\Solr\FacetCacheFactory', - 'VuFind\Search\Solr\FacetCache' => 'VuFind\Search\Solr\FacetCacheFactory', - 'VuFind\Search\Summon\FacetCache' => 'VuFind\Search\Base\FacetCacheFactory', + \VuFind\Search\Search2\FacetCache::class => + \VuFind\Search\Solr\FacetCacheFactory::class, + \VuFind\Search\Solr\FacetCache::class => + \VuFind\Search\Solr\FacetCacheFactory::class, + \VuFind\Search\Summon\FacetCache::class => + \VuFind\Search\Base\FacetCacheFactory::class, ]; /** @@ -68,6 +71,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Search\Base\FacetCache'; + return \VuFind\Search\Base\FacetCache::class; } } diff --git a/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php index d472ca2fd7f847673c2656f06cedefaa9da765e1..6cc7574f088cb61acb357a771f0d302e45928d30 100644 --- a/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/AbstractSolrBackendFactory.php @@ -67,7 +67,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -127,6 +127,20 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface */ protected $uniqueKey = 'id'; + /** + * Solr connector class + * + * @var string + */ + protected $connectorClass = Connector::class; + + /** + * Solr backend class + * + * @var string + */ + protected $backendClass = Backend::class; + /** * Constructor */ @@ -148,9 +162,10 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + $this->config = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -167,7 +182,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface */ protected function createBackend(Connector $connector) { - $backend = new Backend($connector); + $backend = new $this->backendClass($connector); $backend->setQueryBuilder($this->createQueryBuilder()); $backend->setSimilarBuilder($this->createSimilarBuilder()); if ($this->logger) { @@ -340,7 +355,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface array_push($handlers['select']['appends']['fq'], $filter); } - $connector = new Connector( + $connector = new $this->connectorClass( $this->getSolrUrl(), new HandlerMap($handlers), $this->uniqueKey ); $connector->setTimeout( @@ -350,9 +365,10 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface if ($this->logger) { $connector->setLogger($this->logger); } - if ($this->serviceLocator->has('VuFindHttp\HttpService')) { - $connector - ->setProxy($this->serviceLocator->get('VuFindHttp\HttpService')); + if ($this->serviceLocator->has(\VuFindHttp\HttpService::class)) { + $connector->setProxy( + $this->serviceLocator->get(\VuFindHttp\HttpService::class) + ); } return $connector; } @@ -405,7 +421,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface */ protected function loadSpecs() { - return $this->serviceLocator->get('VuFind\Config\SearchSpecsReader') + return $this->serviceLocator->get(\VuFind\Config\SearchSpecsReader::class) ->get($this->searchYaml); } @@ -496,7 +512,7 @@ abstract class AbstractSolrBackendFactory implements FactoryInterface $search->ConditionalHiddenFilters->toArray() ); $listener->setAuthorizationService( - $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService') + $this->serviceLocator->get(\ZfcRbac\Service\AuthorizationService::class) ); return $listener; } diff --git a/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php index 13000425e3f2d07e337ea505074e0ee1f6f28aa3..bd612a189622188166cce5a3a1f7defa47d736f0 100644 --- a/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/BrowZineBackendFactory.php @@ -51,7 +51,7 @@ class BrowZineBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -83,10 +83,11 @@ class BrowZineBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configReader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $this->browzineConfig = $configReader->get('BrowZine'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); @@ -125,7 +126,7 @@ class BrowZineBackendFactory implements FactoryInterface throw new \Exception("Missing library ID in BrowZine.ini"); } // Build HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient(); $timeout = isset($this->browzineConfig->General->timeout) ? $this->browzineConfig->General->timeout : 30; @@ -158,7 +159,8 @@ class BrowZineBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('BrowZine'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php index 6893ce0eaf8911e4825be7fb2ee0543100a40b55..3fe65d424df4da62a50ef3c751a0c8040c911d93 100644 --- a/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/EITBackendFactory.php @@ -52,7 +52,7 @@ class EITBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -84,10 +84,11 @@ class EITBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $this->config = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class) ->get('EIT'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -116,18 +117,13 @@ class EITBackendFactory implements FactoryInterface */ protected function createConnector() { - $prof = isset($this->config->General->prof) - ? $this->config->General->prof : null; - $pwd = isset($this->config->General->pwd) - ? $this->config->General->pwd : null; + $prof = $this->config->General->prof ?? null; + $pwd = $this->config->General->pwd ?? null; $base = "http://eit.ebscohost.com/Services/SearchService.asmx/Search"; - $dbs = isset($this->config->General->dbs) - ? $this->config->General->dbs : null; - $connector = new Connector( - $base, - $this->serviceLocator->get('VuFindHttp\HttpService')->createClient(), - $prof, $pwd, $dbs - ); + $dbs = $this->config->General->dbs ?? null; + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) + ->createClient(); + $connector = new Connector($base, $client, $prof, $pwd, $dbs); $connector->setLogger($this->logger); return $connector; } @@ -149,7 +145,8 @@ class EITBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('EIT'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php index f3b2cf906f8256a1a733bae581c867a957535898..8b317001bf2479f7c03acf931ffec4633e142a7e 100644 --- a/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/EdsBackendFactory.php @@ -51,7 +51,7 @@ class EdsBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger = null; @@ -90,10 +90,11 @@ class EdsBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $this->edsConfig = $this->serviceLocator->get('VuFind\Config\PluginManager') + $this->edsConfig = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class) ->get('EDS'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); return $this->createBackend($connector); @@ -108,17 +109,21 @@ class EdsBackendFactory implements FactoryInterface */ protected function createBackend(Connector $connector) { - $auth = $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService'); + $auth = $this->serviceLocator + ->get(\ZfcRbac\Service\AuthorizationService::class); $isGuest = !$auth->isGranted('access.EDSExtendedResults'); $session = new \Zend\Session\Container( - 'EBSCO', $this->serviceLocator->get('Zend\Session\SessionManager') + 'EBSCO', $this->serviceLocator->get(\Zend\Session\SessionManager::class) ); $backend = new Backend( $connector, $this->createRecordCollectionFactory(), - $this->serviceLocator->get('VuFind\Cache\Manager')->getCache('object'), + $this->serviceLocator->get(\VuFind\Cache\Manager::class) + ->getCache('object'), $session, $this->edsConfig, $isGuest ); - $backend->setAuthManager($this->serviceLocator->get('VuFind\Auth\Manager')); + $backend->setAuthManager( + $this->serviceLocator->get(\VuFind\Auth\Manager::class) + ); $backend->setLogger($this->logger); $backend->setQueryBuilder($this->createQueryBuilder()); return $backend; @@ -131,16 +136,13 @@ class EdsBackendFactory implements FactoryInterface */ protected function createConnector() { - $options = []; - $id = 'EDS'; - $key = 'EDS'; + $options = [ + 'timeout' => $this->edsConfig->General->timeout ?? 120 + ]; // Build HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient(); - $timeout = isset($this->edsConfig->General->timeout) - ? $this->edsConfig->General->timeout : 30; - $client->setOptions(['timeout' => $timeout]); - $connector = new Connector($id, $key, $options, $client); + $connector = new Connector($options, $client); $connector->setLogger($this->logger); return $connector; } @@ -163,7 +165,8 @@ class EdsBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('EDS'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php index e5cdf06beb824bce8061d06491ef5e076abd3477..d8da8b3a9ff9d70bced7253bea2cedce0a5d1999 100644 --- a/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/LibGuidesBackendFactory.php @@ -51,7 +51,7 @@ class LibGuidesBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -83,10 +83,11 @@ class LibGuidesBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configReader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $this->libGuidesConfig = $configReader->get('LibGuides'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -128,7 +129,7 @@ class LibGuidesBackendFactory implements FactoryInterface $baseUrl = $this->libGuidesConfig->General->baseUrl ?? null; // Build HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient($baseUrl); $timeout = $this->libGuidesConfig->General->timeout ?? 30; $client->setOptions(['timeout' => $timeout]); @@ -155,7 +156,8 @@ class LibGuidesBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('LibGuides'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php index 973e7f6f1931f6ca2eafec4404578a89e89dc302..335da7e50a1d529dc6fc34650d84969b06ba7a29 100644 --- a/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/Pazpar2BackendFactory.php @@ -30,6 +30,8 @@ namespace VuFind\Search\Factory; use Interop\Container\ContainerInterface; +use VuFindHttp\HttpService; + use VuFindSearch\Backend\Pazpar2\Backend; use VuFindSearch\Backend\Pazpar2\Connector; use VuFindSearch\Backend\Pazpar2\QueryBuilder; @@ -51,7 +53,7 @@ class Pazpar2BackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -83,10 +85,11 @@ class Pazpar2BackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $this->config = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class) ->get('Pazpar2'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -125,7 +128,7 @@ class Pazpar2BackendFactory implements FactoryInterface { $connector = new Connector( $this->config->General->base_url, - $this->serviceLocator->get('VuFindHttp\HttpService')->createClient() + $this->serviceLocator->get(HttpService::class)->createClient() ); $connector->setLogger($this->logger); return $connector; @@ -148,7 +151,8 @@ class Pazpar2BackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('Pazpar2'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php index 941d93cfe915bca5d157841a170291952fa70e8b..d3a57fb75ddb0401982059ecf88c2d63cec8e237 100644 --- a/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/PrimoBackendFactory.php @@ -40,6 +40,8 @@ use VuFindSearch\Backend\Primo\Response\RecordCollectionFactory; use Zend\ServiceManager\Factory\FactoryInterface; +use ZfcRbac\Service\AuthorizationService; + /** * Factory for Primo Central backends. * @@ -54,7 +56,7 @@ class PrimoBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -86,10 +88,11 @@ class PrimoBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configReader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $this->primoConfig = $configReader->get('Primo'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); @@ -146,7 +149,7 @@ class PrimoBackendFactory implements FactoryInterface : null; // Build HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient(); $timeout = isset($this->primoConfig->General->timeout) ? $this->primoConfig->General->timeout : 30; @@ -177,7 +180,8 @@ class PrimoBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('Primo'); $driver->setRawData($data); @@ -209,7 +213,7 @@ class PrimoBackendFactory implements FactoryInterface $this->primoConfig->Institutions ); $permHandler->setAuthorizationService( - $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService') + $this->serviceLocator->get(AuthorizationService::class) ); return $permHandler; } diff --git a/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php index 17dfa2c34317c00e7af3daa754bfc51aaf87fc97..c96fbe9e9fb22f3414db23061cf67ff4b9c738ba 100644 --- a/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/Search2BackendFactory.php @@ -39,6 +39,13 @@ namespace VuFind\Search\Factory; */ class Search2BackendFactory extends SolrDefaultBackendFactory { + /** + * Callback for creating a record driver. + * + * @var string + */ + protected $createRecordMethod = 'getSearch2Record'; + /** * Constructor */ diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php index df3086ddba7865f762eee59b10d02a586a90d2fd..eb31d8319da016135aceac3934726496fb409eb5 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrAuthBackendFactory.php @@ -74,7 +74,8 @@ class SolrAuthBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $factory = new RecordCollectionFactory([$manager, 'getSolrAuthRecord']); $backend->setRecordCollectionFactory($factory); return $backend; diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php index 2afeae41f39473c747ef212f1b72ba9c412e7afb..5d9b2232ae42952d87b82f6daee4b858cf9b4fa3 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrDefaultBackendFactory.php @@ -43,6 +43,13 @@ use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; */ class SolrDefaultBackendFactory extends AbstractSolrBackendFactory { + /** + * Method for creating a record driver. + * + * @var string + */ + protected $createRecordMethod = 'getSolrRecord'; + /** * Constructor */ @@ -76,8 +83,10 @@ class SolrDefaultBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); - $factory = new RecordCollectionFactory([$manager, 'getSolrRecord']); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); + $factory + = new RecordCollectionFactory([$manager, $this->createRecordMethod]); $backend->setRecordCollectionFactory($factory); return $backend; } diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php index ac30b4323d8f2db60491e8f888698ca1d9469cca..d26627f417bd18f4a450248dba8fddaeea48c40c 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrReservesBackendFactory.php @@ -64,7 +64,8 @@ class SolrReservesBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('SolrReserves'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php index b2cb3f8377df0bbda35276bed04ea3b64ca18d1e..a0e0fce2b4743fd899f91493cee9aadf911098ce 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SolrWebBackendFactory.php @@ -91,7 +91,8 @@ class SolrWebBackendFactory extends AbstractSolrBackendFactory protected function createBackend(Connector $connector) { $backend = parent::createBackend($connector); - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('SolrWeb'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php index 45f4811ee56023a6c4e783e97316eb5f99df55e8..2b52ce49ac872d6df05bdd854bc06b2dd9569817 100644 --- a/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/SummonBackendFactory.php @@ -52,7 +52,7 @@ class SummonBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -91,11 +91,12 @@ class SummonBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $configReader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configReader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $this->config = $configReader->get('config'); $this->summonConfig = $configReader->get('Summon'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -131,7 +132,7 @@ class SummonBackendFactory implements FactoryInterface ? $this->config->Summon->apiKey : null; // Build HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient(); $timeout = isset($this->summonConfig->General->timeout) ? $this->summonConfig->General->timeout : 30; @@ -150,7 +151,8 @@ class SummonBackendFactory implements FactoryInterface */ protected function isAuthed() { - return $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService') + return $this->serviceLocator + ->get(\ZfcRbac\Service\AuthorizationService::class) ->isGranted('access.SummonExtendedResults'); } @@ -177,7 +179,8 @@ class SummonBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $stripSnippets = !isset($this->summonConfig->General->snippets) || !$this->summonConfig->General->snippets; $callback = function ($data) use ($manager, $stripSnippets) { diff --git a/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php b/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php index 5888fae74ef4bfe8a133495684dc00865fc711d3..21bf2fd0559394a2feec7f2e52867d6c1141c525 100644 --- a/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/UrlQueryHelperFactory.php @@ -41,6 +41,13 @@ use VuFind\Search\UrlQueryHelper; */ class UrlQueryHelperFactory { + /** + * Name of class built by factory. + * + * @var string + */ + protected $helperClass = UrlQueryHelper::class; + /** * Extract default settings from the search parameters. * @@ -115,7 +122,7 @@ class UrlQueryHelperFactory if ($params->getPage() != 1) { $urlParams['page'] = $params->getPage(); } - $filters = $params->getFilters(); + $filters = $params->getRawFilters(); if (!empty($filters)) { $urlParams['filter'] = []; foreach ($filters as $field => $values) { @@ -158,7 +165,7 @@ class UrlQueryHelperFactory public function fromParams(Params $params, array $config = []) { $finalConfig = $this->addDefaultsToConfig($params, $config); - return new UrlQueryHelper( + return new $this->helperClass( $this->getUrlParams($params, $finalConfig), $params->getQuery(), $finalConfig diff --git a/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php index 92ac5ab10e2e5926f308afdc60dd7e79191a9856..a257f372f7cda1d3bdf3be53ae9e5309bd197570 100644 --- a/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php +++ b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php @@ -51,7 +51,7 @@ class WorldCatBackendFactory implements FactoryInterface /** * Logger. * - * @var Zend\Log\LoggerInterface + * @var \Zend\Log\LoggerInterface */ protected $logger; @@ -90,12 +90,13 @@ class WorldCatBackendFactory implements FactoryInterface public function __invoke(ContainerInterface $sm, $name, array $options = null) { $this->serviceLocator = $sm; - $this->config = $this->serviceLocator->get('VuFind\Config\PluginManager') + $this->config = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class) ->get('config'); $this->wcConfig = $this->serviceLocator - ->get('VuFind\Config\PluginManager')->get('WorldCat'); - if ($this->serviceLocator->has('VuFind\Log\Logger')) { - $this->logger = $this->serviceLocator->get('VuFind\Log\Logger'); + ->get(\VuFind\Config\PluginManager::class)->get('WorldCat'); + if ($this->serviceLocator->has(\VuFind\Log\Logger::class)) { + $this->logger = $this->serviceLocator->get(\VuFind\Log\Logger::class); } $connector = $this->createConnector(); $backend = $this->createBackend($connector); @@ -128,11 +129,9 @@ class WorldCatBackendFactory implements FactoryInterface ? $this->config->WorldCat->apiKey : null; $connectorOptions = isset($this->wcConfig->Connector) ? $this->wcConfig->Connector->toArray() : []; - $connector = new Connector( - $wsKey, - $this->serviceLocator->get('VuFindHttp\HttpService')->createClient(), - $connectorOptions - ); + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) + ->createClient(); + $connector = new Connector($wsKey, $client, $connectorOptions); $connector->setLogger($this->logger); return $connector; } @@ -156,7 +155,8 @@ class WorldCatBackendFactory implements FactoryInterface */ protected function createRecordCollectionFactory() { - $manager = $this->serviceLocator->get('VuFind\RecordDriver\PluginManager'); + $manager = $this->serviceLocator + ->get(\VuFind\RecordDriver\PluginManager::class); $callback = function ($data) use ($manager) { $driver = $manager->get('WorldCat'); $driver->setRawData($data); diff --git a/module/VuFind/src/VuFind/Search/Favorites/Results.php b/module/VuFind/src/VuFind/Search/Favorites/Results.php index e5159778756d5d36b53eb82f8596777efe7e512c..1d53a02c99eb1e171c9ca781f566ef785d86a3b9 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/Results.php +++ b/module/VuFind/src/VuFind/Search/Favorites/Results.php @@ -222,7 +222,7 @@ class Results extends BaseResults */ protected function getTagFilters() { - $filters = $this->getParams()->getFilters(); + $filters = $this->getParams()->getRawFilters(); return $filters['tags'] ?? []; } @@ -238,7 +238,7 @@ class Results extends BaseResults if ($this->list === false) { // Check the filters for a list ID, and load the corresponding object // if one is found: - $filters = $this->getParams()->getFilters(); + $filters = $this->getParams()->getRawFilters(); $listId = $filters['lists'][0] ?? null; $this->list = (null === $listId) ? null : $this->listTable->getExisting($listId); diff --git a/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php index cdb78e616aa1079d4769f1ab74cbccde642d5c69..fe13ff91d9f715728060d31da0b6659ccec2a7fc 100644 --- a/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php +++ b/module/VuFind/src/VuFind/Search/Favorites/ResultsFactory.php @@ -60,7 +60,7 @@ class ResultsFactory extends \VuFind\Search\Results\ResultsFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $tm = $container->get('VuFind\Db\Table\PluginManager'); + $tm = $container->get(\VuFind\Db\Table\PluginManager::class); $obj = parent::__invoke( $container, $requestedName, [$tm->get('Resource'), $tm->get('UserList')] diff --git a/module/VuFind/src/VuFind/Search/History.php b/module/VuFind/src/VuFind/Search/History.php index 2c8be04066057d351adca316b63bdd8d207aa515..8115263e6b2643cada22f2275a860d5014b8bd89 100644 --- a/module/VuFind/src/VuFind/Search/History.php +++ b/module/VuFind/src/VuFind/Search/History.php @@ -61,18 +61,28 @@ class History */ protected $resultsManager; + /** + * VuFind configuration + * + * @var \Zend\Config\Config + */ + protected $config; + /** * History constructor * * @param \VuFind\Db\Table\Search $searchTable Search table * @param string $sessionId Session ID * @param \VuFind\Search\Results\PluginManager $resultsManager Results manager + * @param \Zend\Config\Config $config Configuration */ - public function __construct($searchTable, $sessionId, $resultsManager) - { + public function __construct($searchTable, $sessionId, $resultsManager, + \Zend\Config\Config $config = null + ) { $this->searchTable = $searchTable; $this->sessionId = $sessionId; $this->resultsManager = $resultsManager; + $this->config = $config; } /** @@ -100,7 +110,7 @@ class History $searchHistory = $this->searchTable->getSearches($this->sessionId, $userId); // Loop through and sort the history - $saved = $unsaved = []; + $saved = $schedule = $unsaved = []; foreach ($searchHistory as $current) { $search = $current->getSearchObject()->deminify($this->resultsManager); if ($current->saved == 1) { @@ -108,8 +118,25 @@ class History } else { $unsaved[] = $search; } + if ($search->getOptions()->supportsScheduledSearch()) { + $schedule[$search->getSearchId()] = $current->notification_frequency; + } } - return compact('saved', 'unsaved'); + return compact('saved', 'schedule', 'unsaved'); + } + + /** + * Get a list of scheduling options (empty list if scheduling disabled). + * + * @return array + */ + public function getScheduleOptions() + { + if (!($this->config->Account->schedule_searches ?? false)) { + return []; + } + return $this->config->Account->scheduled_search_frequencies + ?? [0 => 'schedule_none', 1 => 'schedule_daily', 7 => 'schedule_weekly']; } } diff --git a/module/VuFind/src/VuFind/Search/HistoryFactory.php b/module/VuFind/src/VuFind/Search/HistoryFactory.php index ac418e347a83240839bdaadf078376955c80e87e..6c95e9d079cc9d9682bab8860ff7f92ea0115cd0 100644 --- a/module/VuFind/src/VuFind/Search/HistoryFactory.php +++ b/module/VuFind/src/VuFind/Search/HistoryFactory.php @@ -61,10 +61,12 @@ class HistoryFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $searchTable = $container->get('VuFind\Db\Table\PluginManager') + $searchTable = $container->get(\VuFind\Db\Table\PluginManager::class) ->get('Search'); - $resultsManager = $container->get('VuFind\Search\Results\PluginManager'); - $sessionId = $container->get('Zend\Session\SessionManager')->getId(); - return new $requestedName($searchTable, $sessionId, $resultsManager); + $resultsManager = $container + ->get(\VuFind\Search\Results\PluginManager::class); + $sessionId = $container->get(\Zend\Session\SessionManager::class)->getId(); + $cfg = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + return new $requestedName($searchTable, $sessionId, $resultsManager, $cfg); } } diff --git a/module/VuFind/src/VuFind/Search/Memory.php b/module/VuFind/src/VuFind/Search/Memory.php index bf7d3ee3614366fb1a1c98b6d849cc6ac3973ee0..f44ec6aa318a08eee865095c5791789267cb2371 100644 --- a/module/VuFind/src/VuFind/Search/Memory.php +++ b/module/VuFind/src/VuFind/Search/Memory.php @@ -154,18 +154,6 @@ class Memory } } - /** - * Deprecated alias for retrieveSearch, for legacy compatibility. - * - * @deprecated - * - * @return string|null - */ - public function retrieve() - { - return $this->retrieveSearch(); - } - /** * Retrieve a previous user parameter, if available. Return $default if * not found. diff --git a/module/VuFind/src/VuFind/Search/MemoryFactory.php b/module/VuFind/src/VuFind/Search/MemoryFactory.php index 1e19eb540be7e702869ea6a45416668a08bf0d64..a54cdebc95294ca48c78c3e6c34bfe4d870a2945 100644 --- a/module/VuFind/src/VuFind/Search/MemoryFactory.php +++ b/module/VuFind/src/VuFind/Search/MemoryFactory.php @@ -63,7 +63,7 @@ class MemoryFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } $session = new Container( - 'Search', $container->get('Zend\Session\SessionManager') + 'Search', $container->get(\Zend\Session\SessionManager::class) ); return new $requestedName($session); } diff --git a/module/VuFind/src/VuFind/Search/Minified.php b/module/VuFind/src/VuFind/Search/Minified.php index f4c5f77ff0d0786fc0749e9a8b04b38d88d80996..695cc3da72a609c1437a30812b5f83834340b842 100644 --- a/module/VuFind/src/VuFind/Search/Minified.php +++ b/module/VuFind/src/VuFind/Search/Minified.php @@ -107,7 +107,7 @@ class Minified // It would be nice to shorten filter fields too, but // it would be a nightmare to maintain. - $this->f = $searchObject->getParams()->getFilters(); + $this->f = $searchObject->getParams()->getRawFilters(); $this->hf = $searchObject->getParams()->getHiddenFilters(); } diff --git a/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php b/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php index e2f1da2d1db9ba6eb35b8bead07f6414e81cbe74..1c08cfddbca8e1fc8298e96e869bddc08b2232d2 100644 --- a/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php +++ b/module/VuFind/src/VuFind/Search/Options/OptionsFactory.php @@ -59,7 +59,7 @@ class OptionsFactory implements FactoryInterface array $options = null ) { return new $requestedName( - $container->get('VuFind\Config\PluginManager'), ...($options ?: []) + $container->get(\VuFind\Config\PluginManager::class), ...($options ?: []) ); } } diff --git a/module/VuFind/src/VuFind/Search/Options/PluginFactory.php b/module/VuFind/src/VuFind/Search/Options/PluginFactory.php index 586152dd6e3fc2d96cdef5c00c550c51b2e5e07d..46bbf7db4bef099bddc9fa7ec63f60f4776a5b6c 100644 --- a/module/VuFind/src/VuFind/Search/Options/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Options/PluginFactory.php @@ -64,6 +64,6 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory array $options = null ) { $class = $this->getClassName($requestedName); - return new $class($container->get('VuFind\Config\PluginManager')); + return new $class($container->get(\VuFind\Config\PluginManager::class)); } } diff --git a/module/VuFind/src/VuFind/Search/Options/PluginManager.php b/module/VuFind/src/VuFind/Search/Options/PluginManager.php index 7522a644e56c847511e5a9367eed02ba4d8ccc92..3a493178f1125e71e9d1ac240288f4240c4cc917 100644 --- a/module/VuFind/src/VuFind/Search/Options/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Options/PluginManager.php @@ -44,27 +44,28 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'browzine' => 'VuFind\Search\BrowZine\Options', - 'combined' => 'VuFind\Search\Combined\Options', - 'eds' => 'VuFind\Search\EDS\Options', - 'eit' => 'VuFind\Search\EIT\Options', - 'emptyset' => 'VuFind\Search\EmptySet\Options', - 'favorites' => 'VuFind\Search\Favorites\Options', - 'libguides' => 'VuFind\Search\LibGuides\Options', - 'mixedlist' => 'VuFind\Search\MixedList\Options', - 'pazpar2' => 'VuFind\Search\Pazpar2\Options', - 'primo' => 'VuFind\Search\Primo\Options', - 'search2' => 'VuFind\Search\Search2\Options', - 'solr' => 'VuFind\Search\Solr\Options', - 'solrauth' => 'VuFind\Search\SolrAuth\Options', - 'solrauthor' => 'VuFind\Search\SolrAuthor\Options', - 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Options', - 'solrcollection' => 'VuFind\Search\SolrCollection\Options', - 'solrreserves' => 'VuFind\Search\SolrReserves\Options', - 'solrweb' => 'VuFind\Search\SolrWeb\Options', - 'summon' => 'VuFind\Search\Summon\Options', - 'tags' => 'VuFind\Search\Tags\Options', - 'worldcat' => 'VuFind\Search\WorldCat\Options', + 'browzine' => \VuFind\Search\BrowZine\Options::class, + 'combined' => \VuFind\Search\Combined\Options::class, + 'eds' => \VuFind\Search\EDS\Options::class, + 'eit' => \VuFind\Search\EIT\Options::class, + 'emptyset' => \VuFind\Search\EmptySet\Options::class, + 'favorites' => \VuFind\Search\Favorites\Options::class, + 'libguides' => \VuFind\Search\LibGuides\Options::class, + 'mixedlist' => \VuFind\Search\MixedList\Options::class, + 'pazpar2' => \VuFind\Search\Pazpar2\Options::class, + 'primo' => \VuFind\Search\Primo\Options::class, + 'search2' => \VuFind\Search\Search2\Options::class, + 'search2collection' => \VuFind\Search\Search2\Options::class, + 'solr' => \VuFind\Search\Solr\Options::class, + 'solrauth' => \VuFind\Search\SolrAuth\Options::class, + 'solrauthor' => \VuFind\Search\SolrAuthor\Options::class, + 'solrauthorfacets' => \VuFind\Search\SolrAuthorFacets\Options::class, + 'solrcollection' => \VuFind\Search\SolrCollection\Options::class, + 'solrreserves' => \VuFind\Search\SolrReserves\Options::class, + 'solrweb' => \VuFind\Search\SolrWeb\Options::class, + 'summon' => \VuFind\Search\Summon\Options::class, + 'tags' => \VuFind\Search\Tags\Options::class, + 'worldcat' => \VuFind\Search\WorldCat\Options::class, ]; /** @@ -73,30 +74,29 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Search\BrowZine\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Combined\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\EDS\Options' => 'VuFind\Search\EDS\OptionsFactory', - 'VuFind\Search\EIT\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\EmptySet\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Favorites\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\LibGuides\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\MixedList\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Pazpar2\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Primo\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Search2\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Solr\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrAuth\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrAuthor\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrAuthorFacets\Options' => - 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrCollection\Options' => - 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrReserves\Options' => - 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\SolrWeb\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Summon\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\Tags\Options' => 'VuFind\Search\Options\OptionsFactory', - 'VuFind\Search\WorldCat\Options' => 'VuFind\Search\Options\OptionsFactory', + \VuFind\Search\BrowZine\Options::class => OptionsFactory::class, + \VuFind\Search\Combined\Options::class => OptionsFactory::class, + \VuFind\Search\EDS\Options::class => + \VuFind\Search\EDS\OptionsFactory::class, + \VuFind\Search\EIT\Options::class => OptionsFactory::class, + \VuFind\Search\EmptySet\Options::class => OptionsFactory::class, + \VuFind\Search\Favorites\Options::class => OptionsFactory::class, + \VuFind\Search\LibGuides\Options::class => OptionsFactory::class, + \VuFind\Search\MixedList\Options::class => OptionsFactory::class, + \VuFind\Search\Pazpar2\Options::class => OptionsFactory::class, + \VuFind\Search\Primo\Options::class => OptionsFactory::class, + \VuFind\Search\Search2\Options::class => OptionsFactory::class, + \VuFind\Search\Search2Collection\Options::class => OptionsFactory::class, + \VuFind\Search\Solr\Options::class => OptionsFactory::class, + \VuFind\Search\SolrAuth\Options::class => OptionsFactory::class, + \VuFind\Search\SolrAuthor\Options::class => OptionsFactory::class, + \VuFind\Search\SolrAuthorFacets\Options::class => OptionsFactory::class, + \VuFind\Search\SolrCollection\Options::class => OptionsFactory::class, + \VuFind\Search\SolrReserves\Options::class => OptionsFactory::class, + \VuFind\Search\SolrWeb\Options::class => OptionsFactory::class, + \VuFind\Search\Summon\Options::class => OptionsFactory::class, + \VuFind\Search\Tags\Options::class => OptionsFactory::class, + \VuFind\Search\WorldCat\Options::class => OptionsFactory::class, ]; /** @@ -111,7 +111,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Search\Options\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -123,6 +123,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Search\Base\Options'; + return \VuFind\Search\Base\Options::class; } } diff --git a/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php b/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php index 1cfdfb3bd1d7d6f830ba8ffb50a0690f5822afb6..5e1fe1b80dd550f196e6bc781d8b4d16b4841dc9 100644 --- a/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php +++ b/module/VuFind/src/VuFind/Search/Params/ParamsFactory.php @@ -60,9 +60,9 @@ class ParamsFactory implements FactoryInterface ) { // Replace trailing "Params" with "Options" to get the options service: $optionsService = preg_replace('/Params$/', 'Options', $requestedName); - $optionsObj = $container->get('VuFind\Search\Options\PluginManager') + $optionsObj = $container->get(\VuFind\Search\Options\PluginManager::class) ->get($optionsService); - $configLoader = $container->get('VuFind\Config\PluginManager'); + $configLoader = $container->get(\VuFind\Config\PluginManager::class); // Clone the options instance in case caller modifies it: return new $requestedName( clone $optionsObj, $configLoader, ...($options ?: []) diff --git a/module/VuFind/src/VuFind/Search/Params/PluginFactory.php b/module/VuFind/src/VuFind/Search/Params/PluginFactory.php index 4c4b7aa45ce8d09dae3b84697fd0c060e1ac2b15..8e6c5eea67bd6b8949965515cf9c8172f54133e3 100644 --- a/module/VuFind/src/VuFind/Search/Params/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Params/PluginFactory.php @@ -64,10 +64,10 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory array $extras = null ) { $optionsService = preg_replace('/Params$/', 'Options', $requestedName); - $options = $container->get('VuFind\Search\Options\PluginManager') + $options = $container->get(\VuFind\Search\Options\PluginManager::class) ->get($optionsService); $class = $this->getClassName($requestedName); - $configLoader = $container->get('VuFind\Config\PluginManager'); + $configLoader = $container->get(\VuFind\Config\PluginManager::class); // Clone the options instance in case caller modifies it: return new $class(clone $options, $configLoader, ...($extras ?: [])); } diff --git a/module/VuFind/src/VuFind/Search/Params/PluginManager.php b/module/VuFind/src/VuFind/Search/Params/PluginManager.php index d26e0db888d4850f152541f3f434f250925df82a..43e064a3f65821efb8e83d589ff46070151c0c97 100644 --- a/module/VuFind/src/VuFind/Search/Params/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Params/PluginManager.php @@ -44,27 +44,27 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'browzine' => 'VuFind\Search\BrowZine\Params', - 'combined' => 'VuFind\Search\Combined\Params', - 'eds' => 'VuFind\Search\EDS\Params', - 'eit' => 'VuFind\Search\EIT\Params', - 'emptyset' => 'VuFind\Search\EmptySet\Params', - 'favorites' => 'VuFind\Search\Favorites\Params', - 'libguides' => 'VuFind\Search\LibGuides\Params', - 'mixedlist' => 'VuFind\Search\MixedList\Params', - 'pazpar2' => 'VuFind\Search\Pazpar2\Params', - 'primo' => 'VuFind\Search\Primo\Params', - 'search2' => 'VuFind\Search\Search2\Params', - 'solr' => 'VuFind\Search\Solr\Params', - 'solrauth' => 'VuFind\Search\SolrAuth\Params', - 'solrauthor' => 'VuFind\Search\SolrAuthor\Params', - 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Params', - 'solrcollection' => 'VuFind\Search\SolrCollection\Params', - 'solrreserves' => 'VuFind\Search\SolrReserves\Params', - 'solrweb' => 'VuFind\Search\SolrWeb\Params', - 'summon' => 'VuFind\Search\Summon\Params', - 'tags' => 'VuFind\Search\Tags\Params', - 'worldcat' => 'VuFind\Search\WorldCat\Params', + 'browzine' => \VuFind\Search\BrowZine\Params::class, + 'combined' => \VuFind\Search\Combined\Params::class, + 'eds' => \VuFind\Search\EDS\Params::class, + 'eit' => \VuFind\Search\EIT\Params::class, + 'emptyset' => \VuFind\Search\EmptySet\Params::class, + 'favorites' => \VuFind\Search\Favorites\Params::class, + 'libguides' => \VuFind\Search\LibGuides\Params::class, + 'mixedlist' => \VuFind\Search\MixedList\Params::class, + 'pazpar2' => \VuFind\Search\Pazpar2\Params::class, + 'primo' => \VuFind\Search\Primo\Params::class, + 'search2' => \VuFind\Search\Search2\Params::class, + 'solr' => \VuFind\Search\Solr\Params::class, + 'solrauth' => \VuFind\Search\SolrAuth\Params::class, + 'solrauthor' => \VuFind\Search\SolrAuthor\Params::class, + 'solrauthorfacets' => \VuFind\Search\SolrAuthorFacets\Params::class, + 'solrcollection' => \VuFind\Search\SolrCollection\Params::class, + 'solrreserves' => \VuFind\Search\SolrReserves\Params::class, + 'solrweb' => \VuFind\Search\SolrWeb\Params::class, + 'summon' => \VuFind\Search\Summon\Params::class, + 'tags' => \VuFind\Search\Tags\Params::class, + 'worldcat' => \VuFind\Search\WorldCat\Params::class, ]; /** @@ -73,30 +73,29 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Search\BrowZine\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Combined\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\EDS\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\EIT\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\EmptySet\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Favorites\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\LibGuides\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\MixedList\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Pazpar2\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Primo\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Search2\Params' => 'VuFind\Search\Solr\ParamsFactory', - 'VuFind\Search\Solr\Params' => 'VuFind\Search\Solr\ParamsFactory', - 'VuFind\Search\SolrAuth\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\SolrAuthor\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\SolrAuthorFacets\Params' => - 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\SolrCollection\Params' => - 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\SolrReserves\Params' => - 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\SolrWeb\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Summon\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\Tags\Params' => 'VuFind\Search\Params\ParamsFactory', - 'VuFind\Search\WorldCat\Params' => 'VuFind\Search\Params\ParamsFactory', + \VuFind\Search\BrowZine\Params::class => ParamsFactory::class, + \VuFind\Search\Combined\Params::class => ParamsFactory::class, + \VuFind\Search\EDS\Params::class => ParamsFactory::class, + \VuFind\Search\EIT\Params::class => ParamsFactory::class, + \VuFind\Search\EmptySet\Params::class => ParamsFactory::class, + \VuFind\Search\Favorites\Params::class => ParamsFactory::class, + \VuFind\Search\LibGuides\Params::class => ParamsFactory::class, + \VuFind\Search\MixedList\Params::class => ParamsFactory::class, + \VuFind\Search\Pazpar2\Params::class => ParamsFactory::class, + \VuFind\Search\Primo\Params::class => ParamsFactory::class, + \VuFind\Search\Search2\Params::class => + \VuFind\Search\Solr\ParamsFactory::class, + \VuFind\Search\Solr\Params::class => + \VuFind\Search\Solr\ParamsFactory::class, + \VuFind\Search\SolrAuth\Params::class => ParamsFactory::class, + \VuFind\Search\SolrAuthor\Params::class => ParamsFactory::class, + \VuFind\Search\SolrAuthorFacets\Params::class => ParamsFactory::class, + \VuFind\Search\SolrCollection\Params::class => ParamsFactory::class, + \VuFind\Search\SolrReserves\Params::class => ParamsFactory::class, + \VuFind\Search\SolrWeb\Params::class => ParamsFactory::class, + \VuFind\Search\Summon\Params::class => ParamsFactory::class, + \VuFind\Search\Tags\Params::class => ParamsFactory::class, + \VuFind\Search\WorldCat\Params::class => ParamsFactory::class, ]; /** @@ -115,7 +114,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager // we are building a brand new object. $this->sharedByDefault = false; - $this->addAbstractFactory('VuFind\Search\Params\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -127,6 +126,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Search\Base\Params'; + return \VuFind\Search\Base\Params::class; } } diff --git a/module/VuFind/src/VuFind/Search/Primo/Params.php b/module/VuFind/src/VuFind/Search/Primo/Params.php index 5338d197dcb08fb752cc32182dc6340f3aef9aa8..b5643ebd84c2ec5ecb7a41ebf55e705eab304dd8 100644 --- a/module/VuFind/src/VuFind/Search/Primo/Params.php +++ b/module/VuFind/src/VuFind/Search/Primo/Params.php @@ -40,6 +40,23 @@ use VuFindSearch\ParamBag; */ class Params extends \VuFind\Search\Base\Params { + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections + = ['Advanced_Facets', 'FacetsTop', 'Facets']; + + /** + * Config sections to search for checkbox facet labels if no override + * configuration is set. + * + * @var array + */ + protected $defaultFacetLabelCheckboxSections = ['CheckboxFacets']; + /** * Create search backend parameters for advanced features. * @@ -54,11 +71,7 @@ class Params extends \VuFind\Search\Base\Params $sort = $this->getSort(); $finalSort = ($sort == 'relevance') ? null : $sort; $backendParams->set('sort', $finalSort); - $filterList = array_merge( - $this->getHiddenFilters(), - $this->filterList - ); - $backendParams->set('filterList', $filterList); + $backendParams->set('filterList', $this->getFilterSettings()); return $backendParams; } @@ -101,20 +114,29 @@ class Params extends \VuFind\Search\Base\Params } /** - * Load all available facet settings. This is mainly useful for showing - * appropriate labels when an existing search has multiple filters associated - * with it. - * - * @param string $preferredSection Section to favor when loading settings; if - * multiple sections contain the same facet, this section's description will - * be favored. + * Return the current filters as an array * - * @return void + * @return array */ - public function activateAllFacets($preferredSection = false) + public function getFilterSettings() { - $this->initFacetList('Facets', 'Results_Settings'); - $this->initFacetList('Advanced_Facets', 'Advanced_Facet_Settings'); - $this->initCheckboxFacets(); + $result = []; + $filterList = array_merge( + $this->getHiddenFilters(), + $this->filterList + ); + foreach ($filterList as $field => $filter) { + $facetOp = 'AND'; + $prefix = substr($field, 0, 1); + if ('~' === $prefix || '-' === $prefix) { + $facetOp = '~' === $prefix ? 'OR' : 'NOT'; + $field = substr($field, 1); + } + $result[$field] = [ + 'facetOp' => $facetOp, + 'values' => $filter + ]; + } + return $result; } } diff --git a/module/VuFind/src/VuFind/Search/Primo/Results.php b/module/VuFind/src/VuFind/Search/Primo/Results.php index 6565d7100f06ddd3b4598f3d86c636df79b7aa1b..f2fac9cc3863e16a774b182d3e594c723f332319 100644 --- a/module/VuFind/src/VuFind/Search/Primo/Results.php +++ b/module/VuFind/src/VuFind/Search/Primo/Results.php @@ -56,6 +56,7 @@ class Results extends \VuFind\Search\Base\Results $this->responseFacets = $collection->getFacets(); $this->resultTotal = $collection->getTotal(); + $this->errors = $collection->getErrors(); // Construct record drivers for all the items in the response: $this->results = $collection->getRecords(); @@ -100,7 +101,9 @@ class Results extends \VuFind\Search\Base\Results 'displayText' => $displayText, 'isApplied' => $this->getParams()->hasFilter("$field:" . $value), - 'operator' => 'AND', 'count' => $count + 'operator' => + $this->getParams()->getFacetOperator($field), + 'count' => $count ]; } diff --git a/module/VuFind/src/VuFind/Search/Results/PluginFactory.php b/module/VuFind/src/VuFind/Search/Results/PluginFactory.php index 022d9c17d8960f7d8bdbcaa4a31e8195072d0ca6..3466c884e4c7da7b98c39c01fba78763e6f3eea5 100644 --- a/module/VuFind/src/VuFind/Search/Results/PluginFactory.php +++ b/module/VuFind/src/VuFind/Search/Results/PluginFactory.php @@ -64,10 +64,10 @@ class PluginFactory extends \VuFind\ServiceManager\AbstractPluginFactory array $extras = null ) { $paramsService = preg_replace('/Results$/', 'Params', $requestedName); - $params = $container->get('VuFind\Search\Params\PluginManager') + $params = $container->get(\VuFind\Search\Params\PluginManager::class) ->get($paramsService); - $searchService = $container->get('VuFindSearch\Service'); - $recordLoader = $container->get('VuFind\Record\Loader'); + $searchService = $container->get(\VuFindSearch\Service::class); + $recordLoader = $container->get(\VuFind\Record\Loader::class); $class = $this->getClassName($requestedName); return new $class( $params, $searchService, $recordLoader, ...($extras ?: []) diff --git a/module/VuFind/src/VuFind/Search/Results/PluginManager.php b/module/VuFind/src/VuFind/Search/Results/PluginManager.php index 4585f59a0801c6ca147f9c0e5ddb5d3ca0ffc548..edc227d40ef6abc956d968b9824def5c91861e8b 100644 --- a/module/VuFind/src/VuFind/Search/Results/PluginManager.php +++ b/module/VuFind/src/VuFind/Search/Results/PluginManager.php @@ -44,27 +44,28 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'browzine' => 'VuFind\Search\BrowZine\Results', - 'combined' => 'VuFind\Search\Combined\Results', - 'eds' => 'VuFind\Search\EDS\Results', - 'eit' => 'VuFind\Search\EIT\Results', - 'emptyset' => 'VuFind\Search\EmptySet\Results', - 'favorites' => 'VuFind\Search\Favorites\Results', - 'libguides' => 'VuFind\Search\LibGuides\Results', - 'mixedlist' => 'VuFind\Search\MixedList\Results', - 'pazpar2' => 'VuFind\Search\Pazpar2\Results', - 'primo' => 'VuFind\Search\Primo\Results', - 'search2' => 'VuFind\Search\Search2\Results', - 'solr' => 'VuFind\Search\Solr\Results', - 'solrauth' => 'VuFind\Search\SolrAuth\Results', - 'solrauthor' => 'VuFind\Search\SolrAuthor\Results', - 'solrauthorfacets' => 'VuFind\Search\SolrAuthorFacets\Results', - 'solrcollection' => 'VuFind\Search\SolrCollection\Results', - 'solrreserves' => 'VuFind\Search\SolrReserves\Results', - 'solrweb' => 'VuFind\Search\SolrWeb\Results', - 'summon' => 'VuFind\Search\Summon\Results', - 'tags' => 'VuFind\Search\Tags\Results', - 'worldcat' => 'VuFind\Search\WorldCat\Results', + 'browzine' => \VuFind\Search\BrowZine\Results::class, + 'combined' => \VuFind\Search\Combined\Results::class, + 'eds' => \VuFind\Search\EDS\Results::class, + 'eit' => \VuFind\Search\EIT\Results::class, + 'emptyset' => \VuFind\Search\EmptySet\Results::class, + 'favorites' => \VuFind\Search\Favorites\Results::class, + 'libguides' => \VuFind\Search\LibGuides\Results::class, + 'mixedlist' => \VuFind\Search\MixedList\Results::class, + 'pazpar2' => \VuFind\Search\Pazpar2\Results::class, + 'primo' => \VuFind\Search\Primo\Results::class, + 'search2' => \VuFind\Search\Search2\Results::class, + 'search2collection' => \VuFind\Search\Search2Collection\Results::class, + 'solr' => \VuFind\Search\Solr\Results::class, + 'solrauth' => \VuFind\Search\SolrAuth\Results::class, + 'solrauthor' => \VuFind\Search\SolrAuthor\Results::class, + 'solrauthorfacets' => \VuFind\Search\SolrAuthorFacets\Results::class, + 'solrcollection' => \VuFind\Search\SolrCollection\Results::class, + 'solrreserves' => \VuFind\Search\SolrReserves\Results::class, + 'solrweb' => \VuFind\Search\SolrWeb\Results::class, + 'summon' => \VuFind\Search\Summon\Results::class, + 'tags' => \VuFind\Search\Tags\Results::class, + 'worldcat' => \VuFind\Search\WorldCat\Results::class, ]; /** @@ -73,31 +74,32 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Search\BrowZine\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Combined\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\EDS\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\EIT\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\EmptySet\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Favorites\Results' => - 'VuFind\Search\Favorites\ResultsFactory', - 'VuFind\Search\LibGuides\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\MixedList\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Pazpar2\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Primo\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Search2\Results' => 'VuFind\Search\Search2\ResultsFactory', - 'VuFind\Search\Solr\Results' => 'VuFind\Search\Solr\ResultsFactory', - 'VuFind\Search\SolrAuth\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\SolrAuthor\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\SolrAuthorFacets\Results' => - 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\SolrCollection\Results' => - 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\SolrReserves\Results' => - 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\SolrWeb\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Summon\Results' => 'VuFind\Search\Results\ResultsFactory', - 'VuFind\Search\Tags\Results' => 'VuFind\Search\Tags\ResultsFactory', - 'VuFind\Search\WorldCat\Results' => 'VuFind\Search\Results\ResultsFactory', + \VuFind\Search\BrowZine\Results::class => ResultsFactory::class, + \VuFind\Search\Combined\Results::class => ResultsFactory::class, + \VuFind\Search\EDS\Results::class => ResultsFactory::class, + \VuFind\Search\EIT\Results::class => ResultsFactory::class, + \VuFind\Search\EmptySet\Results::class => ResultsFactory::class, + \VuFind\Search\Favorites\Results::class => + \VuFind\Search\Favorites\ResultsFactory::class, + \VuFind\Search\LibGuides\Results::class => ResultsFactory::class, + \VuFind\Search\MixedList\Results::class => ResultsFactory::class, + \VuFind\Search\Pazpar2\Results::class => ResultsFactory::class, + \VuFind\Search\Primo\Results::class => ResultsFactory::class, + \VuFind\Search\Search2\Results::class => + \VuFind\Search\Search2\ResultsFactory::class, + \VuFind\Search\Search2Collection\Results::class => ResultsFactory::class, + \VuFind\Search\Solr\Results::class => + \VuFind\Search\Solr\ResultsFactory::class, + \VuFind\Search\SolrAuth\Results::class => ResultsFactory::class, + \VuFind\Search\SolrAuthor\Results::class => ResultsFactory::class, + \VuFind\Search\SolrAuthorFacets\Results::class => ResultsFactory::class, + \VuFind\Search\SolrCollection\Results::class => ResultsFactory::class, + \VuFind\Search\SolrReserves\Results::class => ResultsFactory::class, + \VuFind\Search\SolrWeb\Results::class => ResultsFactory::class, + \VuFind\Search\Summon\Results::class => ResultsFactory::class, + \VuFind\Search\Tags\Results::class => + \VuFind\Search\Tags\ResultsFactory::class, + \VuFind\Search\WorldCat\Results::class => ResultsFactory::class, ]; /** @@ -116,7 +118,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager // we are building a brand new object. $this->sharedByDefault = false; - $this->addAbstractFactory('VuFind\Search\Results\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -128,6 +130,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Search\Base\Results'; + return \VuFind\Search\Base\Results::class; } } diff --git a/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php index fe05add028f170b2a9ee03e06f06b8b42974e739..cddcfeb579755b181a3e3af869620d4d81cb97cd 100644 --- a/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php +++ b/module/VuFind/src/VuFind/Search/Results/ResultsFactory.php @@ -28,6 +28,7 @@ namespace VuFind\Search\Results; use Interop\Container\ContainerInterface; +use VuFind\Search\Factory\UrlQueryHelperFactory; use Zend\ServiceManager\Factory\FactoryInterface; /** @@ -60,12 +61,16 @@ class ResultsFactory implements FactoryInterface ) { // Replace trailing "Results" with "Params" to get the params service: $paramsService = preg_replace('/Results$/', 'Params', $requestedName); - $params = $container->get('VuFind\Search\Params\PluginManager') + $params = $container->get(\VuFind\Search\Params\PluginManager::class) ->get($paramsService); - $searchService = $container->get('VuFindSearch\Service'); - $recordLoader = $container->get('VuFind\Record\Loader'); - return new $requestedName( + $searchService = $container->get(\VuFindSearch\Service::class); + $recordLoader = $container->get(\VuFind\Record\Loader::class); + $results = new $requestedName( $params, $searchService, $recordLoader, ...($options ?: []) ); + $results->setUrlQueryHelperFactory( + $container->get(UrlQueryHelperFactory::class) + ); + return $results; } } diff --git a/module/VuFind/src/VuFind/Search/Search2/Params.php b/module/VuFind/src/VuFind/Search/Search2/Params.php index a139cb3f3404dbaf4c4cf38f00abc67fb3ea013a..fb3530a5747a8c5eca13d5a98b56ed4bb477a937 100644 --- a/module/VuFind/src/VuFind/Search/Search2/Params.php +++ b/module/VuFind/src/VuFind/Search/Search2/Params.php @@ -39,6 +39,17 @@ namespace VuFind\Search\Search2; */ class Params extends \VuFind\Search\Solr\Params { + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections = [ + 'Advanced_Facets', 'HomePage_Facets', 'ResultsTop', 'Results', + 'ExtraFacetLabels' + ]; + /** * Initialize facet settings for the advanced search screen. * diff --git a/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php index 11d75b5bfb02dff20485d29f9a47ada40f342c3a..52716e06bcc6537ec5ae9740c49792b1d4d98191 100644 --- a/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php +++ b/module/VuFind/src/VuFind/Search/Search2/ResultsFactory.php @@ -58,7 +58,8 @@ class ResultsFactory extends \VuFind\Search\Results\ResultsFactory array $options = null ) { $solr = parent::__invoke($container, $requestedName, $options); - $config = $container->get('VuFind\Config\PluginManager')->get('Search2'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('Search2'); $solr->setSpellingProcessor( new \VuFind\Search\Solr\SpellingProcessor($config->Spelling ?? null) ); diff --git a/module/VuFind/src/VuFind/Search/Search2Collection/Options.php b/module/VuFind/src/VuFind/Search/Search2Collection/Options.php new file mode 100644 index 0000000000000000000000000000000000000000..57fa7b7f8e743652063ed3e4d98bbd5369a23b78 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2Collection/Options.php @@ -0,0 +1,41 @@ +<?php +/** + * Search2 Collection aspect of the Search Multi-class (Options) + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Search\Search2Collection; + +/** + * Search2 Collection Search Options + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli SillanpäÃ¤ <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Options extends \VuFind\Search\SolrCollection\Options +{ +} diff --git a/module/VuFind/src/VuFind/Search/Search2Collection/Params.php b/module/VuFind/src/VuFind/Search/Search2Collection/Params.php new file mode 100644 index 0000000000000000000000000000000000000000..dd6e55b2c6ff06a65540e1d4d866d7f8d4c4b799 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2Collection/Params.php @@ -0,0 +1,41 @@ +<?php +/** + * Search2 Collection aspect of the Search Multi-class (Params) + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Search\Search2Collection; + +/** + * Search2 Collection Search Options + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Params extends \VuFind\Search\SolrCollection\Params +{ +} diff --git a/module/VuFind/src/VuFind/Search/Search2Collection/Results.php b/module/VuFind/src/VuFind/Search/Search2Collection/Results.php new file mode 100644 index 0000000000000000000000000000000000000000..a3bbb5962bc0a8910d2ba1e26b116f221c66b557 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Search2Collection/Results.php @@ -0,0 +1,47 @@ +<?php +/** + * Search2 Collection aspect of the Search Multi-class (Results) + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\Search\Search2Collection; + +/** + * Search2 Collection Search Options + * + * @category VuFind + * @package Search_SolrAuthor + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class Results extends \VuFind\Search\SolrCollection\Results +{ + /** + * Search backend identifiers. + * + * @var string + */ + protected $backendId = 'Search2Collection'; +} diff --git a/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php b/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php index d7ed6bd3b979aa765b157beddf2b7a422edbab83..135bd7e5942d85cbd8a22c83ee7ed1f8c5145c84 100644 --- a/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php +++ b/module/VuFind/src/VuFind/Search/SearchRunnerFactory.php @@ -63,7 +63,7 @@ class SearchRunnerFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Search\Results\PluginManager'), + $container->get(\VuFind\Search\Results\PluginManager::class), new EventManager($container->get('SharedEventManager')) ); } diff --git a/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php b/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php index 932742f3da709eae517fe5842c8e7d9f169fef4a..136ab2b5d645bb72318b674163c0c5c02b3ab76d 100644 --- a/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php +++ b/module/VuFind/src/VuFind/Search/SearchTabsHelperFactory.php @@ -61,7 +61,8 @@ class SearchTabsHelperFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $tabConfig = isset($config->SearchTabs) ? $config->SearchTabs->toArray() : []; $filterConfig = isset($config->SearchTabsFilters) @@ -69,7 +70,7 @@ class SearchTabsHelperFactory implements FactoryInterface $permissionConfig = isset($config->SearchTabsPermissions) ? $config->SearchTabsPermissions->toArray() : []; return new $requestedName( - $container->get('VuFind\Search\Results\PluginManager'), + $container->get(\VuFind\Search\Results\PluginManager::class), $tabConfig, $filterConfig, $container->get('Application')->getRequest(), $permissionConfig ); diff --git a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php index 37869df1ff74d68d2622b05ac9927dab2487c771..4c987a65edf1a67af382a5c69a14a72d83cd674c 100644 --- a/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/DeduplicationListener.php @@ -183,7 +183,7 @@ class DeduplicationListener */ protected function fetchLocalRecords($event) { - $config = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class); $searchConfig = $config->get($this->searchConfig); $dataSourceConfig = $config->get($this->dataSourceConfig); $recordSources = !empty($searchConfig->Records->sources) diff --git a/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php b/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php index 9edfd6ba3d4e7bb4757ddade5d2b403e108255f3..61a093067437577845c807a346d01265d36d97e9 100644 --- a/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php +++ b/module/VuFind/src/VuFind/Search/Solr/FacetCacheFactory.php @@ -51,9 +51,9 @@ class FacetCacheFactory extends \VuFind\Search\Base\FacetCacheFactory */ protected function getResults(ContainerInterface $container, $name) { - $filters = $container->get('VuFind\Search\SearchTabsHelper') + $filters = $container->get(\VuFind\Search\SearchTabsHelper::class) ->getHiddenFilters($name); - $results = $container->get('VuFind\Search\Results\PluginManager') + $results = $container->get(\VuFind\Search\Results\PluginManager::class) ->get($name); $params = $results->getParams(); foreach ($filters as $key => $subFilters) { diff --git a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php index 41ded201203ecd17b50a536852a029f1d0b722f7..d67c4df5fa1d5ace6642d86ef2dd2c6e6b144b1f 100644 --- a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php +++ b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetHelper.php @@ -45,13 +45,39 @@ class HierarchicalFacetHelper * Helper method for building hierarchical facets: * Sort a facet list according to the given sort order * - * @param array $facetList Facet list returned from Solr - * @param bool $topLevel Whether to sort only top level + * @param array $facetList Facet list returned from Solr + * @param boolean|string $order Sort order: + * - true|top sort top level alphabetically and the rest by count + * - false|all sort all levels alphabetically + * - count sort all levels by count * * @return void */ - public function sortFacetList(&$facetList, $topLevel) + public function sortFacetList(&$facetList, $order = null) { + // We need a boolean flag indicating whether or not to sort only the top + // level of the hierarchy. If we received a string configuration option, + // we should set the flag accordingly (boolean values of $order are + // supported for backward compatibility). + $topLevel = $order ?? 'count'; + if (is_string($topLevel)) { + switch (strtolower(trim($topLevel))) { + case 'top': + $topLevel = true; + break; + case 'all': + $topLevel = false; + break; + case '': + case 'count': + // At present, we assume the incoming list is already sorted by + // count, so no further action is needed. If in future we need + // to support re-sorting an arbitrary list, rather than simply + // operating on raw Solr values, we may need to implement logic. + return; + } + } + // Parse level from each facet value so that the sort function // can run faster foreach ($facetList as &$facetItem) { diff --git a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php index ac99cd2371fb521f101f3a63d5d45af46de887f3..96052f2c2b5b670b9d485644e019221d75387875 100644 --- a/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php +++ b/module/VuFind/src/VuFind/Search/Solr/HierarchicalFacetListener.php @@ -107,10 +107,10 @@ class HierarchicalFacetListener $this->backend = $backend; $this->serviceLocator = $serviceLocator; - $config = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $config = $this->serviceLocator->get(\VuFind\Config\PluginManager::class); $this->facetConfig = $config->get($facetConfig); $this->facetHelper = $this->serviceLocator - ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + ->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class); $specialFacets = $this->facetConfig->SpecialFacets; $this->displayStyles diff --git a/module/VuFind/src/VuFind/Search/Solr/Options.php b/module/VuFind/src/VuFind/Search/Solr/Options.php index 1a1bfc43f276c73ea7ff8c4cb9ea202205d88efe..de96e13cdb6affb583d951bb09b452b35fcb5998 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Options.php +++ b/module/VuFind/src/VuFind/Search/Solr/Options.php @@ -46,8 +46,7 @@ class Options extends \VuFind\Search\Base\Options * @var array */ protected $facetSortOptions = [ - 'count' => 'sort_count', - 'index' => 'sort_alphabetic' + '*' => ['count' => 'sort_count', 'index' => 'sort_alphabetic'] ]; /** @@ -209,13 +208,7 @@ class Options extends \VuFind\Search\Base\Options } // Load autocomplete preferences: - if (isset($searchSettings->Autocomplete->enabled)) { - $this->autocompleteEnabled = $searchSettings->Autocomplete->enabled; - } - if (isset($searchSettings->Autocomplete->auto_submit)) { - $this->autocompleteAutoSubmit - = $searchSettings->Autocomplete->auto_submit; - } + $this->configureAutocomplete($searchSettings); // Load shard settings if (isset($searchSettings->IndexShards) @@ -308,4 +301,15 @@ class Options extends \VuFind\Search\Base\Options { return $this->hierarchicalFacetSeparators; } + + /** + * Does this search backend support scheduled searching? + * + * @return bool + */ + public function supportsScheduledSearch() + { + // Solr supports this! + return true; + } } diff --git a/module/VuFind/src/VuFind/Search/Solr/Params.php b/module/VuFind/src/VuFind/Search/Solr/Params.php index 06ac6f635efc49dbaa480855a60058466b7a5bc9..8f9a07e524efccf69c5451d15fb8aa0eb01ffae3 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Params.php +++ b/module/VuFind/src/VuFind/Search/Solr/Params.php @@ -42,6 +42,22 @@ class Params extends \VuFind\Search\Base\Params { use \VuFind\Search\Params\FacetLimitTrait; + /** + * Search with facet.contains + * cf. https://lucene.apache.org/solr/guide/7_3/faceting.html + * + * @var string + */ + protected $facetContains = null; + + /** + * Ignore Case when using facet.contains + * cf. https://lucene.apache.org/solr/guide/7_3/faceting.html + * + * @var bool + */ + protected $facetContainsIgnoreCase = null; + /** * Offset for facet results * @@ -84,6 +100,23 @@ class Params extends \VuFind\Search\Base\Params */ protected $facetHelper; + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections + = ['Advanced', 'HomePage', 'ResultsTop', 'Results', 'ExtraFacetLabels']; + + /** + * Config sections to search for checkbox facet labels if no override + * configuration is set. + * + * @var array + */ + protected $defaultFacetLabelCheckboxSections = ['CheckboxFacets']; + /** * Constructor * @@ -103,9 +136,6 @@ class Params extends \VuFind\Search\Base\Params if (isset($config->LegacyFields)) { $this->facetAliases = $config->LegacyFields->toArray(); } - if (isset($config->ExtraFacetLabels)) { - $this->extraFacetLabels = $config->ExtraFacetLabels->toArray(); - } if (isset($config->Results_Settings->sorted_by_index) && count($config->Results_Settings->sorted_by_index) > 0 ) { @@ -182,6 +212,13 @@ class Params extends \VuFind\Search\Base\Params } $facetSet['field'][] = $facetField; } + if ($this->facetContains != null) { + $facetSet['contains'] = $this->facetContains; + } + if ($this->facetContainsIgnoreCase != null) { + $facetSet['contains.ignoreCase'] + = $this->facetContainsIgnoreCase ? 'true' : 'false'; + } if ($this->facetOffset != null) { $facetSet['offset'] = $this->facetOffset; } @@ -218,6 +255,30 @@ class Params extends \VuFind\Search\Base\Params } } + /** + * Set Facet Contains + * + * @param string $p the new contains value + * + * @return void + */ + public function setFacetContains($p) + { + $this->facetContains = $p; + } + + /** + * Set Facet Contains Ignore Case + * + * @param bool $val the new boolean value + * + * @return void + */ + public function setFacetContainsIgnoreCase($val) + { + $this->facetContainsIgnoreCase = $val; + } + /** * Set Facet Offset * @@ -307,44 +368,6 @@ class Params extends \VuFind\Search\Base\Params } } - /** - * Initialize facet settings for the standard search screen. - * - * @return void - */ - public function initBasicFacets() - { - $this->initFacetList('ResultsTop', 'Results_Settings'); - $this->initFacetList('Results', 'Results_Settings'); - } - - /** - * Load all available facet settings. This is mainly useful for showing - * appropriate labels when an existing search has multiple filters associated - * with it. - * - * @param string $preferredSection Section to favor when loading settings; if - * multiple sections contain the same facet, this section's description will - * be favored. - * - * @return void - */ - public function activateAllFacets($preferredSection = false) - { - // Based on preference, change the order of initialization to make sure - // that preferred facet labels come in last. - if ($preferredSection == 'Advanced') { - $this->initHomePageFacets(); - $this->initBasicFacets(); - $this->initAdvancedFacets(); - } else { - $this->initHomePageFacets(); - $this->initAdvancedFacets(); - $this->initBasicFacets(); - } - $this->initCheckboxFacets(); - } - /** * Add filters to the object based on values found in the request object. * diff --git a/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php b/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php index 5e6fa1aed652826cd33bb0f5c8640c933c9deeea..a8acce3f6f8db7352742a76139a27a5c4aa9a9ff 100644 --- a/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php +++ b/module/VuFind/src/VuFind/Search/Solr/ParamsFactory.php @@ -60,7 +60,8 @@ class ParamsFactory extends \VuFind\Search\Params\ParamsFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $helper = $container->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + $helper = $container + ->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class); return parent::__invoke($container, $requestedName, [$helper]); } } diff --git a/module/VuFind/src/VuFind/Search/Solr/Results.php b/module/VuFind/src/VuFind/Search/Solr/Results.php index 83bdb021061652584ecd4297b3a3d3668d449792..5d725772acd6a9089a91fcd85363a948cdc9966c 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Results.php +++ b/module/VuFind/src/VuFind/Search/Solr/Results.php @@ -71,6 +71,15 @@ class Results extends \VuFind\Search\Base\Results */ protected $spellingProcessor = null; + /** + * CursorMark used for deep paging (e.g. OAI-PMH Server). + * Set to '*' to start paging a request and use the new value returned from the + * search request for the next request. + * + * @var null|string + */ + protected $cursorMark = null; + /** * Get spelling processor. * @@ -96,6 +105,28 @@ class Results extends \VuFind\Search\Base\Results $this->spellingProcessor = $processor; } + /** + * Get cursorMark. + * + * @return null|string + */ + public function getCursorMark() + { + return $this->cursorMark; + } + + /** + * Set cursorMark. + * + * @param null|string $cursorMark New cursor mark + * + * @return void + */ + public function setCursorMark($cursorMark) + { + $this->cursorMark = $cursorMark; + } + /** * Support method for performAndProcessSearch -- perform a search based on the * parameters passed to the object. @@ -109,6 +140,13 @@ class Results extends \VuFind\Search\Base\Results $offset = $this->getStartRecord() - 1; $params = $this->getParams()->getBackendParameters(); $searchService = $this->getSearchService(); + $cursorMark = $this->getCursorMark(); + if (null !== $cursorMark) { + $params->set('cursorMark', '' === $cursorMark ? '*' : $cursorMark); + // Override any default timeAllowed since it cannot be used with + // cursorMark + $params->set('timeAllowed', -1); + } try { $collection = $searchService @@ -137,6 +175,11 @@ class Results extends \VuFind\Search\Base\Results $this->suggestions = $this->getSpellingProcessor() ->getSuggestions($spellcheck, $this->getParams()->getQuery()); + // Update current cursorMark + if (null !== $cursorMark) { + $this->setCursorMark($collection->getCursorMark()); + } + // Construct record drivers for all the items in the response: $this->results = $collection->getRecords(); } diff --git a/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php index 5eea28ca728e0ebb5e2db9c167b8950f7e9929da..f2affd7464b161d19250bc74589025eaa4a72e3e 100644 --- a/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php +++ b/module/VuFind/src/VuFind/Search/Solr/ResultsFactory.php @@ -58,7 +58,8 @@ class ResultsFactory extends \VuFind\Search\Results\ResultsFactory array $options = null ) { $solr = parent::__invoke($container, $requestedName, $options); - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $solr->setSpellingProcessor( new \VuFind\Search\Solr\SpellingProcessor($config->Spelling ?? null) ); diff --git a/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php b/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php index 03720d3fbc75c857d3efacee16fa1703ab5a9144..53b9738eac99a7d9a955518084fb2b7e1ee910e8 100644 --- a/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php +++ b/module/VuFind/src/VuFind/Search/Solr/SpellingProcessor.php @@ -230,7 +230,7 @@ class SpellingProcessor return true; } // We should also skip terms already contained within the query: - return $queryContains == $query->containsTerm($term); + return $queryContains == $query->containsNormalizedTerm($term); } /** @@ -250,9 +250,8 @@ class SpellingProcessor $inToken = false; $targetTerm = ""; foreach ($this->tokenize($query) as $token) { - // TODO - Do we need stricter matching here, similar to that in - // \VuFindSearch\Query\Query::replaceTerm()? - if (stripos($token, $term) !== false) { + // Is the term part of the current token? + if (strpos($token, (string)$term) !== false) { $inToken = true; // We need to replace the whole token $targetTerm = $token; @@ -291,21 +290,16 @@ class SpellingProcessor ) { $returnArray[$targetTerm]['freq'] = $details['freq']; foreach ($details['suggestions'] as $word => $freq) { - // If the suggested word is part of a token - if ($inToken) { - // We need to make sure we replace the whole token - $replacement = str_replace($term, $word, $targetTerm); - } else { - $replacement = $word; - } + // If the suggested word is part of a token, we need to make sure we + // replace the whole token: + $replacement = $inToken ? str_replace($term, $word, $targetTerm) : $word; + // Do we need to show the whole, modified query? - if ($this->phrase) { - $label = $params->getDisplayQueryWithReplacedTerm( + $label = $this->phrase + ? $params->getDisplayQueryWithReplacedTerm( $targetTerm, $replacement - ); - } else { - $label = $replacement; - } + ) : $replacement; + // Basic spelling suggestion data $returnArray[$targetTerm]['suggestions'][$label] = [ 'freq' => $freq, @@ -315,11 +309,9 @@ class SpellingProcessor // Only generate expansions if enabled in config if ($this->expand) { // Parentheses differ for shingles - if (strstr($targetTerm, " ") !== false) { - $replacement = "(($targetTerm) OR ($replacement))"; - } else { - $replacement = "($targetTerm OR $replacement)"; - } + $replacement = (strstr($targetTerm, " ") !== false) + ? "(($targetTerm) OR ($replacement))" + : "($targetTerm OR $replacement)"; $returnArray[$targetTerm]['suggestions'][$label]['expand_term'] = $replacement; } diff --git a/module/VuFind/src/VuFind/Search/SolrAuth/Params.php b/module/VuFind/src/VuFind/Search/SolrAuth/Params.php index b4470567affd4e0b0c4e692381262aed5369093a..019dc41c3a8b13bf49639ffe6e542ac5d883a343 100644 --- a/module/VuFind/src/VuFind/Search/SolrAuth/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrAuth/Params.php @@ -38,4 +38,11 @@ namespace VuFind\Search\SolrAuth; */ class Params extends \VuFind\Search\Solr\Params { + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections = ['Facets']; } diff --git a/module/VuFind/src/VuFind/Search/SolrReserves/Params.php b/module/VuFind/src/VuFind/Search/SolrReserves/Params.php index c115b436aa8e3158aee83dfc3b40127aa56e6308..96a3c70d8771438accd7d648e43841a9f7f3ce89 100644 --- a/module/VuFind/src/VuFind/Search/SolrReserves/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrReserves/Params.php @@ -40,4 +40,11 @@ namespace VuFind\Search\SolrReserves; */ class Params extends \VuFind\Search\Solr\Params { + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections = ['Facets']; } diff --git a/module/VuFind/src/VuFind/Search/SolrWeb/Params.php b/module/VuFind/src/VuFind/Search/SolrWeb/Params.php index 6f4bd1d8cfc615b871cfc1c98f78d6236a2922eb..e05fcb12e930a32a47997fd897ae65a434f09021 100644 --- a/module/VuFind/src/VuFind/Search/SolrWeb/Params.php +++ b/module/VuFind/src/VuFind/Search/SolrWeb/Params.php @@ -38,4 +38,11 @@ namespace VuFind\Search\SolrWeb; */ class Params extends \VuFind\Search\Solr\Params { + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections = ['Facets']; } diff --git a/module/VuFind/src/VuFind/Search/Summon/Options.php b/module/VuFind/src/VuFind/Search/Summon/Options.php index ac5fb409c1c9c2cd8452bc2bd373985aa6c567e9..ce8f8888d0d25acf9e7fe82b632f162f678f726b 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Options.php +++ b/module/VuFind/src/VuFind/Search/Summon/Options.php @@ -147,6 +147,9 @@ class Options extends \VuFind\Search\Base\Options = $searchSettings->General->empty_search_relevance_override; } + // Load autocomplete preferences: + $this->configureAutocomplete($searchSettings); + // Set up views $this->initViewOptions($searchSettings); diff --git a/module/VuFind/src/VuFind/Search/Summon/Params.php b/module/VuFind/src/VuFind/Search/Summon/Params.php index 2b4acec0e3fea75588b00cdf26557085563eff80..238b349c3ad49dee2d683c07efcd39d4725a6873 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Params.php +++ b/module/VuFind/src/VuFind/Search/Summon/Params.php @@ -58,6 +58,23 @@ class Params extends \VuFind\Search\Base\Params */ protected $dateFacetSettings = []; + /** + * Config sections to search for facet labels if no override configuration + * is set. + * + * @var array + */ + protected $defaultFacetLabelSections + = ['Advanced_Facets', 'HomePage_Facets', 'FacetsTop', 'Facets']; + + /** + * Config sections to search for checkbox facet labels if no override + * configuration is set. + * + * @var array + */ + protected $defaultFacetLabelCheckboxSections = ['CheckboxFacets']; + /** * Constructor * @@ -134,31 +151,31 @@ class Params extends \VuFind\Search\Base\Params /** * Get a user-friendly string to describe the provided facet field. * - * @param string $field Facet field name. - * @param string $value Facet value. + * @param string $field Facet field name. + * @param string $value Facet value. + * @param string $default Default field name (null for default behavior). * - * @return string Human-readable description of field. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return string Human-readable description of field. */ - public function getFacetLabel($field, $value = null) + public function getFacetLabel($field, $value = null, $default = null) { // The default use of "Other" for undefined facets doesn't work well with // checkbox facets -- we'll use field names as the default within the Summon // search object. - return isset($this->facetConfig[$field]) - ? $this->facetConfig[$field] : $field; + return parent::getFacetLabel($field, $value, $default ?: $field); } /** * Get information on the current state of the boolean checkbox facets. * + * @param array $whitelist Whitelist of checkbox filters to return (null for all) + * * @return array */ - public function getCheckboxFacets() + public function getCheckboxFacets(array $whitelist = null) { // Grab checkbox facet details using the standard method: - $facets = parent::getCheckboxFacets(); + $facets = parent::getCheckboxFacets($whitelist); // Special case -- if we have a "holdings only" or "expand query" facet, // we want this to always appear, even on the "no results" screen, since @@ -400,41 +417,4 @@ class Params extends \VuFind\Search\Base\Params $this->initAdvancedFacets(); } } - - /** - * Initialize facet settings for the standard search screen. - * - * @return void - */ - public function initBasicFacets() - { - $this->initFacetList('Facets', 'Results_Settings'); - } - - /** - * Load all available facet settings. This is mainly useful for showing - * appropriate labels when an existing search has multiple filters associated - * with it. - * - * @param string $preferredSection Section to favor when loading settings; if - * multiple sections contain the same facet, this section's description will - * be favored. - * - * @return void - */ - public function activateAllFacets($preferredSection = false) - { - // Based on preference, change the order of initialization to make sure - // that preferred facet labels come in last. - if ($preferredSection == 'Advanced') { - $this->initHomePageFacets(); - $this->initBasicFacets(); - $this->initAdvancedFacets(); - } else { - $this->initHomePageFacets(); - $this->initAdvancedFacets(); - $this->initBasicFacets(); - } - $this->initCheckboxFacets(); - } } diff --git a/module/VuFind/src/VuFind/Search/Summon/Results.php b/module/VuFind/src/VuFind/Search/Summon/Results.php index 78f1fea14348ca11610d2aa6a6860405d03adf1d..24ca9fa5d3201099111532e60b619fcef74b8a43 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Results.php +++ b/module/VuFind/src/VuFind/Search/Summon/Results.php @@ -202,7 +202,7 @@ class Results extends \VuFind\Search\Base\Results protected function formatFacetData($current) { // We'll need this in the loop below: - $filterList = $this->getParams()->getFilters(); + $filterList = $this->getParams()->getRawFilters(); // Should we translate values for the current facet? $field = $current['displayName']; diff --git a/module/VuFind/src/VuFind/Search/Tags/Options.php b/module/VuFind/src/VuFind/Search/Tags/Options.php index 05dcca6376703a1ff32821660d9de8eb3be60360..8ad292ccdd3598d896859423c4e625fd92d767be 100644 --- a/module/VuFind/src/VuFind/Search/Tags/Options.php +++ b/module/VuFind/src/VuFind/Search/Tags/Options.php @@ -75,10 +75,8 @@ class Options extends \VuFind\Search\Base\Options 'title' => 'sort_title', 'author' => 'sort_author', 'year DESC' => 'sort_year', 'year' => 'sort_year asc' ]; - // Load autocomplete preference: - if (isset($searchSettings->Autocomplete->enabled)) { - $this->autocompleteEnabled = $searchSettings->Autocomplete->enabled; - } + // Load autocomplete preferences: + $this->configureAutocomplete($searchSettings); } /** diff --git a/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php b/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php index 513966b94b2153953ae89d88e2efca41d96b76b9..f2da46495be3eb454ca045635ecba7113e584067 100644 --- a/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php +++ b/module/VuFind/src/VuFind/Search/Tags/ResultsFactory.php @@ -60,7 +60,7 @@ class ResultsFactory extends \VuFind\Search\Results\ResultsFactory if (!empty($options)) { throw new \Exception('Unexpected options sent to factory!'); } - $tm = $container->get('VuFind\Db\Table\PluginManager'); + $tm = $container->get(\VuFind\Db\Table\PluginManager::class); return parent::__invoke($container, $requestedName, [$tm->get('Tags')]); } } diff --git a/module/VuFind/src/VuFind/Search/UrlQueryHelper.php b/module/VuFind/src/VuFind/Search/UrlQueryHelper.php index 0f9f7c0bca0ea8b18ed03f04b2cd3ec8b05b69c2..f45de1267717b95be06b8a1e1a344a631832fe29 100644 --- a/module/VuFind/src/VuFind/Search/UrlQueryHelper.php +++ b/module/VuFind/src/VuFind/Search/UrlQueryHelper.php @@ -248,15 +248,16 @@ class UrlQueryHelper /** * Replace a term in the search query (used for spelling replacement) * - * @param string $from Search term to find - * @param string $to Search term to insert + * @param string $from Search term to find + * @param string $to Search term to insert + * @param boolean $normalize If we should apply text normalization when replacing * * @return UrlQueryHelper */ - public function replaceTerm($from, $to) + public function replaceTerm($from, $to, $normalize = false) { $query = clone $this->queryObject; - $query->replaceTerm($from, $to); + $query->replaceTerm($from, $to, $normalize); return new static($this->urlParams, $query, $this->config); } @@ -313,6 +314,20 @@ class UrlQueryHelper return new static($params, $this->queryObject, $this->config, false); } + /** + * Reset default filter state. + * + * @return string + */ + public function resetDefaultFilters() + { + $params = $this->urlParams; + // Clear page: + unset($params['dfApplied']); + + return new static($params, $this->queryObject, $this->config, false); + } + /** * Get the current search parameters as a GET query. * diff --git a/module/VuFind/src/VuFind/Service/DateConverterFactory.php b/module/VuFind/src/VuFind/Service/DateConverterFactory.php index 2df544f7bad0ec3eda1f9c658e596766a7bc2d61..5e9a7eb11dd6dce7cba38845abbb28253788dc78 100644 --- a/module/VuFind/src/VuFind/Service/DateConverterFactory.php +++ b/module/VuFind/src/VuFind/Service/DateConverterFactory.php @@ -63,7 +63,8 @@ class DateConverterFactory implements FactoryInterface } // Pass along key [Site] settings: displayDateFormat, displayTimeFormat, // timezone - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $settings = isset($config->Site) ? $config->Site->toArray() : []; return new $requestedName($settings); } diff --git a/module/VuFind/src/VuFind/Service/Factory.php b/module/VuFind/src/VuFind/Service/Factory.php deleted file mode 100644 index ec700667adabe2a503740653cb09770d30629c56..0000000000000000000000000000000000000000 --- a/module/VuFind/src/VuFind/Service/Factory.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php -/** - * Factory for various top-level VuFind services. - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package Service - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFind\Service; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for various top-level VuFind services. - * - * @category VuFind - * @package Service - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the date converter. - * - * @param ServiceManager $sm Service manager. - * - * @return \Zend\Db\Adapter\Adapter - */ - public static function getDbAdapter(ServiceManager $sm) - { - return $sm->get('VuFind\Db\AdapterFactory')->getAdapter(); - } - - /** - * Construct the HTTP service. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFindHttp\HttpService - */ - public static function getHttp(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $options = []; - if (isset($config->Proxy->host)) { - $options['proxy_host'] = $config->Proxy->host; - if (isset($config->Proxy->port)) { - $options['proxy_port'] = $config->Proxy->port; - } - if (isset($config->Proxy->type)) { - $options['proxy_type'] = $config->Proxy->type; - } - } - $defaults = isset($config->Http) - ? $config->Http->toArray() : []; - return new \VuFindHttp\HttpService($options, $defaults); - } - - /** - * Construct the ProxyManager configuration. - * - * @param ServiceManager $sm Service manager. - * - * @return \ProxyManager\Configuration - */ - public static function getProxyConfig(ServiceManager $sm) - { - $config = new \ProxyManager\Configuration(); - $cacheManager = $sm->get('VuFind\Cache\Manager'); - $dir = $cacheManager->getCacheDir() . 'objects'; - $config->setProxiesTargetDir($dir); - if (APPLICATION_ENV != 'development') { - spl_autoload_register($config->getProxyAutoloader()); - } - return $config; - } - - /** - * Construct the search service. - * - * @param ServiceManager $sm Service manager. - * - * @return \VuFindSearch\Service - */ - public static function getSearchService(ServiceManager $sm) - { - return new \VuFindSearch\Service( - new \Zend\EventManager\EventManager($sm->get('SharedEventManager')) - ); - } - - /** - * Construct the translator. - * - * @param ServiceManager $sm Service manager. - * - * @return \Zend\Mvc\I18n\Translator - */ - public static function getTranslator(ServiceManager $sm) - { - $factory = new \Zend\Mvc\I18n\TranslatorFactory(); - $translator = $factory->createService($sm); - - // Set up the ExtendedIni plugin: - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $pathStack = [ - APPLICATION_PATH . '/languages', - LOCAL_OVERRIDE_DIR . '/languages' - ]; - $fallbackLocales = $config->Site->language == 'en' - ? 'en' - : [$config->Site->language, 'en']; - try { - $pm = $translator->getPluginManager(); - } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $ex) { - // If getPluginManager is missing, this means that the user has - // disabled translation in module.config.php or PHP's intl extension - // is missing. We can do no further configuration of the object. - return $translator; - } - $pm->setService( - 'ExtendedIni', - new \VuFind\I18n\Translator\Loader\ExtendedIni( - $pathStack, $fallbackLocales - ) - ); - - // Set up language caching for better performance: - try { - $translator->setCache( - $sm->get('VuFind\Cache\Manager')->getCache('language') - ); - } catch (\Exception $e) { - // Don't let a cache failure kill the whole application, but make - // note of it: - $logger = $sm->get('VuFind\Log\Logger'); - $logger->debug( - 'Problem loading cache: ' . get_class($e) . ' exception: ' - . $e->getMessage() - ); - } - - return $translator; - } -} diff --git a/module/VuFind/src/VuFind/Service/HttpServiceFactory.php b/module/VuFind/src/VuFind/Service/HttpServiceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..b4801c73280a44d1b9fce4d17295b959d0839875 --- /dev/null +++ b/module/VuFind/src/VuFind/Service/HttpServiceFactory.php @@ -0,0 +1,80 @@ +<?php +/** + * VuFind HTTP Service factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * VuFind HTTP Service factory. + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class HttpServiceFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $options = []; + if (isset($config->Proxy->host)) { + $options['proxy_host'] = $config->Proxy->host; + if (isset($config->Proxy->port)) { + $options['proxy_port'] = $config->Proxy->port; + } + if (isset($config->Proxy->type)) { + $options['proxy_type'] = $config->Proxy->type; + } + } + $defaults = isset($config->Http) + ? $config->Http->toArray() : []; + return new $requestedName($options, $defaults); + } +} diff --git a/module/VuFind/src/VuFind/Service/ProxyConfigFactory.php b/module/VuFind/src/VuFind/Service/ProxyConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5fdd6bc9aafed599fd3ad889237c4fb0e71f72c2 --- /dev/null +++ b/module/VuFind/src/VuFind/Service/ProxyConfigFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * ProxyManager configuration factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\Service; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ProxyManager configuration factory. + * + * @category VuFind + * @package Service + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ProxyConfigFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = new $requestedName(); + $cacheManager = $container->get(\VuFind\Cache\Manager::class); + $dir = $cacheManager->getCacheDir() . 'objects'; + $config->setProxiesTargetDir($dir); + if (APPLICATION_ENV != 'development') { + spl_autoload_register($config->getProxyAutoloader()); + } + return $config; + } +} diff --git a/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php b/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php index 3719d3e7c7492e072d436d36405be442b4f2f7ab..336bb0c1e556f1a4dec65801aae27e8a1e9745db 100644 --- a/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php +++ b/module/VuFind/src/VuFind/Service/ReCaptchaFactory.php @@ -61,7 +61,8 @@ class ReCaptchaFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $siteKey = isset($config->Captcha->siteKey) ? $config->Captcha->siteKey : (isset($config->Captcha->publicKey) @@ -72,8 +73,9 @@ class ReCaptchaFactory implements FactoryInterface : (isset($config->Captcha->privateKey) ? $config->Captcha->privateKey : ''); - $httpClient = $container->get('VuFindHttp\HttpService')->createClient(); - $translator = $container->get('Zend\Mvc\I18n\Translator'); + $httpClient = $container->get(\VuFindHttp\HttpService::class) + ->createClient(); + $translator = $container->get(\Zend\Mvc\I18n\Translator::class); $rcOptions = ['lang' => $translator->getLocale()]; if (isset($config->Captcha->theme)) { $rcOptions['theme'] = $config->Captcha->theme; diff --git a/module/VuFind/src/VuFind/Record/RouterFactory.php b/module/VuFind/src/VuFind/Service/SearchServiceFactory.php similarity index 85% rename from module/VuFind/src/VuFind/Record/RouterFactory.php rename to module/VuFind/src/VuFind/Service/SearchServiceFactory.php index b9e7f79b055e5d24b15fd8283d5aa5782eac18f9..a23756a460cd01979e61375d75476218590f6542 100644 --- a/module/VuFind/src/VuFind/Record/RouterFactory.php +++ b/module/VuFind/src/VuFind/Service/SearchServiceFactory.php @@ -1,10 +1,10 @@ <?php /** - * Record router factory. + * VuFind Search Service factory. * * PHP version 7 * - * Copyright (C) Villanova University 2018. + * Copyright (C) Villanova University 2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -20,26 +20,27 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Record + * @package Service * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -namespace VuFind\Record; +namespace VuFind\Service; use Interop\Container\ContainerInterface; +use Zend\EventManager\EventManager; use Zend\ServiceManager\Factory\FactoryInterface; /** - * Record router factory. + * VuFind Search Service factory. * * @category VuFind - * @package Record + * @package Service * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -class RouterFactory implements FactoryInterface +class SearchServiceFactory implements FactoryInterface { /** * Create an object @@ -62,8 +63,7 @@ class RouterFactory implements FactoryInterface throw new \Exception('Unexpected options passed to factory.'); } return new $requestedName( - $container->get('VuFind\Record\Loader'), - $container->get('VuFind\Config\PluginManager')->get('config') + new EventManager($container->get('SharedEventManager')) ); } } diff --git a/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php b/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php index ecba0637e88df1663e3ba37586c59e8ba6e242f0..139181674a36387dbe2c1f8c3008c4468bf64055 100644 --- a/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php +++ b/module/VuFind/src/VuFind/Service/ServiceWithConfigIniFactory.php @@ -58,7 +58,8 @@ class ServiceWithConfigIniFactory implements FactoryInterface public function __invoke(ContainerInterface $container, $requestedName, array $options = null ) { - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config, ...($options ?: [])); } } diff --git a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php index fb6c94ef2eab7f9d5c7428462677d5dde1288a37..e48db3cfcda53a4cd2dbfc0e9f02801c0ee6a8f5 100644 --- a/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php +++ b/module/VuFind/src/VuFind/ServiceManager/AbstractPluginManager.php @@ -59,7 +59,7 @@ abstract class AbstractPluginManager extends Base ) { parent::__construct($configOrContainerInstance, $v3config); $this->addInitializer( - 'VuFind\ServiceManager\ServiceInitializer', false + \VuFind\ServiceManager\ServiceInitializer::class, false ); } diff --git a/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php b/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php index f383c4515acf300f34ca34169ecde0d9b516160d..7aa68764eb340e70f3c75e44fce31d3e09bc6f3b 100644 --- a/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php +++ b/module/VuFind/src/VuFind/ServiceManager/ServiceInitializer.php @@ -54,7 +54,7 @@ class ServiceInitializer implements InitializerInterface static $enabled = null; if (null === $enabled) { // Return true if Record Cache is enabled for any data source - $cacheConfig = $sm->get('VuFind\Config\PluginManager') + $cacheConfig = $sm->get(\VuFind\Config\PluginManager::class) ->get('RecordCache'); $enabled = false; foreach ($cacheConfig as $section) { @@ -82,22 +82,24 @@ class ServiceInitializer implements InitializerInterface public function __invoke(ContainerInterface $sm, $instance) { if ($instance instanceof \VuFind\Db\Table\DbTableAwareInterface) { - $instance->setDbTableManager($sm->get('VuFind\Db\Table\PluginManager')); + $instance->setDbTableManager( + $sm->get(\VuFind\Db\Table\PluginManager::class) + ); } if ($instance instanceof \Zend\Log\LoggerAwareInterface) { - $instance->setLogger($sm->get('VuFind\Log\Logger')); + $instance->setLogger($sm->get(\VuFind\Log\Logger::class)); } if ($instance instanceof \VuFind\I18n\Translator\TranslatorAwareInterface) { - $instance->setTranslator($sm->get('Zend\Mvc\I18n\Translator')); + $instance->setTranslator($sm->get(\Zend\Mvc\I18n\Translator::class)); } if ($instance instanceof \VuFindHttp\HttpServiceAwareInterface) { - $instance->setHttpService($sm->get('VuFindHttp\HttpService')); + $instance->setHttpService($sm->get(\VuFindHttp\HttpService::class)); } // Only inject cache if configuration enabled (to save resources): if ($instance instanceof \VuFind\Record\Cache\RecordCacheAwareInterface && $this->isCacheEnabled($sm) ) { - $instance->setRecordCache($sm->get('VuFind\Record\Cache')); + $instance->setRecordCache($sm->get(\VuFind\Record\Cache::class)); } return $instance; } diff --git a/module/VuFind/src/VuFind/Session/ManagerFactory.php b/module/VuFind/src/VuFind/Session/ManagerFactory.php index 10757084262fd18370bbbf4e8645d144c01fd70c..2995052263da285f2e9b2a665697bdb0fc515402 100644 --- a/module/VuFind/src/VuFind/Session/ManagerFactory.php +++ b/module/VuFind/src/VuFind/Session/ManagerFactory.php @@ -53,7 +53,7 @@ class ManagerFactory implements FactoryInterface */ protected function getOptions(ContainerInterface $container) { - $cookieManager = $container->get('VuFind\Cookie\CookieManager'); + $cookieManager = $container->get(\VuFind\Cookie\CookieManager::class); // Set options only if we are not running from CLI $options = 'cli' !== PHP_SAPI ? [ 'cookie_httponly' => $cookieManager->isHttpOnly(), @@ -85,12 +85,14 @@ class ManagerFactory implements FactoryInterface protected function getHandler(ContainerInterface $container) { // Load and validate session configuration: - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); if (!isset($config->Session->type)) { throw new \Exception('Cannot initialize session; configuration missing'); } - $sessionPluginManager = $container->get('VuFind\Session\PluginManager'); + $sessionPluginManager = $container + ->get(\VuFind\Session\PluginManager::class); $sessionHandler = $sessionPluginManager->get($config->Session->type); $sessionHandler->setConfig($config->Session); return $sessionHandler; @@ -171,7 +173,7 @@ class ManagerFactory implements FactoryInterface // Check if we need to immediately stop it based on the settings object // (which may have been informed by a controller that sessions should not // be written as part of the current process): - $settings = $container->get('VuFind\Session\Settings'); + $settings = $container->get(\VuFind\Session\Settings::class); if ($settings->setSessionManager($sessionManager)->isWriteDisabled()) { $sessionManager->getSaveHandler()->disableWrites(); } else { diff --git a/module/VuFind/src/VuFind/Session/PluginManager.php b/module/VuFind/src/VuFind/Session/PluginManager.php index 61ad67cc59a4a03326ff02ea0fba4b34ec9daf1b..6927865aa57f51412950e035a6fea0e604784f0d 100644 --- a/module/VuFind/src/VuFind/Session/PluginManager.php +++ b/module/VuFind/src/VuFind/Session/PluginManager.php @@ -29,6 +29,8 @@ */ namespace VuFind\Session; +use Zend\ServiceManager\Factory\InvokableFactory; + /** * Session handler plugin manager * @@ -47,13 +49,14 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $aliases = [ - 'database' => 'VuFind\Session\Database', - 'file' => 'VuFind\Session\File', - 'memcache' => 'VuFind\Session\Memcache', + 'database' => Database::class, + 'file' => File::class, + 'memcache' => Memcache::class, + 'redis' => Redis::class, // for legacy 1.x compatibility - 'filesession' => 'VuFind\Session\File', - 'memcachesession' => 'VuFind\Session\Memcache', - 'mysqlsession' => 'VuFind\Session\Database', + 'filesession' => File::class, + 'memcachesession' => Memcache::class, + 'mysqlsession' => Database::class, ]; /** @@ -62,9 +65,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var array */ protected $factories = [ - 'VuFind\Session\Database' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Session\File' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFind\Session\Memcache' => 'Zend\ServiceManager\Factory\InvokableFactory', + Database::class => InvokableFactory::class, + File::class => InvokableFactory::class, + Memcache::class => InvokableFactory::class, + Redis::class => InvokableFactory::class, ]; /** @@ -73,9 +77,10 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager * @var string[][]|\Zend\ServiceManager\Factory\DelegatorFactoryInterface[][] */ protected $delegators = [ - 'VuFind\Session\Database' => ['VuFind\Session\SecureDelegatorFactory'], - 'VuFind\Session\File' => ['VuFind\Session\SecureDelegatorFactory'], - 'VuFind\Session\Memcache' => ['VuFind\Session\SecureDelegatorFactory'], + Database::class => [SecureDelegatorFactory::class], + File::class => [SecureDelegatorFactory::class], + Memcache::class => [SecureDelegatorFactory::class], + Redis::class => [SecureDelegatorFactory::class], ]; /** @@ -90,7 +95,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager public function __construct($configOrContainerInstance = null, array $v3config = [] ) { - $this->addAbstractFactory('VuFind\Session\PluginFactory'); + $this->addAbstractFactory(PluginFactory::class); parent::__construct($configOrContainerInstance, $v3config); } @@ -102,6 +107,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager */ protected function getExpectedInterface() { - return 'VuFind\Session\HandlerInterface'; + return HandlerInterface::class; } } diff --git a/module/VuFind/src/VuFind/Session/Redis.php b/module/VuFind/src/VuFind/Session/Redis.php new file mode 100644 index 0000000000000000000000000000000000000000..dc8645e4f8be1d7b28acc4fff73faeb38bd8f780 --- /dev/null +++ b/module/VuFind/src/VuFind/Session/Redis.php @@ -0,0 +1,139 @@ +<?php +/** + * Redis session handler + * + * Note: Using phpredis extension (see https://github.com/phpredis/phpredis) is + * optional, this class use Credis in standalone mode by default + * + * PHP version 7 + * + * Coypright (C) Moravian Library 2019. + * + * 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 VuFind + * @package Session_Handlers + * @author Veros Kaplan <cpk-dev@mzk.cz> + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:session_handlers Wiki + */ +namespace VuFind\Session; + +/** + * Redis session handler + * + * @category VuFind + * @package Session_Handlers + * @author Veros Kaplan <cpk-dev@mzk.cz> + * @author Josef Moravec <moravec@mzk.cz> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:session_handlers Wiki + */ +class Redis extends AbstractBase +{ + /** + * Redis connection + * + * @var \Credis_Client + */ + protected $connection = false; + + /** + * Redis version + * + * @var int + */ + protected $redisVersion = 3; + + /** + * Get connection to Redis + * + * @throws \Exception + * @return \Credis_Client + */ + protected function getConnection() + { + if (!$this->connection) { + // Set defaults if nothing set in config file. + $host = $this->config->redis_host ?? 'localhost'; + $port = $this->config->redis_port ?? 6379; + $timeout = $this->config->redis_connection_timeout ?? 0.5; + $auth = $this->config->redis_auth ?? false; + $redis_db = $this->config->redis_db ?? 0; + $this->redisVersion = (int)($this->config->redis_version ?? 3); + $standalone = (bool)($this->config->redis_standalone ?? true); + + // Create Credis client, the connection is established lazily + $this->connection = new \Credis_Client( + $host, $port, $timeout, '', $redis_db, $auth + ); + if ($standalone) { + $this->connection->forceStandalone(); + } + } + return $this->connection; + } + + /** + * Read function must return string value always to make save handler work as + * expected. Return empty string if there is no data to read. + * + * @param string $sess_id The session ID to read + * + * @return string + */ + public function read($sess_id) + { + $session = $this->getConnection()->get("vufind_sessions/{$sess_id}"); + return $session !== false ? $session : ''; + } + + /** + * Write function that is called when session data is to be saved. + * + * @param string $sess_id The current session ID + * @param string $data The session data to write + * + * @return bool + */ + protected function saveSession($sess_id, $data) + { + return $this->getConnection()->setex( + "vufind_sessions/{$sess_id}", $this->lifetime, $data + ); + } + + /** + * The destroy handler, this is executed when a session is destroyed with + * session_destroy() and takes the session id as its only parameter. + * + * @param string $sess_id The session ID to destroy + * + * @return bool + */ + public function destroy($sess_id) + { + // Perform standard actions required by all session methods: + parent::destroy($sess_id); + + // Perform Redis-specific cleanup + if ($this->redisVersion >= 4) { + $return = $this->getConnection()->unlink("vufind_sessions/{$sess_id}"); + } else { + $return = $this->getConnection()->del("vufind_sessions/{$sess_id}"); + } + return ($return > 0) ? true : false; + } +} diff --git a/module/VuFind/src/VuFind/Session/SecureDelegator.php b/module/VuFind/src/VuFind/Session/SecureDelegator.php index 8425c42fba18d1863bb3749005ac4755b173266b..6ae95a1efeb6c1441cc3f46ddff21b23a000bc76 100644 --- a/module/VuFind/src/VuFind/Session/SecureDelegator.php +++ b/module/VuFind/src/VuFind/Session/SecureDelegator.php @@ -69,8 +69,8 @@ class SecureDelegator /** * SecureDelegator constructor. * - * @param CookieManager $cookieManager {@see $cookieHandler} - * @param HandlerInterface $handler {@see $handler} + * @param CookieManager $cookieManager VuFind cookie manager service. + * @param HandlerInterface $handler The wrapped session handler. */ public function __construct( CookieManager $cookieManager, HandlerInterface $handler diff --git a/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php b/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php index edfb35daf94ec5927943619f45f65a24bd655b79..fd370367314801a6470478d8f9b0adea86e9407a 100644 --- a/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php +++ b/module/VuFind/src/VuFind/Session/SecureDelegatorFactory.php @@ -67,7 +67,7 @@ class SecureDelegatorFactory implements DelegatorFactoryInterface * @var HandlerInterface $handler */ $handler = call_user_func($callback); - $config = $container->get('VuFind\Config\PluginManager'); + $config = $container->get(\VuFind\Config\PluginManager::class); $secure = $config->get('config')->Session->secure ?? false; return $secure ? $this->delegate($container, $handler) : $handler; } @@ -83,8 +83,8 @@ class SecureDelegatorFactory implements DelegatorFactoryInterface protected function delegate( ContainerInterface $container, HandlerInterface $handler ): HandlerInterface { - $cookieManager = $container->get('VuFind\Cookie\CookieManager'); - $config = $container->get('ProxyManager\Configuration'); + $cookieManager = $container->get(\VuFind\Cookie\CookieManager::class); + $config = $container->get(\ProxyManager\Configuration::class); $factory = new LazyLoadingValueHolderFactory($config); $delegator = new SecureDelegator($cookieManager, $handler); /** diff --git a/module/VuFind/src/VuFind/Sitemap/Generator.php b/module/VuFind/src/VuFind/Sitemap/Generator.php index a04068cdd9536140a5a2a80ff08f43d5e56b8d31..2d2a2bd464c8a3847cc4d1a9f5299e4e791e49f0 100644 --- a/module/VuFind/src/VuFind/Sitemap/Generator.php +++ b/module/VuFind/src/VuFind/Sitemap/Generator.php @@ -58,6 +58,13 @@ class Generator */ protected $baseUrl; + /** + * Base URL for sitemap + * + * @var string + */ + protected $baseSitemapUrl; + /** * Settings specifying which backends to index. * @@ -134,6 +141,8 @@ class Generator $this->backendManager = $bm; $this->baseUrl = $baseUrl; $this->config = $config; + $this->baseSitemapUrl = empty($this->config->SitemapIndex->baseSitemapUrl) + ? $this->baseUrl : $this->config->SitemapIndex->baseSitemapUrl; // Process backend configuration: $backendConfig = isset($this->config->Sitemap->index) @@ -175,6 +184,36 @@ class Generator return $this->verbose; } + /** + * Get/set base url + * + * @param string $newUrl New base url + * + * @return string Current or new base url + */ + public function setBaseUrl($newUrl = null) + { + if (null !== $newUrl) { + $this->baseUrl = $newUrl; + } + return $this->baseUrl; + } + + /** + * Get/set base sitemap url + * + * @param string $newUrl New base sitemap url + * + * @return string Current or new base sitemap url + */ + public function setBaseSitemapUrl($newUrl = null) + { + if (null !== $newUrl) { + $this->baseSitemapUrl = $newUrl; + } + return $this->baseSitemapUrl; + } + /** * Get the current microtime, formatted to a number. * @@ -443,7 +482,6 @@ class Generator protected function getBaseSitemapIndexUrl() { // Pick the appropriate base URL based on the configuration files: - return empty($this->config->SitemapIndex->baseSitemapUrl) - ? $this->baseUrl : $this->config->SitemapIndex->baseSitemapUrl; + return $this->baseSitemapUrl; } } diff --git a/module/VuFind/src/VuFind/Solr/WriterFactory.php b/module/VuFind/src/VuFind/Solr/WriterFactory.php index e37493415f531a98518db8b2b6209291687a8e2d..729499db6fe3e74e233f5c728d207616679058c9 100644 --- a/module/VuFind/src/VuFind/Solr/WriterFactory.php +++ b/module/VuFind/src/VuFind/Solr/WriterFactory.php @@ -61,9 +61,10 @@ class WriterFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } + $changeTracker = $container->get(\VuFind\Db\Table\PluginManager::class) + ->get('changetracker'); return new $requestedName( - $container->get('VuFind\Search\BackendManager'), - $container->get('VuFind\Db\Table\PluginManager')->get('changetracker') + $container->get(\VuFind\Search\BackendManager::class), $changeTracker ); } } diff --git a/module/VuFind/src/VuFind/TagsFactory.php b/module/VuFind/src/VuFind/TagsFactory.php index 58e5c5222c46d25fefb79f51bdb2b587d2909002..d32fbd03cd54f714136e11648c13755ad1bd4c18 100644 --- a/module/VuFind/src/VuFind/TagsFactory.php +++ b/module/VuFind/src/VuFind/TagsFactory.php @@ -61,7 +61,8 @@ class TagsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $maxLength = isset($config->Social->max_tag_length) ? $config->Social->max_tag_length : 64; return new $requestedName($maxLength); diff --git a/module/VuFind/src/VuFind/UrlShortener/Database.php b/module/VuFind/src/VuFind/UrlShortener/Database.php new file mode 100644 index 0000000000000000000000000000000000000000..81c09a93eadcbd86a9b57e00c57b3fde422a98e7 --- /dev/null +++ b/module/VuFind/src/VuFind/UrlShortener/Database.php @@ -0,0 +1,159 @@ +<?php +/** + * Local database-driven URL shortener. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\UrlShortener; + +use VuFind\Db\Table\Shortlinks as ShortlinksTable; + +/** + * Local database-driven URL shortener. + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Database implements UrlShortenerInterface +{ + const BASE62_ALPHABET + = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + const BASE62_BASE = 62; + + /** + * Base URL of current VuFind site + * + * @var string + */ + protected $baseUrl; + + /** + * Table containing shortlinks + * + * @var ShortlinksTable + */ + protected $table; + + /** + * Constructor + * + * @param string $baseUrl Base URL of current VuFind site + * @param ShortlinksTable $table Shortlinks database table + */ + public function __construct(string $baseUrl, ShortlinksTable $table) + { + $this->baseUrl = $baseUrl; + $this->table = $table; + } + + /** + * Common base62 encoding function. + * Implemented here so we don't need additional PHP modules like bcmath. + * + * @param string $base10Number Number to encode + * + * @return string + * + * @throws \Exception + */ + protected function base62Encode($base10Number) + { + $binaryNumber = intval($base10Number); + if ($binaryNumber === 0) { + throw new \Exception('not a base10 number: "' . $base10Number . '"'); + } + + $base62Number = ''; + while ($binaryNumber != 0) { + $base62Number = self::BASE62_ALPHABET[$binaryNumber % self::BASE62_BASE] + . $base62Number; + $binaryNumber = intdiv($binaryNumber, self::BASE62_BASE); + } + + return ($base62Number == '') ? '0' : $base62Number; + } + + /** + * Common base62 decoding function. + * Implemented here so we don't need additional PHP modules like bcmath. + * + * @param string $base62Number Number to decode + * + * @return int + * + * @throws \Exception + */ + protected function base62Decode($base62Number) + { + $binaryNumber = 0; + for ($i = 0; $i < strlen($base62Number); ++$i) { + $digit = $base62Number[$i]; + $strpos = strpos(self::BASE62_ALPHABET, $digit); + if ($strpos === false) { + throw new \Exception('not a base62 digit: "' . $digit . '"'); + } + + $binaryNumber *= self::BASE62_BASE; + $binaryNumber += $strpos; + } + return $binaryNumber; + } + + /** + * Generate & store shortened URL in Database. + * + * @param string $url URL + * + * @return string + */ + public function shorten($url) + { + $path = str_replace($this->baseUrl, '', $url); + $this->table->insert(['path' => $path]); + $id = $this->table->getLastInsertValue(); + + $shortUrl = $this->baseUrl . '/short/' . $this->base62Encode($id); + return $shortUrl; + } + + /** + * Resolve URL from Database via id. + * + * @param string $id ID to resolve + * + * @return string + */ + public function resolve($id) + { + $results = $this->table->select(['id' => $this->base62Decode($id)]); + if ($results->count() !== 1) { + throw new \Exception('Shortlink could not be resolved: ' . $id); + } + + return $this->baseUrl . $results->current()['path']; + } +} diff --git a/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php b/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2790b129e76c066f02dd65549eac02a2c4b63ac3 --- /dev/null +++ b/module/VuFind/src/VuFind/UrlShortener/DatabaseFactory.php @@ -0,0 +1,65 @@ +<?php +/** + * Factory for local database-driven URL shortener. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\UrlShortener; + +use Interop\Container\ContainerInterface; + +/** + * Factory for local database-driven URL shortener. + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class DatabaseFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $router = $container->get('HttpRouter'); + $baseUrl = $container->get('ViewRenderer')->plugin('serverurl') + ->__invoke($router->assemble([], ['name' => 'home'])); + $table = $container->get(\VuFind\Db\Table\PluginManager::class) + ->get('shortlinks'); + return new $requestedName(rtrim($baseUrl, '/'), $table); + } +} diff --git a/module/VuFind/src/VuFind/UrlShortener/None.php b/module/VuFind/src/VuFind/UrlShortener/None.php new file mode 100644 index 0000000000000000000000000000000000000000..5651b7a878447ba6f9eb152d11561ea4e8131d41 --- /dev/null +++ b/module/VuFind/src/VuFind/UrlShortener/None.php @@ -0,0 +1,66 @@ +<?php +/** + * No-op URL shortener (default version, does nothing). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\UrlShortener; + +/** + * No-op URL shortener (default version, does nothing). + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class None implements UrlShortenerInterface +{ + /** + * Dummy to return original URL version. + * + * @param string $url URL + * + * @return string + */ + public function shorten($url) + { + return $url; + } + + /** + * Dummy implementation. Resolving is not necessary because initial URL + * has not been shortened. + * + * @param string $id ID to resolve + * + * @return string + * @throws Exception because this class is not meant to resolve shortlinks. + */ + public function resolve($id) + { + throw new \Exception('UrlShortener None is unable to resolve shortlinks.'); + } +} diff --git a/module/VuFind/src/VuFind/UrlShortener/PluginManager.php b/module/VuFind/src/VuFind/UrlShortener/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..8e8080988c97d1b365cc6d40a49529550efcfb89 --- /dev/null +++ b/module/VuFind/src/VuFind/UrlShortener/PluginManager.php @@ -0,0 +1,73 @@ +<?php +/** + * URL shortener plugin manager. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\UrlShortener; + +use Zend\ServiceManager\Factory\InvokableFactory; + +/** + * URL shortener plugin manager. + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'none' => None::class, + 'database' => Database::class, + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + None::class => InvokableFactory::class, + Database::class => DatabaseFactory::class, + ]; + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return UrlShortenerInterface::class; + } +} diff --git a/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php b/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..286a405f24995395e5fd96a4c48365d33874706d --- /dev/null +++ b/module/VuFind/src/VuFind/UrlShortener/ServiceFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Factory to construct the configured UrlShortener service. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\UrlShortener; + +use Interop\Container\ContainerInterface; + +/** + * Factory to construct the configured UrlShortener service. + * + * @category VuFind + * @package UrlShortener + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ServiceFactory +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $service = empty($config->Mail->url_shortener) + ? 'none' : $config->Mail->url_shortener; + return $container->get(PluginManager::class)->get($service); + } +} diff --git a/module/finc/src/finc/Auth/Factory.php b/module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php similarity index 61% rename from module/finc/src/finc/Auth/Factory.php rename to module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php index dead0c9064c4fd7fd672c668bfc56762d8fb9b24..3f9d02e66354605112a3df63d204f67f01856630 100644 --- a/module/finc/src/finc/Auth/Factory.php +++ b/module/VuFind/src/VuFind/UrlShortener/UrlShortenerInterface.php @@ -1,10 +1,10 @@ <?php /** - * Factory for authentication services. + * URL shortener interface. * - * PHP version 5 + * PHP version 7 * - * Copyright (C) Villanova University 2014. + * Copyright (C) Villanova University 2019. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -17,42 +17,42 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Authentication + * @package UrlShortener * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ -namespace finc\Auth; -use Zend\ServiceManager\ServiceManager; +namespace VuFind\UrlShortener; /** - * Factory for authentication services. + * URL shortener interface. * * @category VuFind - * @package Authentication + * @package UrlShortener * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore */ -class Factory +interface UrlShortenerInterface { /** - * Construct the ILS plugin. + * Generate and return shortened version of a URL. + * + * @param string $url URL + * + * @return string + */ + public function shorten($url); + + /** + * Resolve a shortened URL by its id. * - * @param ServiceManager $sm Service manager. + * @param string $id ID to resolve * - * @return ILS + * @return string */ - public static function getILS(ServiceManager $sm) - { - return new ILS( - $sm->get('VuFind\ILS\Connection'), - $sm->get('VuFind\Auth\ILSAuthenticator') - ); - } + public function resolve($id); } diff --git a/module/VuFind/src/VuFind/Validator/CsrfFactory.php b/module/VuFind/src/VuFind/Validator/CsrfFactory.php index e50365789b6d25980adf5e7bfddbdec4aa4c04fa..c41ee838e0aff5356b12a0ab1551aafe5e7aec7e 100644 --- a/module/VuFind/src/VuFind/Validator/CsrfFactory.php +++ b/module/VuFind/src/VuFind/Validator/CsrfFactory.php @@ -66,8 +66,9 @@ class CsrfFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options passed to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); - $sessionManager = $container->get('Zend\Session\SessionManager'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); return new $requestedName( [ 'session' => new \Zend\Session\Container('csrf', $sessionManager), diff --git a/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php b/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php index 00d20f359860aabfd1061a682581124979166194..c2b585b2aad26fd976568339dd9a707a24cfceb4 100644 --- a/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php +++ b/module/VuFind/src/VuFind/View/Helper/AbstractSearch.php @@ -84,12 +84,20 @@ abstract class AbstractSearch extends AbstractHelper $html .= ', '; } $href = $results->getUrlQuery() - ->replaceTerm($term, $data['new_term'])->getParams(); + ->replaceTerm( + $term, + $data['new_term'], + true + )->getParams(); $html .= '<a href="' . $href . '">' . $view->escapeHtml($word) . '</a>'; if (isset($data['expand_term']) && !empty($data['expand_term'])) { $url = $results->getUrlQuery() - ->replaceTerm($term, $data['expand_term'])->getParams(); + ->replaceTerm( + $term, + $data['expand_term'], + true + )->getParams(); $html .= $this->renderExpandLink($url, $view); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php index 8703255b27111b61cee00d7f7e5ec82f52504eba..e991df5bbcff763b6aa1ff75c3378e4888dca8d0 100644 --- a/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Bootstrap3/LayoutClassFactory.php @@ -61,7 +61,8 @@ class LayoutClassFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $sidebarOnLeft = $config->Site->sidebarOnLeft ?? false; $mirror = $config->Site->mirrorSidebarInRTL ?? true; $offcanvas = $config->Site->offcanvas ?? false; diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php index f3f1bfc0052ef8591abc6574ea8ebe26daf1b882..49074080385cfeedd6086a67feb3ee0b6d3bf9dd 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AccountCapabilitiesFactory.php @@ -62,7 +62,7 @@ class AccountCapabilitiesFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Config\AccountCapabilities') + $container->get(\VuFind\Config\AccountCapabilities::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php index ec73c23d32b1aa7e50e49364b14f3be1823d86b6..a23df23148a53d4bc1fd5daabcd8cfb6434e8c80 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AddThisFactory.php @@ -61,7 +61,8 @@ class AddThisFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->AddThis->key ?? false); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php index e90a0280cd3a053e521abddf193a4de1027f52d2..2efae01d33656c5fd5fbd2a6aa12e7ae5ff8bf36 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AuthFactory.php @@ -62,8 +62,8 @@ class AuthFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Auth\Manager'), - $container->get('VuFind\Auth\ILSAuthenticator') + $container->get(\VuFind\Auth\Manager::class), + $container->get(\VuFind\Auth\ILSAuthenticator::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php index e90a0280cd3a053e521abddf193a4de1027f52d2..2efae01d33656c5fd5fbd2a6aa12e7ae5ff8bf36 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/AuthorNotesFactory.php @@ -62,8 +62,8 @@ class AuthFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Auth\Manager'), - $container->get('VuFind\Auth\ILSAuthenticator') + $container->get(\VuFind\Auth\Manager::class), + $container->get(\VuFind\Auth\ILSAuthenticator::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php index b9b69a13f99aa38860bb4f21115a32b83f41d5d6..9729a026d976f16b68df0ef968dbc643fd743d7c 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/CartFactory.php @@ -61,6 +61,6 @@ class CartFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Cart')); + return new $requestedName($container->get(\VuFind\Cart::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Citation.php b/module/VuFind/src/VuFind/View/Helper/Root/Citation.php index dff3e52860b1077d7df7608ec9f4bd1a670a5c71..f509089a96f946c9031d9a1f4bab61066fcf04b3 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Citation.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Citation.php @@ -22,12 +22,14 @@ * @category VuFind * @package View_Helpers * @author Demian Katz <demian.katz@villanova.edu> + * @author Juha Luoma <juha.luoma@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ namespace VuFind\View\Helper\Root; use VuFind\Date\DateException; +use VuFind\I18n\Translator\TranslatorAwareInterface; /** * Citation view helper @@ -35,11 +37,15 @@ use VuFind\Date\DateException; * @category VuFind * @package View_Helpers * @author Demian Katz <demian.katz@villanova.edu> + * @author Juha Luoma <juha.luoma@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development Wiki */ class Citation extends \Zend\View\Helper\AbstractHelper + implements TranslatorAwareInterface { + use \VuFind\I18n\Translator\TranslatorAwareTrait; + /** * Citation details * @@ -82,16 +88,12 @@ class Citation extends \Zend\View\Helper\AbstractHelper public function __invoke($driver) { // Build author list: - $authors = []; - $primary = $driver->tryMethod('getPrimaryAuthor'); - if (empty($primary)) { - $primary = $driver->tryMethod('getCorporateAuthor'); - } - if (!empty($primary)) { - $authors[] = $primary; + $authors = (array)$driver->tryMethod('getPrimaryAuthors'); + if (empty($authors)) { + $authors = (array)$driver->tryMethod('getCorporateAuthors'); } - $secondary = $driver->tryMethod('getSecondaryAuthors'); - if (is_array($secondary) && !empty($secondary)) { + $secondary = (array)$driver->tryMethod('getSecondaryAuthors'); + if (!empty($secondary)) { $authors = array_unique(array_merge($authors, $secondary)); } @@ -271,7 +273,7 @@ class Citation extends \Zend\View\Helper\AbstractHelper } else { // Add other journal-specific details: $mla['pageRange'] = $this->getPageRange(); - $mla['journal'] = $this->capitalizeTitle($this->details['journal']); + $mla['journal'] = $this->capitalizeTitle($this->details['journal']); $mla['numberAndDate'] = $this->getMLANumberAndDate($volNumSeparator); return $partial('Citation/mla-article.phtml', $mla); } @@ -702,12 +704,12 @@ class Citation extends \Zend\View\Helper\AbstractHelper $i = 0; if (count($this->details['authors']) > $etAlThreshold) { $author = $this->details['authors'][0]; - $authorStr = $this->cleanNameDates($author) . ', et al'; + $authorStr = $this->cleanNameDates($author) . ', et al.'; } else { foreach ($this->details['authors'] as $author) { if (($i + 1 == count($this->details['authors'])) && ($i > 0)) { // Last - $authorStr .= ', and ' . + $authorStr .= ', ' . $this->translate('and') . ' ' . $this->reverseName($this->stripPunctuation($author)); } elseif ($i > 0) { $authorStr .= ', ' . diff --git a/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php index 18a6a4b9a50ed79c31c5df5555ea7050c6224645..7791872b75b075b8e3abe50d51875930eb33efb6 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/CitationFactory.php @@ -61,6 +61,6 @@ class CitationFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Date\Converter')); + return new $requestedName($container->get(\VuFind\Date\Converter::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php index cdaa45072584d81ba4df3b18a52e5890545ad088..d9f7d02f89b6d386b62136fa12fac8b3238841b9 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ConfigFactory.php @@ -61,6 +61,8 @@ class ConfigFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Config\PluginManager')); + return new $requestedName( + $container->get(\VuFind\Config\PluginManager::class) + ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php index fc4a74f885452432918100ae5cbdff82a96c9935..295182cd465fa8c117606797d19062e54020e74a 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ContentLoaderFactory.php @@ -64,7 +64,7 @@ class ContentLoaderFactory implements FactoryInterface // Use the requested service name to look up the appropriate content loader: $parts = explode('\\', $requestedName); $type = strtolower(array_pop($parts)); - $loader = $container->get('VuFind\Content\PluginManager')->get($type); + $loader = $container->get(\VuFind\Content\PluginManager::class)->get($type); return new ContentLoader($loader); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php index f86154c1c95b17cdc94b27fbdbfecf2a3671f69d..79f704c5974c6d59e9d8c1429e4bb7ba88e18ffd 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/DateTimeFactory.php @@ -61,6 +61,6 @@ class DateTimeFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Date\Converter')); + return new $requestedName($container->get(\VuFind\Date\Converter::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php index c5b0380e94b45fd53fa214c6cf7a9c0f0996fb5f..28506916316e8ab9760b52b14aef38015c3b1613 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/DisplayLanguageOptionFactory.php @@ -28,6 +28,7 @@ namespace VuFind\View\Helper\Root; use Interop\Container\ContainerInterface; +use Zend\Mvc\I18n\Translator; use Zend\ServiceManager\Factory\FactoryInterface; /** @@ -64,8 +65,7 @@ class DisplayLanguageOptionFactory implements FactoryInterface // We want to construct a separate translator instance for this helper, // since it configures different language/locale than the core shared // instance! - return new $requestedName( - \VuFind\Service\Factory::getTranslator($container) - ); + $factory = new \VuFind\I18n\Translator\TranslatorFactory(); + return new $requestedName($factory($container, Translator::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/DoiFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/DoiFactory.php index b552966621c6510f2febe6326c08058b3ae9f7ea..a64e6f8dd38bccf3f7600f9604f5fa1559932ee6 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/DoiFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/DoiFactory.php @@ -61,7 +61,8 @@ class DoiFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $helpers = $container->get('ViewHelperManager'); return new $requestedName($helpers->get('context'), $config->DOI ?? null); } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php index f61d09e92057af6ef97272f4e9765e67a98d2208..bc94baeee8d4e50766a2b52fb2e3d7d84f9a3f2b 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ExportFactory.php @@ -61,6 +61,6 @@ class ExportFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Export')); + return new $requestedName($container->get(\VuFind\Export::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php index 120bd276a05b86842dad2eb9a6f81d683357421d..4ac80ea0b6d6036e1c8f08785aca5c78c6bffeac 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/FeedbackFactory.php @@ -61,7 +61,8 @@ class FeedbackFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->Feedback->tab_enabled ?? false); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php index bcd67fff84f289858b03c64efefc24b73ec4fe5d..2512310196fce253d288380453b7ade375b51a51 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/FlashmessagesFactory.php @@ -62,7 +62,7 @@ class FlashmessagesFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } $messenger = $container->get('ControllerPluginManager') - ->get('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); + ->get(\Zend\Mvc\Plugin\FlashMessenger\FlashMessenger::class); return new $requestedName($messenger); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php index 5627bdf90dfd092b30b19bc69cc216b4e42caff1..92b36992aab55b602dc0def50bba242c8e031e36 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoordsFactory.php @@ -61,7 +61,7 @@ class GeoCoordsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\GeoFeatures\MapSelectionConfig') + $config = $container->get(\VuFind\GeoFeatures\MapSelectionConfig::class) ->getMapSelectionOptions(); return new $requestedName($config['default_coordinates']); } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php index 0d63b823b6ad138ec75b0b5094d3af4d8bdc6dc7..b33603241efd0e8e84c35016eb913207d7a09d97 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/GoogleAnalyticsFactory.php @@ -61,7 +61,8 @@ class GoogleAnalyticsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $key = $config->GoogleAnalytics->apiKey ?? false; $universal = $config->GoogleAnalytics->universal ?? false; return new $requestedName($key, $universal); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php index 218894eec934ee85575121d057214df625a74918..f9fb0b4c5879430bf2dd4a53f0e9869f611be508 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/HelpTextFactory.php @@ -61,8 +61,8 @@ class HelpTextFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $lang = $container->has('Zend\Mvc\I18n\Translator') - ? $container->get('Zend\Mvc\I18n\Translator')->getLocale() + $lang = $container->has(\Zend\Mvc\I18n\Translator::class) + ? $container->get(\Zend\Mvc\I18n\Translator::class)->getLocale() : 'en'; $helpers = $container->get('ViewHelperManager'); return new $requestedName($helpers->get('context'), $lang); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php index b41a08cf1d0953301fb0d07a1a38623ee59079ff..d8af099ef3ec4404754ffa1745b0e8e841780480 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/HistoryLabelFactory.php @@ -61,7 +61,8 @@ class HistoryLabelFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $labels = isset($config->SearchHistoryLabels) ? $config->SearchHistoryLabels->toArray() : []; $helpers = $container->get('ViewHelperManager'); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php index b83f818ee9c53de5660fc5ec4ae9907c84655dbd..bbe1f26ba021be08910312443d0976e13a6eaf2d 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/IlsFactory.php @@ -61,6 +61,6 @@ class IlsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\ILS\Connection')); + return new $requestedName($container->get(\VuFind\ILS\Connection::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php index 603b351ce93b50d04be2e6440ed40295bc33373f..3af02a7f9fe9cb4fab84ed2bb460ab8e0fe1bbb4 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/KeepAliveFactory.php @@ -61,7 +61,8 @@ class KeepAliveFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->Session->keepAlive ?? 0); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Linkify.php b/module/VuFind/src/VuFind/View/Helper/Root/Linkify.php new file mode 100644 index 0000000000000000000000000000000000000000..7116176ce35999a760c6908c3e71394d49925717 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Linkify.php @@ -0,0 +1,64 @@ +<?php +/** + * Linkify a string so that the links become clickable HTML + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Zend\View\Helper\AbstractHelper; + +/** + * Linkify a string so that the links become clickable HTML + * + * @category VuFind + * @package View_Helpers + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Linkify extends AbstractHelper +{ + /** + * Linkify a string + * + * @param string $str String to linkify (must be HTML-escaped) + * + * @return string + */ + public function __invoke($str) + { + $linkify = new \Misd\Linkify\Linkify(); + $proxyUrl = $this->getView()->plugin('proxyUrl'); + $escapeHtmlAttr = $this->getView()->plugin('escapeHtmlAttr'); + $callback = function ($url, $caption, $isEmail) use ($proxyUrl, + $escapeHtmlAttr + ) { + $url = html_entity_decode($url); + return '<a href="' . $escapeHtmlAttr($proxyUrl($url)) . '">' + . "$caption</a>"; + }; + return $linkify->process($str, ['callback' => $callback]); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Metadata.php b/module/VuFind/src/VuFind/View/Helper/Root/Metadata.php new file mode 100644 index 0000000000000000000000000000000000000000..5aadeb2a6aaf2bbcbfd88ff01f3cad626fe1b163 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Metadata.php @@ -0,0 +1,121 @@ +<?php +/** + * Metadata view helper + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +/** + * Metadata view helper + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Metadata extends \Zend\View\Helper\AbstractHelper +{ + /** + * Metadata configuration entries + * + * @var \Zend\Config\Config + */ + protected $config; + + /** + * Zend meta helper, used to embed html tags in the generated page + * + * @var \Zend\View\Helper\HeadMeta + */ + protected $metaHelper; + + /** + * Plugin Manager for vocabularies + * + * @var \VuFind\MetadataVocabulary\PluginManager + */ + protected $pluginManager; + + /** + * Constructor + * + * @param \VuFind\MetadataVocabulary\PluginManager $pm Plugin manager + * @param \Zend\Config\Config $config Configuration + * @param \Zend\View\Helper\HeadMeta $metaHelper Head meta helper + */ + public function __construct(\VuFind\MetadataVocabulary\PluginManager $pm, + \Zend\Config\Config $config, + \Zend\View\Helper\HeadMeta $metaHelper + ) { + $this->pluginManager = $pm; + $this->config = $config; + $this->metaHelper = $metaHelper; + } + + /** + * Get all active vocabularies for the current record. + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return array + */ + protected function getVocabularies(\VuFind\RecordDriver\AbstractBase $driver) + { + $recordDriverConfigs = isset($this->config->Vocabularies) + ? $this->config->Vocabularies->toArray() : []; + $retVal = []; + foreach ($recordDriverConfigs as $className => $vocabs) { + if ($driver instanceof $className) { + $retVal = array_merge($retVal, $vocabs); + } + } + return array_unique($retVal); + } + + /** + * Generate all metatags for RecordDriver and add to page + * + * Decide which Plugins to load for the given RecordDriver + * dependant on configuration. (only by class name, + * namespace will not be considered) + * + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver + * + * @return void + */ + public function generateMetatags(\VuFind\RecordDriver\AbstractBase $driver) + { + foreach ($this->getVocabularies($driver) as $metatagType) { + $vocabulary = $this->pluginManager->get($metatagType); + $mappedFields = $vocabulary->getMappedData($driver); + foreach ($mappedFields as $field => $values) { + foreach ($values as $value) { + $this->metaHelper->appendName($field, $value); + } + } + } + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/MetadataFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/MetadataFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..6c2d2260a49bb0d23775248bef440f0a3051c313 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/MetadataFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Metadata helper factory + * + * PHP version 7 + * + * Copyright (C) University of Tübingen 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Metadata helper factory + * + * @category VuFind + * @package Metadata_Vocabularies + * @author Mario Trojan <mario.trojan@uni-tuebingen.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class MetadataFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service Manager + * @param type $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws \Exception (options not allowed in this implementation) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + + return new Metadata( + $container->get(\VuFind\MetadataVocabulary\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class)->get('metadata'), + $container->get('ViewHelperManager')->get('HeadMeta') + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php index fc3ed6bd8754a25b27c9ce905020e827edd3a7d8..b0dd989ff10a8256cd5ba2d1f5599e4371a3d739 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/OpenUrlFactory.php @@ -61,7 +61,8 @@ class OpenUrlFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $openUrlRules = json_decode( file_get_contents( \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') @@ -69,7 +70,7 @@ class OpenUrlFactory implements FactoryInterface true ); $resolverPluginManager - = $container->get('VuFind\Resolver\Driver\PluginManager'); + = $container->get(\VuFind\Resolver\Driver\PluginManager::class); $helpers = $container->get('ViewHelperManager'); return new $requestedName( $helpers->get('context'), diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Overdrive.php b/module/VuFind/src/VuFind/View/Helper/Root/Overdrive.php new file mode 100644 index 0000000000000000000000000000000000000000..a616404a8a6251d0ebc316aac5a867e8675bded4 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Overdrive.php @@ -0,0 +1,105 @@ +<?php +/** + * Overdrive view helper + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use VuFind\DigitalContent\OverdriveConnector; + +/** + * Overdrive view helper + * + * @category VuFind + * @package View_Helpers + * @author Brent Palmer <brent-palmer@icpl.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Overdrive extends \Zend\View\Helper\AbstractHelper +{ + /** + * Overdrive connector. + * + * @var OverdriveConnector + */ + protected $connector; + + /** + * Constructor + * + * @param OverdriveConnector $connector Overdrive connector + */ + public function __construct(OverdriveConnector $connector = null) + { + $this->connector = $connector; + } + + /** + * Is Overdrive content active? + * + * @return bool + */ + public function showMyContentLink() + { + //if not configured at all, connector is null + if (null === $this->connector) { + return false; + } + $config = $this->connector->getConfig(); + if ($config->showMyContent == "always") { + return true; + } elseif ($config->showMyContent == "never") { + return false; + } else { + //assume that it is accessOnly + $result = $this->connector->getAccess(); + + if (!$result->status && $result->code == "od_account_noaccess") { + return false; + } + return true; + } + } + + /** + * Show the Overdrive API Admin Menu Item? + * + * @return bool + */ + public function showOverdriveAdminLink() + { + //if not configured at all, connector is null + if (null === $this->connector) { + return false; + } + $config = $this->connector->getConfig(); + if ($config->showOverdriveAdminMenu) { + return true; + } else { + return false; + } + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/OverdriveFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/OverdriveFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ee4ba74473a340bf0dfe6135e893857b7fe871a2 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/OverdriveFactory.php @@ -0,0 +1,82 @@ +<?php +/** + * Overdrive helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Overdrive helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public + * License + * @link https://vufind.org/wiki/development Wiki + */ +class OverdriveFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + // Only load the connector if we need to show + $config = $container->get('VuFind\Config\PluginManager')->get( + 'Overdrive' + ); + $connector = null; + $showMyContent = $config->Overdrive->showMyContent; + $showAdmin = $config->Overdrive->showOverdriveAdminMenu; + if ($showAdmin || $showMyContent != "never") { + $connector = $container->get( + 'VuFind\DigitalContent\OverdriveConnector' + ); + } + return new $requestedName($connector); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php index 8b17ded223004b449e89f88dcab7daa43cb4160a..a60b6ca6fb664bd5b20ee8886be2a80d6d2a2e15 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/PermissionFactory.php @@ -62,8 +62,8 @@ class PermissionFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Role\PermissionManager'), - $container->get('VuFind\Role\PermissionDeniedManager') + $container->get(\VuFind\Role\PermissionManager::class), + $container->get(\VuFind\Role\PermissionDeniedManager::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php index 7ce3270573a6ec081ede17a9c601a20d0d449afc..1c8297cfc0b7cad44cd0481c8c9aab9f387571c3 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Piwik.php @@ -59,6 +59,13 @@ class Piwik extends \Zend\View\Helper\AbstractHelper */ protected $searchPrefix; + /** + * Whether to disable cookies (see config.ini for details) + * + * @var bool + */ + protected $disableCookies; + /** * Whether to track use custom variables to track additional information * @@ -69,14 +76,14 @@ class Piwik extends \Zend\View\Helper\AbstractHelper /** * Request object * - * @var Zend\Http\PhpEnvironment\Request + * @var \Zend\Http\PhpEnvironment\Request */ protected $request; /** * Router object * - * @var Zend\Router\Http\RouteMatch + * @var \Zend\Router\Http\RouteMatch */ protected $router; @@ -123,6 +130,7 @@ class Piwik extends \Zend\View\Helper\AbstractHelper if (is_array($options)) { $this->siteId = $options['siteId']; $this->searchPrefix = $options['searchPrefix'] ?? ''; + $this->disableCookies = $options['disableCookies'] ?? ''; } else { $this->siteId = $options; } @@ -426,7 +434,7 @@ class Piwik extends \Zend\View\Helper\AbstractHelper protected function getOpeningTrackingCode() { $escape = $this->getView()->plugin('escapejs'); - return <<<EOT + $code = <<<EOT function initVuFindPiwikTracker{$this->timestamp}(){ var VuFindPiwikTracker = Piwik.getTracker(); @@ -436,6 +444,14 @@ function initVuFindPiwikTracker{$this->timestamp}(){ VuFindPiwikTracker.setCustomUrl('{$escape($this->getCustomUrl())}'); EOT; + if ($this->disableCookies) { + $code .= <<<EOT + VuFindPiwikTracker.disableCookies(); + +EOT; + } + + return $code; } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php index 27fcd3dfd854cb1d2ba717077a8360a71a89c420..92eb3b1c78a1a447a1d92b03ec0229016ef67a73 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/PiwikFactory.php @@ -61,11 +61,13 @@ class PiwikFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $url = $config->Piwik->url ?? false; $settings = [ 'siteId' => $config->Piwik->site_id ?? 1, - 'searchPrefix' => $config->Piwik->searchPrefix ?? null + 'searchPrefix' => $config->Piwik->searchPrefix ?? null, + 'disableCookies' => $config->Piwik->disableCookies ?? false ]; $customVars = $config->Piwik->custom_variables ?? false; $request = $container->get('Request'); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Printms.php b/module/VuFind/src/VuFind/View/Helper/Root/Printms.php index d8708293bc4a19b829794f011828932e8e91e53e..656fb3df46ccc086b9c47b7175d87998570e8f9b 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Printms.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Printms.php @@ -49,6 +49,10 @@ class Printms extends AbstractHelper */ public function __invoke($ms) { + // If we can't do the math, don't bother formatting the value: + if (!is_numeric($ms)) { + return $ms; + } $seconds = floor($ms / 1000); $ms = ($ms % 1000); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php index ea007570fe15226a6d945e2f1fc3c55ebd761bba..1cb09ccbcd019703401a9131133117ded2838cd2 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/ProxyUrlFactory.php @@ -61,7 +61,8 @@ class ProxyUrlFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php index 411d01e06dbe41dce44d1301978d70fe01df4cc7..45ac126b38601f94d3aad717972f87b483b95b27 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecaptchaFactory.php @@ -62,8 +62,8 @@ class RecaptchaFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Service\ReCaptcha'), - $container->get('VuFind\Config\PluginManager')->get('config') + $container->get(\VuFind\Service\ReCaptcha::class), + $container->get(\VuFind\Config\PluginManager::class)->get('config') ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php index 5ae8ecde63f25dc907750acad951a03e06a81123..65872ea0e2f0ec4b07559cd29487f1fc4e4e4da5 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordFactory.php @@ -61,9 +61,10 @@ class RecordFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $helper = new $requestedName($config); - $helper->setCoverRouter($container->get('VuFind\Cover\Router')); + $helper->setCoverRouter($container->get(\VuFind\Cover\Router::class)); return $helper; } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php index 59c2814aa91726afb539ff4f7491020f16c06119..fb4e746f063e23ac14e14dbfd33508af32e5aeb8 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordLink.php @@ -180,15 +180,18 @@ class RecordLink extends \Zend\View\Helper\AbstractHelper * representing record to link to, or source|id pipe-delimited string * @param string $tab Optional record * tab to access + * @param array $query Optional query params * * @return string */ - public function getTabUrl($driver, $tab = null) + public function getTabUrl($driver, $tab = null, $query = []) { // Build the URL: $urlHelper = $this->getView()->plugin('url'); - $details = $this->router->getTabRouteDetails($driver, $tab); - return $urlHelper($details['route'], $details['params']); + $details = $this->router->getTabRouteDetails($driver, $tab, $query); + return $urlHelper( + $details['route'], $details['params'], $details['options'] ?? [] + ); } /** diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php index c326093213766ac2b4f8e5d8daa85956881c5b66..ca3912a33320fd2ffab2de964dfc0b02069538ad 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RecordLinkFactory.php @@ -61,6 +61,6 @@ class RecordLinkFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Record\Router')); + return new $requestedName($container->get(\VuFind\Record\Router::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php index 0000f6291959f159ca1103b73b8b854e9fa208be..3c138fb907879b30711373914328fba25b062a42 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RelaisFactory.php @@ -61,7 +61,8 @@ class RelaisFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $urlHelper = $container->get('ViewHelperManager')->get('url'); $loginUrl = $urlHelper->__invoke('relais-login'); return new $requestedName($config->Relais ?? null, $loginUrl); diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php index bb65c7ee17b2669881df3589b98f19f067fa4b87..7a8b981b652a3094df252b06615548c1e958d507 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RelatedFactory.php @@ -62,9 +62,9 @@ class RelatedFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Related\PluginManager'), - $container->get('VuFind\Config\PluginManager'), - $container->get('VuFind\Search\Options\PluginManager') + $container->get(\VuFind\Related\PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $container->get(\VuFind\Search\Options\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php b/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php index 31021ec000cf8d9c34d9a262ebab5334fb0152e2..e10f73777245a87b5e5e03a917cd79e0fe6973d3 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/RenderArray.php @@ -54,11 +54,14 @@ class RenderArray extends AbstractHelper public function __invoke($tpl, $arr, $rows) { $html = ''; + $translate = $this->view->plugin('translate'); foreach ($rows as $label => $key) { if (isset($arr[$key])) { + $value = $arr[$key] instanceof \VuFind\I18n\TranslatableString + ? $translate($arr[$key]) : $arr[$key]; $html .= str_replace( ['%%LABEL%%', '%%VALUE%%'], - [$label, $this->view->escapeHtml($arr[$key])], + [$label, $this->view->escapeHtml($value)], $tpl ); } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php index d84fef199fe0ae4c3ace64b4ed2aea3ec88e285e..f1b349dce36684331eca85681c1eae07a078f5cc 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SafeMoneyFormatFactory.php @@ -61,7 +61,8 @@ class SafeMoneyFormatFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); $defaultCurrency = $config->Site->defaultCurrency ?? null; return new $requestedName($defaultCurrency); } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php index 6e0eb666f4eadfa25121a31b8c0dd3395c6874d4..0fbda8da1910f5beb3c483a69391c2047f3e8829 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchBox.php @@ -162,6 +162,19 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper && $this->config['General']['combinedHandlers']; } + /** + * Helper method: get special character to represent operator in filter + * + * @param string $operator Operator + * + * @return string + */ + protected function getOperatorCharacter($operator) + { + static $map = ['NOT' => '-', 'OR' => '~']; + return $map[$operator] ?? ''; + } + /** * Get an array of filter information for use by the "retain filters" feature * of the search box. Returns an array of arrays with 'id' and 'value' keys used @@ -177,7 +190,10 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper $results = []; foreach ($filterList as $field => $data) { foreach ($data as $value) { - $results[] = "$field:\"$value\""; + $results[] = is_array($value) + ? $this->getOperatorCharacter($value['operator'] ?? '') + . $value['field'] . ':"' . $value['value'] . '"' + : "$field:\"$value\""; } } foreach ($checkboxFilters as $current) { @@ -239,6 +255,28 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper : $this->getBasicHandlers($activeSearchClass, $activeHandler); } + /** + * Get number of active filters + * + * @param array $checkboxFilters Checkbox filters + * @param array $filterList Other filters + * + * @return int + */ + public function getFilterCount($checkboxFilters, $filterList) + { + $result = 0; + foreach ($checkboxFilters as $filter) { + if ($filter['selected']) { + ++$result; + } + } + foreach ($filterList as $filter) { + $result += count($filter); + } + return $result; + } + /** * Support method for getHandlers() -- load basic settings. * @@ -283,12 +321,19 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper throw new \Exception('CombinedHandlers configuration incomplete.'); } + // Fill in missing group settings, if necessary: + if (count($settings['group'] ?? []) < $typeCount) { + $settings['group'] = array_fill(0, $typeCount, false); + } + // Add configuration for the current search class if it is not already // present: if (!in_array($activeSearchClass, $settings['target'])) { $settings['type'][] = 'VuFind'; $settings['target'][] = $activeSearchClass; $settings['label'][] = $activeSearchClass; + $settings['group'][] + = $this->config['General']['defaultGroupLabel'] ?? false; } $this->cachedConfigs[$activeSearchClass] = $settings; @@ -318,7 +363,8 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper 'value' => 'External:' . $alphaBrowseUrl, 'label' => $labelPrefix . $this->getView()->translate($label), 'indent' => $indent, - 'selected' => $activeHandler == 'AlphaBrowse:' . $source + 'selected' => $activeHandler == 'AlphaBrowse:' . $source, + 'group' => $this->config['General']['alphaBrowseGroup'] ?? false, ]; } return $handlers; @@ -364,11 +410,23 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper ) { $backupSelectedIndex = count($handlers); } + // Depending on whether or not the current section has a label, + // we'll either want to override the first label and indent + // subsequent ones, or else use all default labels without + // any indentation. + if (empty($label)) { + $finalLabel = $searchDesc; + $indent = false; + } else { + $finalLabel = $j == 1 ? $label : $searchDesc; + $indent = $j == 1 ? false : true; + } $handlers[] = [ 'value' => $type . ':' . $target . '|' . $searchVal, - 'label' => $j == 1 ? $label : $searchDesc, - 'indent' => $j == 1 ? false : true, - 'selected' => $selected + 'label' => $finalLabel, + 'indent' => $indent, + 'selected' => $selected, + 'group' => $settings['group'][$i], ]; } @@ -376,13 +434,16 @@ class SearchBox extends \Zend\View\Helper\AbstractHelper if ($target === 'Solr' && $this->alphaBrowseOptionsEnabled()) { $addedBrowseHandlers = true; $handlers = array_merge( - $handlers, $this->getAlphaBrowseHandlers($activeHandler) + $handlers, + // Only indent alphabrowse handlers if label is non-empty: + $this->getAlphaBrowseHandlers($activeHandler, !empty($label)) ); } } elseif ($type == 'External') { $handlers[] = [ 'value' => $type . ':' . $target, 'label' => $label, - 'indent' => false, 'selected' => false + 'indent' => false, 'selected' => false, + 'group' => $settings['group'][$i], ]; } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php index 76edeb356aaab53c2d8498ec902f92209704882d..427ae595273301edf06b948f5e5f287a14ae3f16 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchBoxFactory.php @@ -61,13 +61,13 @@ class SearchBoxFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager'); + $config = $container->get(\VuFind\Config\PluginManager::class); $mainConfig = $config->get('config'); $searchboxConfig = $config->get('searchbox')->toArray(); $includeAlphaOptions = $searchboxConfig['General']['includeAlphaBrowse'] ?? false; return new $requestedName( - $container->get('VuFind\Search\Options\PluginManager'), + $container->get(\VuFind\Search\Options\PluginManager::class), $searchboxConfig, isset($mainConfig->SearchPlaceholder) ? $mainConfig->SearchPlaceholder->toArray() : [], diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php index a28d01f0dd9a3ec82d48061a81a5312cf8e575b6..c33797e0a45cf5cad0f3a06e5aa39b5369266b81 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemory.php @@ -114,4 +114,46 @@ class SearchMemory extends AbstractHelper { return $this->memory->retrieveLastSetting($context, 'sort'); } + + /** + * Get the URL to edit the last search. + * + * @param string $searchClassId Search class + * @param string $action Action to take + * @param mixed $value Value for action + * + * @return string + */ + public function getEditLink($searchClassId, $action, $value) + { + $query = compact('searchClassId') + [$action => $value]; + $url = $this->getView()->plugin('url'); + return $url('search-editmemory', [], compact('query')); + } + + /** + * Retrieve the parameters of the last search by the search class + * + * @param string $searchClassId Search class + * + * @return \VuFind\Search\Base\Params + */ + public function getLastSearchParams($searchClassId) + { + $lastUrl = $this->memory->retrieveSearch(); + $queryParams = $lastUrl ? parse_url($lastUrl, PHP_URL_QUERY) : ''; + $request = new \Zend\StdLib\Parameters(); + $request->fromString($queryParams); + $paramsPlugin = $this->getView()->plugin('searchParams'); + $params = $paramsPlugin($searchClassId); + // Make sure the saved URL represents search results from $searchClassId; + // if the user jumps from search results of one backend to a record of a + // different backend, we don't want to display irrelevant filters. If there + // is a backend mismatch, don't initialize the parameter object! + $expectedPath = $this->view->url($params->getOptions()->getSearchAction()); + if (substr($lastUrl, 0, strlen($expectedPath)) === $expectedPath) { + $params->initFromRequest($request); + } + return $params; + } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php index a4ca1a90c4bbe995682197fd60de1a94f619a903..53e76ccb2f96e321549246a7e66b417aa50a3151 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchMemoryFactory.php @@ -61,6 +61,6 @@ class SearchMemoryFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - return new $requestedName($container->get('VuFind\Search\Memory')); + return new $requestedName($container->get(\VuFind\Search\Memory::class)); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php index cf3deccbc2e44729fd9997d887d0850cd3a1983e..f0ec2b35d671e4a3bf461808f5421b9e4d63052e 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchOptionsFactory.php @@ -62,7 +62,7 @@ class SearchOptionsFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Search\Options\PluginManager') + $container->get(\VuFind\Search\Options\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php index 73b6584b26e4d7a74fae00866cb97453ea6f2f27..c00e68d58afee016592c0e2d3fd2e5a3586c0ac6 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchParamsFactory.php @@ -62,7 +62,7 @@ class SearchParamsFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Search\Params\PluginManager') + $container->get(\VuFind\Search\Params\PluginManager::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php index f8acf7e416d2a561cc68d7a3fda034b2617a9923..f8d84399d30e394f817abeda80bb2bb429752883 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SearchTabsFactory.php @@ -62,9 +62,9 @@ class SearchTabsFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } return new $requestedName( - $container->get('VuFind\Search\Results\PluginManager'), + $container->get(\VuFind\Search\Results\PluginManager::class), $container->get('ViewHelperManager')->get('url'), - $container->get('VuFind\Search\SearchTabsHelper') + $container->get(\VuFind\Search\SearchTabsHelper::class) ); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ServerUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ServerUrlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..bba22b7e3d5bea32b93700264f460d775d8da61e --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ServerUrlFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * ServerUrl helper factory. This uses the core Zend helper but configures it + * according to VuFind settings. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ServerUrl helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ServerUrlFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $cfg = $container->get(\VuFind\Config\PluginManager::class)->get('config'); + $helper = new $requestedName(); + if ($cfg->Site->reverse_proxy ?? false) { + $helper->setUseProxy(true); + } + return $helper; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php new file mode 100644 index 0000000000000000000000000000000000000000..ea70d1be214eab732cedc3550a5eaf5f2142f1e4 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrl.php @@ -0,0 +1,71 @@ +<?php +/** + * View helper for shortening URLs. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use VuFind\UrlShortener\UrlShortenerInterface; + +/** + * View helper for formatting dates and times + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ShortenUrl extends \Zend\View\Helper\AbstractHelper +{ + /** + * URL shortener + * + * @var UrlShortenerInterface + */ + protected $shortener; + + /** + * Constructor + * + * @param UrlShortenerInterface $shortener URL shortener + */ + public function __construct(UrlShortenerInterface $shortener) + { + $this->shortener = $shortener; + } + + /** + * Shorten a URL + * + * @param string $url URL to shorten + * + * @return string + */ + public function __invoke($url) + { + return $this->shortener->shorten($url); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..cbf78fc8e03b17d2c1480513e5fd894e1e4412bb --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/ShortenUrlFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * ShortenUrl helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ShortenUrl helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class ShortenUrlFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\UrlShortener\UrlShortenerInterface::class) + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php index 1ac1eb26d590e8451bcc71798d75874c20e49e5e..08be5432d73724973be8fa332d1e102f9b4add4e 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SyndeticsPlusFactory.php @@ -61,7 +61,8 @@ class SyndeticsPlusFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->Syndetics ?? null); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php index 832f79140c610b24f3329286181671d26b381b11..1da45bbbedb933a3a7a3ac0ea4e6f0e9560eb4cc 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/SystemEmailFactory.php @@ -61,7 +61,8 @@ class SystemEmailFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $config = $container->get('VuFind\Config\PluginManager')->get('config'); + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); return new $requestedName($config->Site->email ?? ''); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/TransEscWithPrefix.php b/module/VuFind/src/VuFind/View/Helper/Root/TransEscWithPrefix.php new file mode 100644 index 0000000000000000000000000000000000000000..914554818040277d2c181d134794e9fc97bb2aa6 --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/TransEscWithPrefix.php @@ -0,0 +1,67 @@ +<?php +/** + * Translate with prefix + escape view helper + * + * PHP version 7 + * + * Copyright (C) Villanova University 2010. + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +/** + * Translate with prefix + escape view helper + * + * Like transEsc, but applies a prefix to the translation key. + * + * @category VuFind + * @package View_Helpers + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TransEscWithPrefix extends \Zend\View\Helper\AbstractHelper + implements \VuFind\I18n\Translator\TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + + /** + * Translate and escape a location + * + * @param string $prefix Translation key prefix + * @param string|object $str String to translate + * @param array $tokens Tokens to inject into the translated string + * @param string $default Default value to use if no translation is found + * (null for no default). + * + * @return string + */ + public function __invoke($prefix, $str, $tokens = [], $default = null) + { + $escaper = $this->getView()->plugin('escapeHtml'); + return $escaper( + $this->translateWithPrefix($prefix, $str, $tokens, $default) + ); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Url.php b/module/VuFind/src/VuFind/View/Helper/Root/Url.php new file mode 100644 index 0000000000000000000000000000000000000000..b886eef285792aad717610657fc9a9483399d8bd --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/Url.php @@ -0,0 +1,105 @@ +<?php +/** + * Url view helper (extending core Zend helper with additional functionality) + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Zend\Http\PhpEnvironment\Request; + +/** + * Url view helper (extending core Zend helper with additional functionality) + * + * @category VuFind + * @package View_Helpers + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class Url extends \Zend\View\Helper\Url +{ + /** + * Request (or null if unavailable) + * + * @var Request + */ + protected $request = null; + + /** + * Constructor + * + * @param Request $request Request object for GET parameters + */ + public function __construct(Request $request) + { + $this->request = $request; + } + + /** + * Generates a url given the name of a route. + * + * @param string $name Name of the route + * @param array $params Parameters for the link + * @param array|Traversable $options Options for the route + * @param bool $reuseMatchedParams Whether to reuse matched + * parameters + * + * @see Zend\Mvc\Router\RouteInterface::assemble() + * @see Zend\Router\RouteInterface::assemble() + * + * @throws Exception\RuntimeException If no RouteStackInterface was provided + * @throws Exception\RuntimeException If no RouteMatch was provided + * @throws Exception\RuntimeException If RouteMatch didn't contain a matched + * route name + * @throws Exception\InvalidArgumentException If the params object was not an + * array or Traversable object. + * + * @return string Url For the link href attribute + */ + public function __invoke( + $name = null, $params = [], $options = [], $reuseMatchedParams = false + ) { + // If argument list is empty, return object for method access: + return func_num_args() == 0 ? $this : parent::__invoke(...func_get_args()); + } + + /** + * Get URL with current GET parameters and add one + * + * @param array $params Key-paired parameters + * @param bool $reuseMatchedParams Whether to reuse matched parameters + * + * @return string + */ + public function addQueryParameters($params, $reuseMatchedParams = true) + { + $requestQuery = $this->request->getQuery()->toArray(); + $options = [ + 'query' => array_merge($requestQuery, $params), + 'normalize_path' => false, // fix for VUFIND-1392 + ]; + return $this->__invoke(null, [], $options, $reuseMatchedParams); + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UrlFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/UrlFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7ed0162d34e8f5402baa51e5f9f91fe2be25492b --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/UrlFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * Url helper factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\Router\RouteMatch; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Url helper factory. + * + * @category VuFind + * @package View_Helpers + * @author Chris Hallberg <challber@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class UrlFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + $helper = new $requestedName($container->get('Request')); + $helper->setRouter($container->get('HttpRouter')); + + $match = $container->get('Application') + ->getMvcEvent() + ->getRouteMatch(); + + if ($match instanceof RouteMatch) { + $helper->setRouteMatch($match); + } + + return $helper; + } +} diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php index 584c20ad92882172132d8a17ba039c4725037a5c..4810779adb26e92b50fdabd87bba01948f809fc5 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserListFactory.php @@ -61,9 +61,9 @@ class UserListFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $sessionManager = $container->get('Zend\Session\SessionManager'); + $sessionManager = $container->get(\Zend\Session\SessionManager::class); $session = new \Zend\Session\Container('List', $sessionManager); - $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); return new $requestedName($session, $capabilities->getListSetting()); } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php b/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php index c7ba6c028b173df8fb71163d0c1f4bf212b7a524..192d25fffc7ff3a319b26bac277a69b614f0e80f 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/UserTagsFactory.php @@ -61,7 +61,7 @@ class UserTagsFactory implements FactoryInterface if (!empty($options)) { throw new \Exception('Unexpected options sent to factory.'); } - $capabilities = $container->get('VuFind\Config\AccountCapabilities'); + $capabilities = $container->get(\VuFind\Config\AccountCapabilities::class); return new $requestedName($capabilities->getTagSetting()); } } diff --git a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php index 746b395cfb5c2a20f77e25a2381513d869bc5341..575407fd728b50ceeda989bf49fe34ca4e61a249 100644 --- a/module/VuFind/src/VuFind/XSLT/Import/VuFind.php +++ b/module/VuFind/src/VuFind/XSLT/Import/VuFind.php @@ -69,7 +69,7 @@ class VuFind */ public static function getChangeTracker() { - return static::$serviceLocator->get('VuFind\Db\Table\PluginManager') + return static::$serviceLocator->get(\VuFind\Db\Table\PluginManager::class) ->get('ChangeTracker'); } @@ -82,7 +82,7 @@ class VuFind */ public static function getConfig($config = 'config') { - return static::$serviceLocator->get('VuFind\Config\PluginManager') + return static::$serviceLocator->get(\VuFind\Config\PluginManager::class) ->get($config); } @@ -393,16 +393,11 @@ class VuFind */ public static function xmlAsText($in) { - // Ensure that $in is an array: - if (!is_array($in)) { - $in = [$in]; - } - // Start building return value: $text = ''; // Extract all text: - foreach ($in as $current) { + foreach ((array)$in as $current) { // Convert DOMElement to SimpleXML: $xml = simplexml_import_dom($current); @@ -427,12 +422,7 @@ class VuFind */ public static function removeTagAndReturnXMLasText($in, $tag) { - // Ensure that $in is an array: - if (!is_array($in)) { - $in = [$in]; - } - - foreach ($in as $current) { + foreach ((array)$in as $current) { $matches = $current->getElementsByTagName($tag); foreach ($matches as $match) { $current->removeChild($match); diff --git a/module/VuFind/src/VuFind/XSLT/Importer.php b/module/VuFind/src/VuFind/XSLT/Importer.php index 0b2111a9573e6fef08b24b65d10a36cdf44585ff..caad2b7f2dc792a73ffb3afe3d04b0d56eca43af 100644 --- a/module/VuFind/src/VuFind/XSLT/Importer.php +++ b/module/VuFind/src/VuFind/XSLT/Importer.php @@ -81,7 +81,7 @@ class Importer // Save the results (or just display them, if in test mode): if (!$testMode) { - $solr = $this->serviceLocator->get('VuFind\Solr\Writer'); + $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); $solr->save($index, new RawXMLDocument($xml)); } else { Console::write($xml . "\n"); diff --git a/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php b/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php index 56f87a259c1993aca378846495e1b19387577881..59d2fcfd19a79bd6cf3101bfd30a6df1684bb01c 100644 --- a/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php +++ b/module/VuFind/src/VuFindTest/Unit/AjaxHandlerTest.php @@ -49,7 +49,7 @@ abstract class AjaxHandlerTest extends MockContainerTest */ protected function getMockUser() { - return $this->container->get('VuFind\Db\Row\User'); + return $this->container->get(\VuFind\Db\Row\User::class); } /** diff --git a/module/VuFind/src/VuFindTest/Unit/AutoRetryTrait.php b/module/VuFind/src/VuFindTest/Unit/AutoRetryTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..42a3dbbb7bc03933d0faa4363eadc93220f0f8b4 --- /dev/null +++ b/module/VuFind/src/VuFindTest/Unit/AutoRetryTrait.php @@ -0,0 +1,109 @@ +<?php + +/** + * Trait introducing an annotation that can be used to auto-retry tests that may + * fail intermittently due to timing issues (e.g. Mink integration tests). + * + * Inspired by discussion here at https://stackoverflow.com/questions/7705169 + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Unit; + +use PHPUnit\Framework\SkippedTestError; + +/** + * Trait introducing an annotation that can be used to auto-retry tests that may + * fail intermittently due to timing issues (e.g. Mink integration tests). + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +trait AutoRetryTrait +{ + /** + * Flag whether we ran out of retries on a prior test. + * + * @var bool + */ + protected static $failedAfterRetries = false; + + /** + * Override PHPUnit's main run method, introducing annotation-based retry + * behavior. + * + * @return void + */ + public function runBare(): void + { + // Fetch retry count from annotations, but make sure it's a sane number; + // default to a single attempt with no retries unless told otherwise. + // Also skip retries if a past test has failed after running out of + // retries -- one failed test is likely to have a knock-on effect on + // subsequent tests, and retrying will just waste time before showing + // the cause of the initial error. We only really want to retry if it + // will prevent ANY failures from occurring. + $annotations = $this->getAnnotations(); + $retryCountAnnotation = $annotations['method']['retry'][0] + ?? $annotations['class']['retry'][0] ?? 0; + $retryCount = !self::$failedAfterRetries && $retryCountAnnotation > 0 + ? $retryCountAnnotation : 0; + + // Also fetch retry callbacks, if any, from annotations; always include + // standard 'tearDown' method: + $retryCallbacks = $annotations['method']['retryCallback'] ?? []; + $retryCallbacks[] = 'tearDown'; + + // Run through all of the attempts... Note that even if retryCount is 0, + // we still need to run the test once (single attempt, no retries)... + // hence the $retryCount + 1 below. + for ($i = 0; $i < $retryCount + 1; $i++) { + try { + parent::runBare(); + // No exception thrown? We can return as normal. + return; + } catch (\Exception $e) { + // Don't retry skipped tests! + if (get_class($e) == SkippedTestError::class) { + throw $e; + } + // Execute callbacks for interrupted test. + foreach ($retryCallbacks as $callback) { + if (is_callable([$this, $callback])) { + $this->{$callback}(); + } + } + } + } + + // If we got this far, something went wrong... under healthy circumstances, + // we should have returned from inside the loop above. $e should be set from + // within the catch above, so if it's unset, something weird has occurred. + self::$failedAfterRetries = true; + throw $e ?? new \Exception('Unexpected state reached'); + } +} diff --git a/module/VuFind/src/VuFindTest/Unit/DbTestCase.php b/module/VuFind/src/VuFindTest/Unit/DbTestCase.php index 9c7ea6eb35bebc4f6cca4f7b3e41e0bbecedd11a..5f288fd53ae524b659e3651e03db8022694b7d28 100644 --- a/module/VuFind/src/VuFindTest/Unit/DbTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/DbTestCase.php @@ -105,16 +105,16 @@ abstract class DbTestCase extends TestCase $sm = parent::getServiceManager(); // Add database service: - if (!$sm->has('VuFind\Db\Table\PluginManager')) { + if (!$sm->has(\VuFind\Db\Table\PluginManager::class)) { $dbFactory = new \VuFind\Db\AdapterFactory( - $sm->get('VuFind\Config\PluginManager')->get('config') + $sm->get(\VuFind\Config\PluginManager::class)->get('config') ); $sm->setService('Zend\Db\Adapter\Adapter', $dbFactory->getAdapter()); $this->addTableManager($sm); $this->addRowManager($sm); $sm->setService( 'Zend\Session\SessionManager', - $this->createMock('Zend\Session\SessionManager') + $this->createMock(\Zend\Session\SessionManager::class) ); // Override the configuration so PostgreSQL tests can work: @@ -154,6 +154,6 @@ abstract class DbTestCase extends TestCase public function getTable($table) { $sm = $this->getServiceManager(); - return $sm->get('VuFind\Db\Table\PluginManager')->get($table); + return $sm->get(\VuFind\Db\Table\PluginManager::class)->get($table); } } diff --git a/module/VuFind/src/VuFindTest/Unit/DemoDriverTestTrait.php b/module/VuFind/src/VuFindTest/Unit/DemoDriverTestTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..e2c7857869c2169c6674e7d7c10fab71c8783740 --- /dev/null +++ b/module/VuFind/src/VuFindTest/Unit/DemoDriverTestTrait.php @@ -0,0 +1,128 @@ +<?php + +/** + * Trait with utility methods for configuring the demo driver in a test + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Unit; + +/** + * Trait with utility methods for configuring the demo driver in a test + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +trait DemoDriverTestTrait +{ + /** + * Get transaction JSON for Demo.ini. + * + * @param string $bibId Bibliographic record ID to create fake item info for. + * + * @return array + */ + protected function getFakeTransactions($bibId) + { + $rawDueDate = strtotime("now +5 days"); + return json_encode( + [ + [ + 'duedate' => $rawDueDate, + 'rawduedate' => $rawDueDate, + 'dueStatus' => 'due', + 'barcode' => 1234567890, + 'renew' => 0, + 'renewLimit' => 1, + 'request' => 0, + 'id' => $bibId, + 'source' => 'Solr', + 'item_id' => 0, + 'renewable' => true, + ] + ] + ); + } + + /** + * Get Demo.ini override settings for testing ILS functions. + * + * @param string $bibId Bibliographic record ID to create fake item info for. + * + * @return array + */ + protected function getDemoIniOverrides($bibId = 'testsample1') + { + return [ + 'Records' => [ + 'transactions' => $this->getFakeTransactions($bibId), + ], + 'Failure_Probabilities' => [ + 'cancelHolds' => 0, + 'cancelILLRequests' => 0, + 'cancelStorageRetrievalRequests' => 0, + 'checkILLRequestIsValid' => 0, + 'checkRenewBlock' => 0, + 'checkRequestIsValid' => 0, + 'checkStorageRetrievalRequestIsValid' => 0, + 'getAccountBlocks' => 0, + 'getDefaultRequestGroup' => 0, + 'getHoldDefaultRequiredDate' => 0, + 'getRequestBlocks' => 0, + 'placeHold' => 0, + 'placeILLRequest' => 0, + 'placeStorageRetrievalRequest' => 0, + 'renewMyItems' => 0, + ], + 'Holdings' => [$bibId => json_encode([$this->getFakeItem()])], + 'Users' => ['catuser' => 'catpass'], + ]; + } + + /** + * Get a fake item record for inclusion in the Demo driver configuration. + * + * @return array + */ + protected function getFakeItem() + { + return [ + 'barcode' => '12345678', + 'availability' => true, + 'status' => 'Available', + 'location' => 'Test Location', + 'locationhref' => false, + 'reserve' => 'N', + 'callnumber' => 'Test Call Number', + 'duedate' => '', + 'is_holdable' => true, + 'addLink' => true, + 'addStorageRetrievalRequestLink' => 'check', + 'addILLRequestLink' => 'check', + ]; + } +} diff --git a/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php b/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php index 1b8feabd351e8df17f7285b1f8beefcc956c1507..671fe354be655fd3ca77cf5d6616764d38bebd2b 100644 --- a/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/MinkTestCase.php @@ -31,6 +31,7 @@ namespace VuFindTest\Unit; use Behat\Mink\Driver\Selenium2Driver; use Behat\Mink\Element\Element; use Behat\Mink\Session; +use DMore\ChromeDriver\ChromeDriver; use VuFind\Config\Locator as ConfigLocator; use VuFind\Config\Writer as ConfigWriter; @@ -128,11 +129,11 @@ abstract class MinkTestCase extends DbTestCase */ protected function snooze($secs = 1) { - $snoozeMultiplier = intval(getenv('VUFIND_SNOOZE_MULTIPLIER')); - if ($snoozeMultiplier < 1) { + $snoozeMultiplier = floatval(getenv('VUFIND_SNOOZE_MULTIPLIER')); + if ($snoozeMultiplier <= 0) { $snoozeMultiplier = 1; } - sleep($secs * $snoozeMultiplier); + usleep(1000000 * $secs * $snoozeMultiplier); } /** @@ -154,8 +155,11 @@ abstract class MinkTestCase extends DbTestCase */ protected function getMinkDriver() { - $env = getenv('VUFIND_SELENIUM_BROWSER'); - $browser = $env ? $env : 'firefox'; + $driver = getenv('VUFIND_MINK_DRIVER') ?? 'selenium'; + if ($driver === 'chrome') { + return new ChromeDriver('http://localhost:9222', null, 'data:;'); + } + $browser = getenv('VUFIND_SELENIUM_BROWSER') ?? 'firefox'; return new Selenium2Driver($browser); } @@ -239,10 +243,34 @@ abstract class MinkTestCase extends DbTestCase $session = $this->getMinkSession(); $session->wait($timeout, "$('$selector').length > 0"); $result = $page->find('css', $selector); - $this->assertTrue(is_object($result)); + $this->assertTrue(is_object($result), "Selector not found: $selector"); return $result; } + /** + * Click on a CSS element. + * + * @param Element $page Page element + * @param string $selector CSS selector + * @param int $timeout Wait timeout (in ms) + * + * @return mixed + */ + protected function clickCss(Element $page, $selector, $timeout = 1000) + { + $result = $this->findCss($page, $selector, $timeout); + for ($tries = 0; $tries < 3; $tries++) { + try { + $result->click(); + return $result; + } catch (\Exception $e) { + // Expected click didn't work... snooze and retry + $this->snooze(); + } + } + throw $e ?? new \Exception('Unexpected state reached.'); + } + /** * Set a value within an element selected via CSS; retry if set fails * due to browser bugs. diff --git a/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php b/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php index d4c3dda218bbd6729dca9e7fce84ffe07569a005..ba71e8255449055039a6808ef38618db2fdbdb9b 100644 --- a/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/RecommendDeferredTestCase.php @@ -77,7 +77,7 @@ abstract class RecommendDeferredTestCase extends TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -96,7 +96,7 @@ abstract class RecommendDeferredTestCase extends TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/src/VuFindTest/Unit/TestCase.php b/module/VuFind/src/VuFindTest/Unit/TestCase.php index eeb78a73326e9b396616e21981323c1c0e877ebf..4ceead7268a231c5da4303f29a3e2a3b18f8e7c0 100644 --- a/module/VuFind/src/VuFindTest/Unit/TestCase.php +++ b/module/VuFind/src/VuFindTest/Unit/TestCase.php @@ -28,6 +28,8 @@ */ namespace VuFindTest\Unit; +use VuFind\Search\Factory\UrlQueryHelperFactory; + /** * Abstract base class for PHPUnit test cases. * @@ -145,73 +147,79 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase $this->serviceManager, [ 'abstract_factories' => - ['VuFind\Search\Options\PluginFactory'], + [\VuFind\Search\Options\PluginFactory::class], ] ); $this->serviceManager->setService( - 'VuFind\Search\Options\PluginManager', $optionsFactory + \VuFind\Search\Options\PluginManager::class, $optionsFactory ); $paramsFactory = new \VuFind\Search\Params\PluginManager( $this->serviceManager, [ 'abstract_factories' => - ['VuFind\Search\Params\PluginFactory'], + [\VuFind\Search\Params\PluginFactory::class], ] ); $this->serviceManager->setService( - 'VuFind\Search\Params\PluginManager', $paramsFactory + \VuFind\Search\Params\PluginManager::class, $paramsFactory ); $resultsFactory = new \VuFind\Search\Results\PluginManager( $this->serviceManager, [ 'abstract_factories' => - ['VuFind\Search\Results\PluginFactory'], + [\VuFind\Search\Results\PluginFactory::class], ] ); $this->serviceManager->setService( - 'VuFind\Search\Results\PluginManager', $resultsFactory + \VuFind\Search\Results\PluginManager::class, $resultsFactory ); $recordDriverFactory = new \VuFind\RecordDriver\PluginManager( $this->serviceManager, [ 'abstract_factories' => - ['VuFind\RecordDriver\PluginFactory'] + [\VuFind\RecordDriver\PluginFactory::class] ] ); $this->serviceManager->setService( - 'VuFind\RecordDriver\PluginManager', $recordDriverFactory + \VuFind\RecordDriver\PluginManager::class, $recordDriverFactory ); $this->serviceManager->setService( - 'VuFind\Config\SearchSpecsReader', + \VuFind\Config\SearchSpecsReader::class, new \VuFind\Config\SearchSpecsReader() ); $this->serviceManager->setService( - 'VuFind\Log\Logger', $this->createMock('VuFind\Log\Logger') + \VuFind\Log\Logger::class, + $this->createMock(\VuFind\Log\Logger::class) ); $this->serviceManager->setService( - 'VuFindHttp\HttpService', new \VuFindHttp\HttpService() + \VuFindHttp\HttpService::class, new \VuFindHttp\HttpService() ); $this->setupSearchService(); - $cfg = ['abstract_factories' => ['VuFind\Config\PluginFactory']]; + $cfg = ['abstract_factories' => [\VuFind\Config\PluginFactory::class]]; $this->serviceManager->setService( - 'VuFind\Config\PluginManager', + \VuFind\Config\PluginManager::class, new \VuFind\Config\PluginManager($this->serviceManager, $cfg) ); $this->serviceManager->setService( 'SharedEventManager', new \Zend\EventManager\SharedEventManager() ); + $driverManager = $this->serviceManager + ->get(\VuFind\RecordDriver\PluginManager::class); $this->serviceManager->setService( - 'VuFind\Record\Loader', new \VuFind\Record\Loader( - $this->serviceManager->get('VuFindSearch\Service'), - $this->serviceManager->get('VuFind\RecordDriver\PluginManager') + \VuFind\Record\Loader::class, new \VuFind\Record\Loader( + $this->serviceManager->get(\VuFindSearch\Service::class), + $driverManager ) ); $this->serviceManager->setService('Config', []); $factory = new \Zend\Mvc\I18n\TranslatorFactory(); $this->serviceManager->setService( - 'Zend\Mvc\I18n\Translator', + \Zend\Mvc\I18n\Translator::class, $factory->createService($this->serviceManager) ); + $this->serviceManager->setService( + UrlQueryHelperFactory::class, new UrlQueryHelperFactory() + ); } return $this->serviceManager; } @@ -224,11 +232,11 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase public function getAuthManager() { $sm = $this->getServiceManager(); - if (!$sm->has('VuFind\Auth\PluginManager')) { + if (!$sm->has(\VuFind\Auth\PluginManager::class)) { $authManager = new \VuFind\Auth\PluginManager($sm); $sm->setService('VuFind\Auth\PluginManager', $authManager); } - return $sm->get('VuFind\Auth\PluginManager'); + return $sm->get(\VuFind\Auth\PluginManager::class); } /** diff --git a/module/VuFind/tests/fixtures/misc/testbug1.json b/module/VuFind/tests/fixtures/misc/testbug1.json index 332f8499dab933be4bbca60671b3ef9b1c8574e1..555a0c2e6c62ba9c5fe4188f1722a964edbdf42d 100644 --- a/module/VuFind/tests/fixtures/misc/testbug1.json +++ b/module/VuFind/tests/fixtures/misc/testbug1.json @@ -1 +1 @@ -{"responseHeader":{"status":0,"QTime":3,"params":{"fl":"*,score","q":"id:\"000105196\"\n\n","hl.simple.pre":"{{{{START_HILITE}}}}","hl.simple.post":"{{{{END_HILITE}}}}","json.nl":"arrarr","hl.fl":"*","wt":"json","hl":"true"}},"response":{"numFound":1,"start":0,"maxScore":3.8332133,"docs":[{"physical":["V."],"issn":["0041-7084"],"illustrated":"Illustrated","dewey-tens":["510 - Mathematics"],"collection":["Catalog"],"publisher":["Zanichelli,"],"id":"000105196","title":"Bollettino della Unione matematica italiana.","spellingShingle":["Bollettino della Unione matematica italiana."],"dewey-hundreds":["500 - Science"],"spelling":["Unione matematica italiana","Bollettino della Unione matematica italiana.","Bologna : Zanichelli, 1922-1975.","V.","1948- Trimestrale","Bimestrale","1922-1975 ACNP","Ha come supplemento: Notiziario della Unione matematica italiana","Matematica Periodici.","Bollettino della Unione matematica italiana A 000343528","Bollettino della Unione matematica B 000343529","Bollettino della Unione matematica italiana 000394898"],"dateSpan":["1922-1975"],"author2":["Unione matematica italiana"],"author2Str":["Unione matematica italiana"],"publishDate":["1922"],"recordtype":"marc","institution":["MyInstitution"],"title_new":["Bollettino della Unione matematica italiana","Bollettino della Unione matematica"],"building":["Library A"],"title_auth":"Bollettino della Unione matematica italiana.","dewey-sort":"510.50000000","dewey-sort-browse":["510.50000000"],"format":["Journal"],"title_short":"Bollettino della Unione matematica italiana.","title_sort":"bollettino della unione matematica italiana","dewey-full":["510.5"],"dewey-ones":["510 - Mathematics"],"fullrecord":"01238casaa22003491u 4500001001000000005001700010007000300027008004100030022001400071040006800085041000800153044000700161082001500168110003100183245004900214260003900263300000700302310002300309321001500332362002000347500006900367653002700436785006200463785005300525785005900578952002000637952001300657958004400670FMT000700714TYP000900721Z30015800730#30;000105196#30;20130506112555.0#30;ta#30;030826u19221975it|b| p|||||| | ||ita |#30; #31;a0041-7084#30; #31;aUniversità Roma Tre. Sistema bibliotecario d'Ateneo#31;bita#31;eRiCA#30;0 #31;aita#30; #31;aIT#30; #31;a510.5#31;221.#30;2 #31;aUnione matematica italiana#30;10#31;aBollettino della Unione matematica italiana.#30; #31;aBologna :#31;bZanichelli,#31;c1922-1975.#30; #31;aV.#30; 1#31;a1948-#31;bTrimestrale#30; 1#31;aBimestrale#30; #31;a1922-1975#31;zACNP#30; #31;aHa come supplemento: Notiziario della Unione matematica italiana#30; #31;aMatematica#31;aPeriodici.#30;01#31;tBollettino della Unione matematica italiana#31;iA#31;w000343528#30;01#31;tBollettino della Unione matematica#31;iB#31;w000343529#30;08#31;tBollettino della Unione matematica italiana#31;w000394898#30; #31;aSCT#31;hMatematica#30; #31;aSCT#31;eSCT#30; #31;aTOR#31;b1948-1964;1968-1975.#31;tD#31;u2013#31;zCHI#30; #31;aSE#30; #31;aSE00#30; 2#31;lCAB01#31;lCAB01#31;mISSUE#31;1TOR#31;aBAST sede Torri#31;5105196-10#31;4MTM#31;820060724#31;a1948#31;f98#31;fSolo consultazione#31;hSerie 3 A.1948 V.3 n.1-3#31;i19960705#31;j19960705#31;k20060724#30;#29;","marc_error":["Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : L ]","Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : A ]","Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : F ]"],"language":["Italian"],"title_full":"Bollettino della Unione matematica italiana.","title_fullStr":"Bollettino della Unione matematica italiana.","title_full_unstemmed":"Bollettino della Unione matematica italiana.","dewey-raw":["510.5"],"_version_":1481401674999267328,"score":3.8332133}]},"highlighting":{"000105196":{"id":["{{{{START_HILITE}}}}000105196{{{{END_HILITE}}}}"]}}} +{"responseHeader":{"status":0,"QTime":3,"params":{"fl":"*,score","q":"id:\"000105196\"\n\n","hl.simple.pre":"{{{{START_HILITE}}}}","hl.simple.post":"{{{{END_HILITE}}}}","json.nl":"arrarr","hl.fl":"*","wt":"json","hl":"true"}},"response":{"numFound":1,"start":0,"maxScore":3.8332133,"docs":[{"physical":["V."],"issn":["0041-7084"],"illustrated":"Illustrated","dewey-tens":["510 - Mathematics"],"collection":["Catalog"],"publisher":["Zanichelli,"],"id":"000105196","title":"Bollettino della Unione matematica italiana.","spellingShingle":["Bollettino della Unione matematica italiana."],"dewey-hundreds":["500 - Science"],"spelling":["Unione matematica italiana","Bollettino della Unione matematica italiana.","Bologna : Zanichelli, 1922-1975.","V.","1948- Trimestrale","Bimestrale","1922-1975 ACNP","Ha come supplemento: Notiziario della Unione matematica italiana","Matematica Periodici.","Bollettino della Unione matematica italiana A 000343528","Bollettino della Unione matematica B 000343529","Bollettino della Unione matematica italiana 000394898"],"dateSpan":["1922-1975"],"author2":["Unione matematica italiana"],"author2Str":["Unione matematica italiana"],"publishDate":["1922"],"record_format":"marc","institution":["MyInstitution"],"title_new":["Bollettino della Unione matematica italiana","Bollettino della Unione matematica"],"building":["Library A"],"title_auth":"Bollettino della Unione matematica italiana.","dewey-sort":"510.50000000","dewey-sort-browse":["510.50000000"],"format":["Journal"],"title_short":"Bollettino della Unione matematica italiana.","title_sort":"bollettino della unione matematica italiana","dewey-full":["510.5"],"dewey-ones":["510 - Mathematics"],"fullrecord":"01238casaa22003491u 4500001001000000005001700010007000300027008004100030022001400071040006800085041000800153044000700161082001500168110003100183245004900214260003900263300000700302310002300309321001500332362002000347500006900367653002700436785006200463785005300525785005900578952002000637952001300657958004400670FMT000700714TYP000900721Z30015800730#30;000105196#30;20130506112555.0#30;ta#30;030826u19221975it|b| p|||||| | ||ita |#30; #31;a0041-7084#30; #31;aUniversità Roma Tre. Sistema bibliotecario d'Ateneo#31;bita#31;eRiCA#30;0 #31;aita#30; #31;aIT#30; #31;a510.5#31;221.#30;2 #31;aUnione matematica italiana#30;10#31;aBollettino della Unione matematica italiana.#30; #31;aBologna :#31;bZanichelli,#31;c1922-1975.#30; #31;aV.#30; 1#31;a1948-#31;bTrimestrale#30; 1#31;aBimestrale#30; #31;a1922-1975#31;zACNP#30; #31;aHa come supplemento: Notiziario della Unione matematica italiana#30; #31;aMatematica#31;aPeriodici.#30;01#31;tBollettino della Unione matematica italiana#31;iA#31;w000343528#30;01#31;tBollettino della Unione matematica#31;iB#31;w000343529#30;08#31;tBollettino della Unione matematica italiana#31;w000394898#30; #31;aSCT#31;hMatematica#30; #31;aSCT#31;eSCT#30; #31;aTOR#31;b1948-1964;1968-1975.#31;tD#31;u2013#31;zCHI#30; #31;aSE#30; #31;aSE00#30; 2#31;lCAB01#31;lCAB01#31;mISSUE#31;1TOR#31;aBAST sede Torri#31;5105196-10#31;4MTM#31;820060724#31;a1948#31;f98#31;fSolo consultazione#31;hSerie 3 A.1948 V.3 n.1-3#31;i19960705#31;j19960705#31;k20060724#30;#29;","marc_error":["Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : L ]","Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : A ]","Minor Error : Subfield tag is an invalid uppercase character, changing it to lower case. --- [ Z30 : F ]"],"language":["Italian"],"title_full":"Bollettino della Unione matematica italiana.","title_fullStr":"Bollettino della Unione matematica italiana.","title_full_unstemmed":"Bollettino della Unione matematica italiana.","dewey-raw":["510.5"],"_version_":1481401674999267328,"score":3.8332133}]},"highlighting":{"000105196":{"id":["{{{{START_HILITE}}}}000105196{{{{END_HILITE}}}}"]}}} diff --git a/module/VuFind/tests/fixtures/misc/testbug2.json b/module/VuFind/tests/fixtures/misc/testbug2.json index b80af3a35c46257783749a973bfde1c57206f7ae..9b36fd7f273860dfb56e853439861fb23015b65f 100644 --- a/module/VuFind/tests/fixtures/misc/testbug2.json +++ b/module/VuFind/tests/fixtures/misc/testbug2.json @@ -22,7 +22,7 @@ "spelling":"Vico, Giambattista, 1668-1744. Principum Neapolitanorum coniurationis anni MDCCI historia. Italian & Latin La congiura dei Principi Napoletani 1701 : (prima e seconda stesura) / Giambattista Vico ; a cura di Claudia Pandolfi. Fictional edition. Morano : Centro di Studi Vichiani, 1992. 296 p. : ill. ; 24 cm. Opere di Giambattista Vico ; 2/1 Italian and Latin. Includes bibliographical references (p. [277]-281) and index. Sample abstract. April11phi Naples (Kingdom) History Spanish rule, 1442-1707 Sources. Pandolfi, Claudia. Vico, Giambattista, 1668-1744. Works. 1982 ; 2, pt. 1. http://fictional.com/sample/url", "title_sub":"(prima e seconda stesura) /", "callnumber-label":"DG848", - "recordtype":"marc", + "record_format":"marc", "callnumber-first":"D - World History", "title_auth":"La congiura dei Principi Napoletani 1701 : (prima e seconda stesura) /", "callnumber-subject":"DG - Italy, Malta", diff --git a/module/VuFind/tests/fixtures/resolver/response/alma.xml b/module/VuFind/tests/fixtures/resolver/response/alma.xml new file mode 100644 index 0000000000000000000000000000000000000000..05c9ccc0bb22dfc3d35f09602e2cd59f3829c177 --- /dev/null +++ b/module/VuFind/tests/fixtures/resolver/response/alma.xml @@ -0,0 +1,335 @@ +HTTP/1.1 200 OK +Server: Apache-Coyote/1.1 +p3p: CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" +Content-Type: text/xml;charset=UTF-8 +Vary: Accept-Encoding +Date: Mon, 19 Aug 2019 12:34:14 GMT + +<uresolver_content xmlns="http://com/exlibris/urm/uresolver/xmlbeans/u" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <context_object> + <keys> + <key id="rft.stitle">Fundamental Data Compression</key> + <key id="rft.pub">Elsevier Science</key> + <key id="rft.place">Burlington :</key> + <key id="licenseEnable">true</key> + <key id="sfx.sid">primo.exlibrisgroup.com</key> + <key id="available_services">viewit</key> + <key id="available_services">getit</key> + <key id="rft.btitle">Fundamental Data Compression</key> + <key id="rft.genre">book</key> + <key id="Incoming_URL">http%3A%2F%2Fna01.alma.exlibrisgroup.com%2Fview%2Furesolver%2FTR_INTEGRATION_INST%2Fopenurl%3Fdebug%3Dtrue%26%26u.ignore_date_coverage%3Dtrue%26rft.mms_id%3D9942811800561%26rfr_id%3Dinfo%3Asid%2Fprimo.exlibrisgroup.com%26svc_dat%3DCTO</key> + <key id="institution">561</key> + <key id="rft.eisbn_10">0-7506-6310-3</key> + <key id="rft.eisbn_10">0-08-053026-5</key> + <key id="rft.eisbn_10">1-281-03498-3</key> + <key id="rft.oclcnum">437189463</key> + <key id="http_request_type">GET</key> + <key id="rft.eisbn_13">978-0-7506-6310-6</key> + <key id="rft.eisbn_13">978-0-08-053026-0</key> + <key id="rft.eisbn_13">978-1-281-03498-4</key> + <key id="rft.normalized_eisbn">9780750663106</key> + <key id="req.id" xsi:nil="true"/> + <key id="rft.mms_id">9942811800561</key> + <key id="user_ip" xsi:nil="true"/> + <key id="inventory_id">5111975010000561</key> + <key id="rfr_id">info:sid/primo.exlibrisgroup.com</key> + <key id="rft.inventory_id">5111975010000561</key> + <key id="rft.eisbn">0-7506-6310-3</key> + <key id="rft.eisbn">0-08-053026-5</key> + <key id="rft.eisbn">9786611034986</key> + <key id="rft.eisbn">1-281-03498-3</key> + <key id="publication_place">Burlington :</key> + <key id="rft.object_type">BOOK</key> + <key id="rft.publisher">Elsevier Science</key> + <key id="rft.au">Pu, Ida Mengyi.</key> + <key id="ctx_id">5687947890000561</key> + <key id="rft.pubdate">2005.</key> + <key id="full_text_indicator">true</key> + <key id="u.ignore_date_coverage">true</key> + <key id="rft.title">Fundamental Data Compression</key> + <key id="customer">550</key> + <key id="rfr.rfr">primo.exlibrisgroup.com</key> + </keys> + </context_object> + <context_services> + <context_service service_type="getFullTxt" context_service_id="5687861830000561"> + <keys> + <key id="package_name">Ebook Central Perpetual and DDA Titles</key> + <key id="package_public_name">Ebook override</key> + <key id="package_display_name">Ebook override</key> + <key id="package_internal_name">EBOOK_CENTRAL_PERPETUAL_DDA_TITLES</key> + <key id="interface_name">Ebook Central Perpetual and DDA Titles</key> + <key id="package_pid">61116046810000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">iso-8859-1</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">53117809230000561</key> + <key id="cz_link_id">534330000001495973</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability">Available from 2019<br></key> + <key id="static_url">true</key> + <key id="parser_program">EBOOK::Central</key> + <key id="parse_parameters">url=http://ebookcentral.proquest.com/lib/ & cust_id=&bkey=4052992</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">false</key> + <key id="proxy_selected">DEFAULT</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861830000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getFullTxt" context_service_id="5687861800000561"> + <keys> + <key id="package_name">ebrary</key> + <key id="package_public_name">ebrary Academic Complete Subscription UKI Edition</key> + <key id="package_display_name">ebrary Academic Complete Subscription UKI Edition</key> + <key id="package_internal_name">EBRARY_ACADEMIC_COMPLETE_SUBSCRIPTION_UKI_EDITION</key> + <key id="interface_name">ebrary</key> + <key id="package_pid">6128319920000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">utf8</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5332048890000561</key> + <key id="cz_link_id">533790000000347286</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">EBRARY::EBOOKS</key> + <key id="parse_parameters">url=http://site.ebrary.com &cust_id=yes&bkey=10190890</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">false</key> + <key id="proxy_selected">DEFAULT</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861800000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getFullTxt" context_service_id="5687861790000561"> + <keys> + <key id="package_name">ebrary</key> + <key id="package_public_name">ebrary Science & Technology Subscription</key> + <key id="package_display_name">ebrary Science & Technology Subscription</key> + <key id="package_internal_name">EBRARY_SCIENCE_AND_TECHNOLOGY_SUBSCRIPTION</key> + <key id="interface_name">ebrary</key> + <key id="package_pid">6134559200000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">utf8</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5334835190000561</key> + <key id="cz_link_id">533790000000521471</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">EBRARY::EBOOKS</key> + <key id="parse_parameters">url=http://site.ebrary.com &cust_id=&bkey=10190890</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">false</key> + <key id="proxy_selected">DEFAULT</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861790000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getFullTxt" context_service_id="5687861770000561"> + <keys> + <key id="package_name">EBSCOhost</key> + <key id="package_public_name">EBSCOhost Academic eBook Collection (North America)</key> + <key id="package_display_name">EBSCOhost Academic eBook Collection (North America)</key> + <key id="package_internal_name">EBSCOHOST_EBOOKS_ACADEMIC_COLLECTION_NORTH_AMERICA</key> + <key id="interface_name">EBSCOhost</key> + <key id="package_pid">6178520000000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">iso-8859-1</key> + <key id="Is_free">1</key> + <key id="portfolio_PID">5379940190000561</key> + <key id="cz_link_id">533640000000243115</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">EBSCO_HOST::netlibrary</key> + <key id="parse_parameters">url=http://search.ebscohost.com & shib= & customer_id=&ID=207290</key> + <key id="Authentication_note">collection level auth<br/>SERVICE LEVEL AUTHE NOTE<br/></key> + <key id="public_note">notessssssssssss<br/>SERVICE LEVEL PUBLIC NOTE<br/></key> + <key id="proxy_enabled">false</key> + <key id="proxy_selected">DEFAULT</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861770000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getHolding" context_service_id="5687861780000561"> + <keys> + <key id="package_name">EBSCOhost</key> + <key id="package_public_name">EBSCOhost eBook Community College Collection</key> + <key id="package_display_name">EBSCOhost eBook Community College Collection</key> + <key id="package_internal_name">EBSCOHOST_EBOOKS_COMMUNITY_COLLEGE_COLLECTION</key> + <key id="interface_name">EBSCOhost</key> + <key id="package_pid">6124119500000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">utf8</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5368625580000561</key> + <key id="cz_link_id">532560000001468461</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">EBSCO_HOST::netlibrary</key> + <key id="parse_parameters">url=http://search.ebscohost.com & shib=test34 & customer_id=test34&ID=207290</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">true</key> + <key id="proxy_selected">proxy2</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861780000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getFullTxt" context_service_id="5687861820000561"> + <keys> + <key id="package_name">Elsevier ScienceDirect</key> + <key id="package_public_name">Elsevier ScienceDirect Books</key> + <key id="package_display_name">Elsevier ScienceDirect Books</key> + <key id="package_internal_name">ELSEVIER_SD_BOOKS</key> + <key id="interface_name">Elsevier ScienceDirect</key> + <key id="package_pid">6111920000000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">iso-8859-1</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5311970200000561</key> + <key id="cz_link_id">531000000000933152</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">ELSEVIER::SCIENCE_DIRECT_BOOKS</key> + <key id="parse_parameters">url=http://www.sciencedirect.com/science & shib=&bkey=9780750663106</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">false</key> + <key id="proxy_selected">DEFAULT</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861820000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="GeneralElectronicService" context_service_id="2336397"> + <keys> + <key id="code">LIBR</key> + <key id="name">Request Assistance for this Resource!</key> + <key id="public_name">Request Assistance for this Resource!</key> + <key id="url">https://www.google.com/search?Testingrft.oclcnum=437189463&q=Fundamental+Data+Compression&rft.archive=9942811800561</key> + <key id="service_order">0</key> + </keys> + </context_service> + <context_service service_type="getFullTxt" context_service_id="5687861810000561"> + <keys> + <key id="package_name">Proquest</key> + <key id="package_public_name">ProQuest Safari Tech Books Online</key> + <key id="package_display_name">ProQuest Safari Tech Books Online</key> + <key id="package_internal_name">PROQUEST_SAFARI_TECH_BOOKS_ONLINE</key> + <key id="interface_name">Proquest</key> + <key id="package_pid">6112970000000561</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">iso-8859-1</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5326327260000561</key> + <key id="cz_link_id">533710000002010686</key> + <key id="electronic_material_type">BOOK</key> + <key id="Availability"/> + <key id="static_url">true</key> + <key id="parser_program">PROQUEST::SAFARI</key> + <key id="parse_parameters">url=http://proquestcombo.safaribooksonline.com & uicode= &shib= &shire=https://authenticate.bvdep.com/ukfederation/Shibboleth.sso/SAML/POST &provider=https://authenticate.bvdep.com/ukfederation&bkey=9780750663106</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">true</key> + <key id="proxy_selected">Proxy 1</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">no</key> + <key id="Filtered">true</key> + <key id="Filter reason">Date Filter</key> + <key id="preferred_link">false</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861810000561&institutionId=561&customerId=550</resolution_url> + </context_service> + <context_service service_type="getFullTxt" context_service_id="971788650005972"> + <keys> + <key id="package_name">Elsevier ScienceDirect</key> + <key id="package_public_name">Elsevier SD FREEDOM COLLECTION</key> + <key id="package_display_name">Elsevier SD FREEDOM COLLECTION</key> + <key id="package_internal_name">ELSEVIER_SD_FREEDOM_COLLECTION</key> + <key id="interface_name">Elsevier ScienceDirect</key> + <key id="db_id">.~1,8P~,AAOAW,ACRLP,AGUBO,AIKHN,BLXMC,EO8,EO9,EP2,EP3,G-Q,OAUVE,Q38,SDF</key> + <key id="package_pid">6139351080005972</key> + <key id="service_type_description">Full text available via</key> + <key id="character_set">iso-8859-1</key> + <key id="Is_free">0</key> + <key id="portfolio_PID">5344797220005972</key> + <key id="cz_link_id">531000000000042994</key> + <key id="electronic_material_type">JOURNAL</key> + <key id="Availability">Available from 1995 volume: 22 issue: 1.<br></key> + <key id="static_url">true</key> + <key id="parser_program">ELSEVIER::SCIENCE_DIRECT</key> + <key id="parse_parameters">host=https://www.sciencedirect.com/science/& prefsite = sd & shib= & u_shib=&uis=03064549</key> + <key id="Authentication_note"/> + <key id="public_note"/> + <key id="proxy_enabled">true</key> + <key id="proxy_selected">Ã…A Proxy</key> + <key id="related_title">@TITLE (@RelationType)</key> + <key id="is_related_service">false</key> + <key id="is_closly_related">false</key> + <key id="license_exist">false</key> + <key id="crossref_enabled">yes</key> + <key id="Filtered">true</key> + <key id="Filter reason">Available for - the ip groups are not part of the service AF groups</key> + <key id="preferred_link">true</key> + </keys> + <resolution_url>https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=971788650005972&institutionId=561&customerId=550</resolution_url> + </context_service> + </context_services> + <performance_counters> + <performance_counter name="TOTAL" duration="0.0"/> + <performance_counter name="MMS_LOOKUP" duration="0.0"/> + <performance_counter name="ENRICH" duration="0.0"/> + <performance_counter name="PARSE" duration="0.0"/> + <performance_counter name="GET_SERVICES" duration="1.109"/> + <performance_counter name="FILTER" duration="0.249"/> + <performance_counter name="SAVE" duration="0.202"/> + <performance_counter name="GET_ZERO_TITLE_SERVICES" duration="0.0"/> + <performance_counter name="FETCH_SERVICE_THRESHOLD_RULES" duration="0.0"/> + <performance_counter name="CREATE_ZERO_TITLE_SERVICES" duration="0.0"/> + <performance_counter name="SAVE_ZERO_TITLE_SERVICES" duration="0.0"/> + <performance_counter name="GET_SINGLE_SERVICE" duration="0.0"/> + <performance_counter name="EXECUTE_TARGET_PARSER" duration="0.0"/> + <performance_counter name="GET_URESOLVER_CONTENT" duration="0.0"/> + <performance_counter name="UPDATE_SELECTED" duration="0.0"/> + </performance_counters> +</uresolver_content> \ No newline at end of file diff --git a/module/VuFind/tests/fixtures/spell/query1 b/module/VuFind/tests/fixtures/spell/query1 old mode 100755 new mode 100644 diff --git a/module/VuFind/tests/fixtures/spell/query6 b/module/VuFind/tests/fixtures/spell/query6 new file mode 100644 index 0000000000000000000000000000000000000000..4432abe5923cf8d2485a0d06e8600262d2796048 Binary files /dev/null and b/module/VuFind/tests/fixtures/spell/query6 differ diff --git a/module/VuFind/tests/fixtures/spell/spell1 b/module/VuFind/tests/fixtures/spell/spell1 old mode 100755 new mode 100644 diff --git a/module/VuFind/tests/fixtures/spell/spell6 b/module/VuFind/tests/fixtures/spell/spell6 new file mode 100644 index 0000000000000000000000000000000000000000..2e8be4dd366aa81d6d5a4e8574565742283e7720 Binary files /dev/null and b/module/VuFind/tests/fixtures/spell/spell6 differ diff --git a/module/VuFind/tests/fixtures/unpaywall/goodresponse b/module/VuFind/tests/fixtures/unpaywall/goodresponse new file mode 100644 index 0000000000000000000000000000000000000000..8eaa010199266b15842e005f4738718f92d462d7 --- /dev/null +++ b/module/VuFind/tests/fixtures/unpaywall/goodresponse @@ -0,0 +1,80 @@ +HTTP/1.1 200 OK +Connection: keep-alive +Server: gunicorn/19.9.0 +Date: Wed, 06 Nov 2019 20:38:59 GMT +Content-Type: application/json +Content-Length: 2336 +Access-Control-Allow-Origin: * +Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH +Access-Control-Allow-Headers: origin, content-type, accept, x-requested-with +Via: 1.1 vegur + +{ + "best_oa_location": { + "endpoint_id": null, + "evidence": "open (via page says license)", + "host_type": "publisher", + "is_best": true, + "license": "cc-by-sa", + "pmh_id": null, + "repository_institution": null, + "updated": "2018-07-24T23:01:04.482481", + "url": "http://sajlis.journals.ac.za/pub/article/download/1434/1332", + "url_for_landing_page": "https://doi.org/10.7553/66-4-1434", + "url_for_pdf": "http://sajlis.journals.ac.za/pub/article/download/1434/1332", + "version": "publishedVersion" + }, + "data_standard": 2, + "doi": "10.7553/66-4-1434", + "doi_url": "https://doi.org/10.7553/66-4-1434", + "genre": "journal-article", + "has_repository_copy": false, + "is_oa": true, + "journal_is_in_doaj": true, + "journal_is_oa": true, + "journal_issn_l": "0256-8861", + "journal_issns": "2304-8263", + "journal_name": "South African Journal of Libraries and Information Science", + "oa_locations": [ + { + "endpoint_id": null, + "evidence": "open (via page says license)", + "host_type": "publisher", + "is_best": true, + "license": "cc-by-sa", + "pmh_id": null, + "repository_institution": null, + "updated": "2018-07-24T23:01:04.482481", + "url": "http://sajlis.journals.ac.za/pub/article/download/1434/1332", + "url_for_landing_page": "https://doi.org/10.7553/66-4-1434", + "url_for_pdf": "http://sajlis.journals.ac.za/pub/article/download/1434/1332", + "version": "publishedVersion" + }, + { + "endpoint_id": null, + "evidence": "oa journal (via doaj)", + "host_type": "publisher", + "is_best": false, + "license": "cc-by-sa", + "pmh_id": null, + "repository_institution": null, + "updated": "2019-11-06T20:38:59.834062", + "url": "https://doi.org/10.7553/66-4-1434", + "url_for_landing_page": "https://doi.org/10.7553/66-4-1434", + "url_for_pdf": null, + "version": "publishedVersion" + } + ], + "oa_status": "gold", + "published_date": "2014-01-26", + "publisher": "Stellenbosch University", + "title": "Pioneers, passionate ladies, and private eyes : dime novels, series books, and paperbacks Sullivan, Larry E. & Cushman Schurman, Lydia eds.", + "updated": "2018-07-24T23:02:00.263680", + "year": 2014, + "z_authors": [ + { + "family": "Sandham", + "given": "Tim" + } + ] +} \ No newline at end of file diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php index 5ad50b8fb2b68474bca8462da13332165e9cf184..d93d89382ee57191922354c4959f20c77074a00f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ILSTest.php @@ -102,7 +102,7 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase $this->getServiceManager() ); $driverManager->setService('Sample', $driver); - $mockConfigReader = $this->createMock('VuFind\Config\PluginManager'); + $mockConfigReader = $this->createMock(\VuFind\Config\PluginManager::class); $mockConfigReader->expects($this->any())->method('get') ->will($this->returnValue(new \Zend\Config\Config([]))); $auth = new \VuFind\Auth\ILS( @@ -113,7 +113,7 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase $authenticator ); $auth->setDbTableManager( - $this->getServiceManager()->get('VuFind\Db\Table\PluginManager') + $this->getServiceManager()->get(\VuFind\Db\Table\PluginManager::class) ); $auth->getCatalog()->setDriver($driver); return $auth; @@ -368,7 +368,7 @@ class ILSTest extends \VuFindTest\Unit\DbTestCase */ protected function getMockILSAuthenticator($patron = null) { - $mock = $this->getMockBuilder('VuFind\Auth\ILSAuthenticator') + $mock = $this->getMockBuilder(\VuFind\Auth\ILSAuthenticator::class) ->disableOriginalConstructor() ->setMethods(['storedCatalogLogin']) ->getMock(); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php index cd5f254522c48c94868385bd8db826a2077ca2ed..d92f1398dcffbefb62e9a5f269b9db8d9306694d 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Auth/ShibbolethTest.php @@ -78,7 +78,7 @@ class ShibbolethTest extends \VuFindTest\Unit\DbTestCase if (null === $config) { $config = $this->getAuthConfig(); } - $obj = new Shibboleth($this->createMock('Zend\Session\ManagerInterface')); + $obj = new Shibboleth($this->createMock(\Zend\Session\ManagerInterface::class)); $initializer = new \VuFind\ServiceManager\ServiceInitializer(); $initializer($this->getServiceManager(), $obj); $obj->setConfig($config); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php index 9db7e3e24e1638f5b966cf302ba92fdd87831253..640542685d14bb716ac10f9953b46788e3e151ff 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrAuthTest.php @@ -60,7 +60,7 @@ class SolrAuthTest extends \VuFindTest\Unit\DbTestCase */ public function testSimpleSearch() { - $solr = $this->getServiceManager()->get('VuFind\Search\BackendManager') + $solr = $this->getServiceManager()->get(\VuFind\Search\BackendManager::class) ->get('SolrAuth'); // Search for a term known to exist in the sample data; request just one diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php index a70422d95a8f096bff59ff5f8d10ca3f21c8431b..af6c4b71a71bbb34bdfefd0b01405cc65959e92b 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Connection/SolrTest.php @@ -60,7 +60,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ public function testAlphaBrowseSeeAlso() { - $solr = $this->getServiceManager()->get('VuFind\Search\BackendManager') + $solr = $this->getServiceManager()->get(\VuFind\Search\BackendManager::class) ->get('Solr'); $extras = new ParamBag(['extras' => 'id']); $result = $solr->alphabeticBrowse('author', 'Dublin Society', 0, 1, $extras); @@ -79,7 +79,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ public function testAlphaBrowseUseInstead() { - $solr = $this->getServiceManager()->get('VuFind\Search\BackendManager') + $solr = $this->getServiceManager()->get(\VuFind\Search\BackendManager::class) ->get('Solr'); $extras = new ParamBag(['extras' => 'id']); $result = $solr @@ -99,7 +99,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ public function testDeweyValues() { - $solr = $this->getServiceManager()->get('VuFind\Search\BackendManager') + $solr = $this->getServiceManager()->get(\VuFind\Search\BackendManager::class) ->get('Solr'); $extras = new ParamBag(['extras' => 'id']); $result = $solr->alphabeticBrowse('dewey', '123.45 .I39', 0, 1, $extras); @@ -121,7 +121,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ public function testTermsHandler() { - $solr = $this->getServiceManager()->get('VuFind\Search\BackendManager') + $solr = $this->getServiceManager()->get(\VuFind\Search\BackendManager::class) ->get('Solr'); $currentPageInfo = $solr->terms('id', 'test', 1)->getFieldTerms('id'); $this->assertEquals(1, count($currentPageInfo)); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php index b28792bf174d51b7e9cfecb4f06eac9e833da032..01fefbb74d81f29822a65e64e9b2851eb903a320 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountActionsTest.php @@ -35,9 +35,11 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -66,6 +68,8 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase /** * Test changing a password. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testChangePassword() @@ -75,25 +79,25 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); // Create account - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.modal-body .createAccountLink'); $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Log out - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // Go to profile page: $session->visit($this->getVuFindUrl('/MyResearch/Profile')); // Log back in - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->fillInLoginForm($page, 'username1', 'test'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Now click change password button: @@ -102,7 +106,7 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase // Change the password (but get the old password wrong) $this->fillInChangePasswordForm($page, 'bad', 'good'); - $this->findCss($page, '#newpassword .btn.btn-primary')->click(); + $this->clickCss($page, '#newpassword .btn.btn-primary'); $this->snooze(); $this->assertEquals( 'Invalid login -- please try again.', @@ -111,7 +115,7 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase // Change the password successfully: $this->fillInChangePasswordForm($page, 'test', 'good'); - $this->findCss($page, '#newpassword .btn.btn-primary')->click(); + $this->clickCss($page, '#newpassword .btn.btn-primary'); $this->snooze(); $this->assertEquals( 'Your password has successfully been changed', @@ -119,24 +123,97 @@ class AccountActionsTest extends \VuFindTest\Unit\MinkTestCase ); // Log out - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // Log back in (using old credentials, which should now fail): - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->fillInLoginForm($page, 'username1', 'test'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertLightboxWarning($page, 'Invalid login -- please try again.'); // Now log in successfully: $this->fillInLoginForm($page, 'username1', 'good'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // One final log out (to confirm that log in really worked). - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); + $this->snooze(); + } + + /** + * Test that changing email is disabled by default. + * + * @return void + */ + public function testChangeEmailDisabledByDefault() + { + // Go to profile page: + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl('/MyResearch/Profile')); + $page = $session->getPage(); + + // Log in + $this->clickCss($page, '#loginOptions a'); + $this->fillInLoginForm($page, 'username1', 'good'); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); + + // Now confirm that email button is absent: + $link = $page->findLink('Change Email Address'); + $this->assertFalse(is_object($link)); + } + + /** + * Test changing an email. + * + * @return void + */ + public function testChangeEmail() + { + // Turn on email change option: + $this->changeConfigs( + [ + 'config' => [ + 'Authentication' => [ + 'change_email' => true, + ] + ] + ] + ); + + // Go to profile page: + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl('/MyResearch/Profile')); + $page = $session->getPage(); + + // Log in + $this->clickCss($page, '#loginOptions a'); + $this->fillInLoginForm($page, 'username1', 'good'); + $this->clickCss($page, '.modal-body .btn.btn-primary'); + $this->snooze(); + + // Now click change email button: + $this->findAndAssertLink($page, 'Change Email Address')->click(); + $this->snooze(); + + // Change the email: + $this->findCssAndSetValue($page, '[name="email"]', 'new@email.com'); + $this->clickCss($page, '[name="submit"]'); + $this->snooze(); + $this->assertEquals( + 'Your email address has been changed successfully', + $this->findCss($page, '.alert-success')->getText() + ); + + // Now go to profile page and confirm that email has changed: + $session->visit($this->getVuFindUrl('/MyResearch/Profile')); + $this->assertEquals( + 'First Name: Tester Last Name: McTestenson Email: new@email.com', + $this->findCss($page, '.table-striped')->getText() + ); } /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php new file mode 100644 index 0000000000000000000000000000000000000000..30c779077d5bce52fd204cdeaaf26ee14d3aee68 --- /dev/null +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AccountMenuTest.php @@ -0,0 +1,453 @@ +<?php +/** + * Mink account ajax menu test class. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2011. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Mink; + +/** + * Mink account ajax menu test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + * @retry 4 + */ +class AccountMenuTest extends \VuFindTest\Unit\MinkTestCase +{ + use \VuFindTest\Unit\AutoRetryTrait; + use \VuFindTest\Unit\UserCreationTrait; + use \VuFindTest\Unit\DemoDriverTestTrait; + + /** + * Standard setup method. + * + * @return mixed + */ + public static function setUpBeforeClass() + { + return static::failIfUsersExist(); + } + + /** + * Standard setup + login + * + * @return void + */ + public function setUp() + { + // Give up if we're not running in CI: + if (!$this->continuousIntegrationRunning()) { + return $this->markTestSkipped('Continuous integration not running.'); + } + // Setup config + $this->changeConfigs([ + 'Demo' => $this->getDemoIniOverrides(), + 'config' => [ + 'Catalog' => ['driver' => 'Demo'], + 'Authentication' => [ + 'enableAjax' => true, + 'enableDropdown' => false + ] + ] + ]); + } + + /** + * Create a specific state in the account ajax storage. + * + * Cleared when browser closes. + * If run multiple times in one test function, manually clear cache. + * + * @return void + */ + protected function setJSStorage($states) + { + $session = $this->getMinkSession(); + $js = ''; + foreach ($states as $key => $state) { + $js .= 'sessionStorage.setItem(\'vf-account-status-' . $key . '\', \'' . json_encode($state) . '\');'; + } + $session->evaluateScript($js); + } + + /** + * Get associative array of storage state + * + * @return array + */ + protected function getJSStorage() + { + $session = $this->getMinkSession(); + return $session->evaluateScript( + 'return {' . + ' "checkedOut": sessionStorage.getItem("vf-account-status-checkedOut"),' . + ' "fines": sessionStorage.getItem("vf-account-status-fines"),' . + ' "holds": sessionStorage.getItem("vf-account-status-holds"),' . + ' "illRequests": sessionStorage.getItem("vf-account-status-illRequests"),' . + ' "storageRetrievalRequests": sessionStorage.getItem("vf-account-status-storageRetrievalRequests"),' . + '}' + ); + } + + /** + * Establish the fines in the session that will be used by various tests below... + * + * @return object + */ + protected function setUpFinesEnvironment() + { + // Seed some fines + $this->setJSStorage(['fines' => ['value' => 30.5, 'display' => '$30.50']]); + $session = $this->getMinkSession(); + $session->reload(); + $this->snooze(); + return $session->getPage(); + } + + /** + * Test that the menu is absent when enableAjax is true and enableDropdown + * is false. + * + * @retryCallback tearDownAfterClass + * + * @return void + */ + public function testMenuOffAjaxNoDropdown() + { + // Create user + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl()); + $page = $session->getPage(); + $this->clickCss($page, '#loginOptions a'); + $this->snooze(); + $this->clickCss($page, '.modal-body .createAccountLink'); + $this->snooze(); + $this->fillInAccountForm($page); + $this->clickCss($page, '.modal-body .btn.btn-primary'); + $this->snooze(); + + // Seed some fines + $page = $this->setUpFinesEnvironment(); + $menu = $page->findAll('css', '#login-dropdown'); + $this->assertEquals(0, count($menu)); + $stati = $page->findAll('css', '.account-menu .fines-status.hidden'); + $this->assertEquals(0, count($stati)); + } + + /** + * Test that the menu is absent when enableAjax is false and enableDropdown + * is false. + * + * @return void + */ + public function testMenuOffNoAjaxNoDropdown() + { + // Nothing on + $this->changeConfigs( + [ + 'config' => [ + 'Authentication' => [ + 'enableAjax' => false, + 'enableDropdown' => false + ] + ] + ] + ); + $this->login(); + $this->snooze(); + $page = $this->setUpFinesEnvironment(); + $menu = $page->findAll('css', '#login-dropdown'); + $this->assertEquals(0, count($menu)); + $stati = $page->findAll('css', '.account-menu .fines-status.hidden'); + $this->assertEquals(1, count($stati)); + } + + /** + * Test that the menu is absent when enableAjax is false and enableDropdown + * is true. + * + * @return void + */ + public function testMenuOffNoAjaxDropdown() + { + $this->changeConfigs( + [ + 'config' => [ + 'Authentication' => [ + 'enableAjax' => false, + 'enableDropdown' => true + ] + ] + ] + ); + $this->login(); + $this->snooze(); + $page = $this->setUpFinesEnvironment(); + $menu = $page->findAll('css', '#login-dropdown'); + $this->assertEquals(1, count($menu)); + $stati = $page->findAll('css', '.account-menu .fines-status.hidden'); + $this->assertEquals(2, count($stati)); // one in menu, one in dropdown + } + + /** + * Test that the menu is absent when enableAjax is true and enableDropdown + * is true. + * + * @return void + */ + public function testMenuOffAjaxDropdown() + { + $this->changeConfigs( + [ + 'config' => [ + 'Authentication' => [ + 'enableAjax' => true, + 'enableDropdown' => true + ] + ] + ] + ); + $this->login(); + $this->snooze(); + $page = $this->setUpFinesEnvironment(); + $menu = $page->findAll('css', '#login-dropdown'); + $this->assertEquals(1, count($menu)); + $stati = $page->findAll('css', '.account-menu .fines-status.hidden'); + $this->assertEquals(0, count($stati)); + } + + /** + * Set some values and delete them to test VuFind.account.clearCache + * with parameters. + * + * @return void + */ + public function testIndividualCacheClearing() + { + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl()); + $this->snooze(); + // Seed some fines + $this->setJSStorage(['fines' => ['value' => 30.5, 'display' => '$30.50']]); + // Clear different cache + $session->evaluateScript('VuFind.account.clearCache("holds");'); + $storage = $this->getJSStorage(); + $this->assertNotNull($storage['fines']); + // Clear correct cache + $session->evaluateScript('VuFind.account.clearCache("fines");'); + $storage = $this->getJSStorage(); + $this->assertNull($storage['fines']); + } + + /** + * Set some values and delete them to test VuFind.account.clearCache + * without parameters. + * + * @return void + */ + public function testGlobalCacheClearing() + { + $session = $this->login(); + // Seed some fines + $this->setJSStorage(['fines' => ['value' => 30.5, 'display' => '$30.50']]); + $storage = $this->getJSStorage(); + $this->assertNotNull($storage['fines']); + // Clear all cache + $session->evaluateScript('VuFind.account.clearCache();'); + $storage = $this->getJSStorage(); + $this->assertNull($storage['fines']); + } + + /** + * Utility class to login + * + * @return \Behat\Mink\Session + */ + protected function login() + { + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl()); + $page = $session->getPage(); + $this->clickCss($page, '#loginOptions a'); + $this->snooze(); + $this->fillInLoginForm($page, 'username1', 'test'); + $this->clickCss($page, '.modal-body .btn.btn-primary'); + $this->snooze(); + return $session; + } + + /** + * Abstracted test to set storage and check if the icon is correct + * + * @return void + */ + protected function checkIcon($storage, $checkClass) + { + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl()); + foreach ($storage as $item) { + $this->snooze(); + $this->setJSStorage($item); + $session->reload(); + $page = $session->getPage(); + $this->findCss($page, '#account-icon' . $checkClass); + foreach ($item as $key => $value) { + $session->evaluateScript('VuFind.account.clearCache("' . $key . '");'); + } + } + } + + /** + * Check cases that don't change the account icon + * + * @return void + */ + public function testIconNone() + { + $this->login(); + $storage = [ + // No fines + ['fines' => ['value' => 0, 'display' => 'ZILTCH']], + // Holds in transit only + ['holds' => ['in_transit' => 1, 'available' => 0]], + // ILL Requests in transit only + ['illRequests' => ['in_transit' => 1, 'available' => 0]], + // Storage Retrievals in transit only + ['storageRetrievalRequests' => ['in_transit' => 1, 'available' => 0]] + ]; + $this->checkIcon($storage, '.fa-user-circle'); + } + + /** + * Check cases that change the account icon to a happy bell + * + * @return void + */ + public function testIconGood() + { + $this->login(); + $storage = [ + // Holds available + ['holds' => ['in_transit' => 0, 'available' => 1]], + // ILL Requests available + ['illRequests' => ['in_transit' => 0, 'available' => 1]], + // Storage Retrievals available + ['storageRetrievalRequests' => ['in_transit' => 0, 'available' => 1]] + ]; + $this->checkIcon($storage, '.fa-bell.text-success'); + } + + /** + * Check cases that change the account icon to a concerned bell + * + * @return void + */ + public function testIconWarning() + { + $this->login(); + $storage = [ + // Checked out due soon + ['checkedOut' => ['warn' => 1]] + ]; + $this->checkIcon($storage, '.fa-bell.text-warning'); + } + + /** + * Check cases that change the account icon to an alarming triangle + * + * @return void + */ + public function testIconDanger() + { + $this->login(); + $storage = [ + // User has fines + ['fines' => ['value' => 1000000, 'display' => '$...yikes']], + // Checkedout overdue + ['checkedOut' => ['overdue' => 1]], + ]; + $this->checkIcon($storage, '.fa-exclamation-triangle'); + } + + /** + * More urgent cases should override lower cases + * + * Danger > Warning > Good > None + * + * @return void + */ + public function testIconClashes() + { + $this->login(); + // Danger overrides warning + $this->checkIcon( + [['checkedOut' => ['warn' => 2, 'overdue' => 1]]], + '.fa-exclamation-triangle' + ); + // Danger overrides good + $this->checkIcon( + [ + [ + 'checkedOut' => ['overdue' => 1], + 'holds' => ['available' => 1] + ] + ], + '.fa-exclamation-triangle' + ); + // Warning overrides good + $this->checkIcon( + [ + [ + 'checkedOut' => ['warn' => 1], + 'holds' => ['available' => 1] + ] + ], + '.fa-bell.text-warning' + ); + // Good overrides none + $this->checkIcon( + [ + [ + 'holds' => ['available' => 1], + 'fines' => ['value' => 0, 'display' => 'none'] + ] + ], + '.fa-bell.text-success' + ); + } + + /** + * Standard teardown method. + * + * @return void + */ + public static function tearDownAfterClass() + { + static::removeUsers(['username1']); + } +} diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php index 80553f01fb3b6481438386468a5f9184f13552c8..fe60d477a72acbae8cdc28b067970f6f6713674c 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/AdvancedSearchTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class AdvancedSearchTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Test persistent * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php index 483756c9261d910e75e1fa65435e9e04f88449aa..bb69cd4e8ea0abd3cfcc0750bcb7b32cb30043da 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BasicTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class BasicTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Test that the home page is available. * @@ -64,7 +67,7 @@ class BasicTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:testsample1'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); // Check for sample driver location/call number in output (this will @@ -95,8 +98,8 @@ class BasicTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, 'footer .help-link')->getHTML() ); // Change the language: - $this->findCss($page, '.language.dropdown')->click(); - $this->findCss($page, '.language.dropdown li:not(.active) a')->click(); + $this->clickCss($page, '.language.dropdown'); + $this->clickCss($page, '.language.dropdown li:not(.active) a'); $this->snooze(); // Check footer help-link $this->assertNotEquals( @@ -116,10 +119,10 @@ class BasicTest extends \VuFindTest\Unit\MinkTestCase $session->visit($this->getVuFindUrl() . '/Search/Home'); $page = $session->getPage(); // Open Search tips lightbox - $this->findCss($page, 'footer .help-link')->click(); + $this->clickCss($page, 'footer .help-link'); $this->snooze(); // Click a jump link - $this->findCss($page, '.modal-body .HelpMenu a')->click(); + $this->clickCss($page, '.modal-body .HelpMenu a'); // Make sure we're still in the Search Tips $this->snooze(); $this->findCss($page, '.modal-body .HelpMenu'); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php index 114409a8d84ebdcfe2ec00937372875017378d1f..c27b0c9e8f489040de3f71045f6f9503878c8667 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/BulkTest.php @@ -37,9 +37,11 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class BulkTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -64,7 +66,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase $session->visit($this->getVuFindUrl() . $path); $page = $session->getPage(); // Hide autocomplete menu - $this->findCss($page, '#side-panel-format .title')->click(); + $this->clickCss($page, '#side-panel-format .title'); return $page; } @@ -127,6 +129,8 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase /** * Test that the email control works. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testBulkEmail() @@ -148,16 +152,17 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase $this->checkForLoginMessage($page); // Create an account. - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.modal-body .createAccountLink'); + $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@asdf.com'); $this->findCssAndSetValue($page, '.modal #email_message', 'message'); $this->findCssAndSetValue( $page, '.modal #email_to', 'demian.katz@villanova.edu' ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); /* TODO: add back this check when everything is working (as of this * writing, the pop-up message is inexplicably missing... but we should @@ -198,7 +203,7 @@ class BulkTest extends \VuFindTest\Unit\MinkTestCase $this->submitLoginForm($page); // Save the favorites. - $this->findCss($page, '.modal-body input[name=submit]')->click(); + $this->clickCss($page, '.modal-body input[name=submit]'); $this->snooze(); $result = $this->findCss($page, '.modal-body .alert-success'); $this->assertEquals( diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php index 8cf96006b98e1b192aabee225647c5d55cd358ec..97896df502f4b58f9c14f6c736f79bf28b26a4fa 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php @@ -35,9 +35,17 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class CallnumberBrowseTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + + /** + * Record ID to use in testing. + * + * @var string + */ protected $id = 'testdeweybrowse'; /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php index b0b8767375e3ac1cbc935cf955fa73dbaeceff46..60155c12a27d9824b4d53dec7e7834bd783df29f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CartTest.php @@ -37,9 +37,11 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class CartTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -406,7 +408,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase // Test that we can add multiple records: for ($x = 1; $x <= 3; $x++) { $page = $this->getRecordPage('testsample' . $x); - $this->findCss($page, '.cart-add')->click(); + $this->clickCss($page, '.cart-add'); $this->assertEquals( $x . ' items', $this->findCss($page, '#cartItems')->getText() ); @@ -427,7 +429,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase // First try deleting without selecting anything: $delete->click(); $this->snooze(); - $this->findCss($page, '#cart-confirm-delete')->click(); + $this->clickCss($page, '#cart-confirm-delete'); $this->snooze(); $this->checkForNonSelectedMessage($page); @@ -531,6 +533,8 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase /** * Test that the email control works. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testCartEmail() @@ -552,9 +556,10 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $this->checkForLoginMessage($page); // Create an account. - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.modal-body .createAccountLink'); + $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@asdf.com'); @@ -562,7 +567,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $this->findCssAndSetValue( $page, '.modal #email_to', 'demian.katz@villanova.edu' ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->assertEquals( @@ -598,7 +603,7 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase // Save the favorites. $this->snooze(); - $this->findCss($page, '.modal-body input[name=submit]')->click(); + $this->clickCss($page, '.modal-body input[name=submit]'); $this->snooze(); $result = $this->findCss($page, '.modal-body .alert-success'); $this->assertEquals( @@ -682,14 +687,16 @@ class CartTest extends \VuFindTest\Unit\MinkTestCase $select->selectOption('Google'); // Do the export: + $windowCount = count($this->getMinkSession()->getWindowNames()); $submit = $this->findCss($page, '.modal-body input[name=submit]'); $submit->click(); $this->snooze(); $windows = $this->getMinkSession()->getWindowNames(); - $this->assertEquals(2, count($windows)); - $this->getMinkSession()->switchToWindow($windows[1]); + $this->assertEquals($windowCount + 1, count($windows)); + $this->getMinkSession()->switchToWindow($windows[$windowCount]); $this->assertEquals( - 'https://www.google.com/', $this->getMinkSession()->getCurrentUrl() + 'https://www.google.com/', + $this->getMinkSession()->getCurrentUrl() ); } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php index ab64cfa2f9b7cd99e3eb6ebde08d8edf7c20c48a..224bfa2f32291a3745f8ed14b2927aa261bcae56 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChannelsTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Get a reference to a standard search results page. * @@ -82,7 +85,7 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals(6, count($page->findAll('css', 'div.channel-wrapper'))); $this->assertEquals(8, count($channel->findAll('css', '.channel-add-menu .dropdown-menu li'))); // Click first add button - $this->findCss($channel, '.add-btn')->click(); + $this->clickCss($channel, '.add-btn'); $this->snooze(); // Post count $this->assertEquals(8, count($page->findAll('css', 'div.channel-wrapper'))); @@ -97,10 +100,10 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase $page = $this->getChannelsPage(); $channel = $this->findCss($page, 'div.channel-wrapper'); // Click dropdown to display links - $this->findCss($channel, '.dropdown')->click(); + $this->clickCss($channel, '.dropdown'); $this->snooze(); // Click link to go to search results - $this->findCss($channel, '.channel_search')->click(); + $this->clickCss($channel, '.channel_search'); $this->snooze(); // Make sure the search translated $this->assertEquals( @@ -109,8 +112,12 @@ class ChannelsTest extends \VuFindTest\Unit\MinkTestCase ); // Check facet $this->assertEquals( - 'Clear Filter Suggested Topics: Adult children of aging parents', - $this->findCss($page, '.active-filters .facet')->getText() + 'Suggested Topics:', + $this->findCss($page, '.filters .filters-title')->getText() + ); + $this->assertEquals( + 'Adult children of aging parents', + $this->findCss($page, '.filters .filter-value')->getText() ); } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php index b01ecb0c686d7a4a2a758626f350068bf7e909ac..9cf1cdd90ccafe3d7e98f3eb5d299f4e12f1883a 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ChoiceAuthTest.php @@ -35,9 +35,11 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class ChoiceAuthTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -99,6 +101,10 @@ class ChoiceAuthTest extends \VuFindTest\Unit\MinkTestCase /** * Test creating a DB user.... + * + * @retryCallback tearDownAfterClass + * + * @return void */ public function testCreateDatabaseUser() { @@ -117,14 +123,14 @@ class ChoiceAuthTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals('Login', $element->getText()); $element->click(); $this->snooze(); - $this->findCss($page, '.createAccountLink')->click(); + $this->clickCss($page, '.createAccountLink'); $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); // Log out - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // Log back in to confirm that creation worked @@ -135,7 +141,7 @@ class ChoiceAuthTest extends \VuFindTest\Unit\MinkTestCase $this->submitLoginForm($page, true, '.authmethod0 '); // Log out again to confirm that login worked - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php index ec7e23329856b1b489dd3fde2751c89bc4ccbe73..409d82aacf118b6357c78be65f9c191db9a0828f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CollectionsTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class CollectionsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Go to a collection page. * @@ -149,7 +152,7 @@ class CollectionsTest extends \VuFindTest\Unit\MinkTestCase trim($this->findCss($page, '#tree-preview h2')->getText()), 'Subcollection 1' ); - $this->findCss($page, '[recordid="colitem2"] a')->click(); + $this->clickCss($page, '[recordid="colitem2"] a'); $this->snooze(); $this->assertEquals( diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php index 2d340dc66800d59049bbcf179c85eaeb563e5bb4..ae05c724a3cda162df1cdab38e2d4bb5820fc83d 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CombinedSearchTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class CombinedSearchTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Get config settings for combined.ini. * @@ -107,7 +110,7 @@ class CombinedSearchTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:"testsample1" OR id:"theplus+andtheminus-"'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); $this->assertResultsForDefaultQuery($page); } @@ -130,7 +133,7 @@ class CombinedSearchTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:"testsample1" OR id:"theplus+andtheminus-"'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); $this->assertResultsForDefaultQuery($page); } @@ -152,7 +155,7 @@ class CombinedSearchTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:"testsample1" OR id:"theplus+andtheminus-"'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); $this->assertResultsForDefaultQuery($page); } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php index 94248564d8140e105195e96bb2933e1838658a32..26e2767c2d0566d803ba36a3ed30214d96c47b59 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FavoritesTest.php @@ -37,9 +37,11 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -76,7 +78,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $session->visit($this->getVuFindUrl() . '/Search/Home'); $page = $session->getPage(); $this->findCssAndSetValue($page, '#searchForm_lookfor', 'Dewey'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); return $page; } @@ -89,7 +91,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase protected function gotoRecord() { $page = $this->gotoSearch(); - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); return $page; } @@ -110,53 +112,55 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase * Test adding a record to favorites (from the record page) while creating a * new account. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testAddRecordToFavoritesNewAccount() { $page = $this->gotoRecord(); - $this->findCss($page, '.save-record')->click(); - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.save-record'); + $this->clickCss($page, '.modal-body .createAccountLink'); // Empty $this->snooze(); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); // Invalid email $this->snooze(); $this->fillInAccountForm($page, ['email' => 'blargasaurus']); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); // Correct $this->findCssAndSetValue($page, '#account_email', 'username1@ignore.com'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '#save_list'); // Make list - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); // Empty - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Test List'); $this->findCssAndSetValue($page, '#list_desc', 'Just. THE BEST.'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertEquals($this->findCss($page, '#save_list option[selected]')->getHtml(), 'Test List'); $this->findCssAndSetValue($page, '#add_mytags', 'test1 test2 "test 3"'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.modal .alert.alert-success'); - $this->findCss($page, '.modal-body .btn.btn-default')->click(); + $this->clickCss($page, '.modal-body .btn.btn-default'); // Check list page $session = $this->getMinkSession(); $recordURL = $this->stripHash($session->getCurrentUrl()); $this->snooze(); - $this->findCss($page, '.savedLists a')->click(); + $this->clickCss($page, '.savedLists a'); $this->snooze(); - $this->findCss($page, '.resultItemLine1 a')->click(); + $this->clickCss($page, '.resultItemLine1 a'); $this->assertEquals($recordURL, $this->stripHash($session->getCurrentUrl())); - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** @@ -169,7 +173,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase { $page = $this->gotoRecord(); - $this->findCss($page, '.save-record')->click(); + $this->clickCss($page, '.save-record'); // Login // - empty $this->submitLoginForm($page); @@ -185,26 +189,26 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->assertNull($page->find('css', '.modal-body #save_list')); // Make Two Lists // - One for the next test - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Future List'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Future List' ); // - One for now - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Login Test List'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Login Test List' ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.modal .alert.alert-success'); } @@ -219,16 +223,16 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase { $page = $this->gotoRecord(); // Login - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); // Save Record $this->snooze(); - $this->findCss($page, '.save-record')->click(); + $this->clickCss($page, '.save-record'); $this->snooze(); $this->findCss($page, '#save_list'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.modal .alert.alert-success'); } @@ -237,63 +241,67 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase * Test adding a record to favorites (from the search results) while creating a * new account. * + * @retryCallback removeUsername2 + * * @return void */ public function testAddSearchItemToFavoritesNewAccount() { $page = $this->gotoSearch(); - $this->findCss($page, '.save-record')->click(); - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.save-record'); + $this->clickCss($page, '.modal-body .createAccountLink'); // Empty $this->snooze(); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); + $this->snooze(); $this->fillInAccountForm( $page, ['username' => 'username2', 'email' => 'blargasaurus'] ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCssAndSetValue($page, '#account_email', 'username2@ignore.com'); // Test taken username $this->findCssAndSetValue($page, '#account_username', 'username1'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); + $this->snooze(); $this->findCss($page, '#account_firstname'); // Correct $this->fillInAccountForm( $page, ['username' => 'username2', 'email' => 'username2@ignore.com'] ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '#save_list'); // Make list - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); // Empty - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Test List'); $this->findCssAndSetValue($page, '#list_desc', 'Just. THE BEST.'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Test List' ); $this->findCssAndSetValue($page, '#add_mytags', 'test1 test2 "test 3"'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.alert.alert-success'); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); // Check list page $this->snooze(); - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); $this->snooze(); $session = $this->getMinkSession(); $recordURL = $session->getCurrentUrl(); - $this->findCss($page, '.savedLists a')->click(); + $this->clickCss($page, '.savedLists a'); $this->snooze(); - $this->findCss($page, '.resultItemLine1 a')->click(); + $this->clickCss($page, '.resultItemLine1 a'); $this->snooze(); $this->assertEquals($recordURL, $session->getCurrentUrl()); - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** @@ -306,7 +314,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase { $page = $this->gotoSearch(); - $this->findCss($page, '.save-record')->click(); + $this->clickCss($page, '.save-record'); $this->snooze(); // Login // - empty @@ -320,26 +328,26 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->assertNull($page->find('css', '.modal-body #save_list')); // Make Two Lists // - One for the next test - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Future List'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Future List' ); // - One for now - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); $this->findCssAndSetValue($page, '#list_title', 'Login Test List'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->assertEquals( $this->findCss($page, '#save_list option[selected]')->getHtml(), 'Login Test List' ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.alert.alert-success'); } @@ -354,21 +362,21 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase { $page = $this->gotoSearch(); // Login - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username2', 'test'); $this->submitLoginForm($page); // Count lists $listCount = count($page->findAll('css', '.savedLists a')); // Save Record - $this->findCss($page, '.save-record')->click(); + $this->clickCss($page, '.save-record'); $this->snooze(); $this->findCss($page, '#save_list'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '.alert.alert-success'); // Test save status update on modal close - $this->findCss($page, '.modal-body .btn.btn-default')->click(); + $this->clickCss($page, '#modal .close'); $this->snooze(); $savedLists = $page->findAll('css', '.savedLists a'); $this->assertEquals($listCount + 1, count($savedLists)); @@ -394,7 +402,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $session->visit($this->getVuFindUrl() . $path); $page = $session->getPage(); // Login - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); @@ -419,7 +427,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase 'No items were selected. Please click on a checkbox next to an item and try again.', $warning->getText() ); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); $this->snooze(); } @@ -446,7 +454,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $page = $this->setupBulkTest(); // First try clicking without selecting anything: - $button = $this->findCss($page, '[name=bulkActionForm] .btn-group [name=email]'); + $button = $this->findCss($page, '[name=bulkActionForm] [name=email]'); $button->click(); $this->snooze(); $this->checkForNonSelectedMessage($page); @@ -458,7 +466,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->findCssAndSetValue($page, '.modal #email_to', 'tester@vufind.org'); $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@vufind.org'); $this->findCssAndSetValue($page, '.modal #email_message', 'message'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->assertEquals( @@ -477,7 +485,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $page = $this->setupBulkTest(); // First try clicking without selecting anything: - $button = $this->findCss($page, '[name=bulkActionForm] .btn-group [name=export]'); + $button = $this->findCss($page, '[name=bulkActionForm] [name=export]'); $button->click(); $this->snooze(); $this->checkForNonSelectedMessage($page); @@ -510,7 +518,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $page = $this->setupBulkTest(); // First try clicking without selecting anything: - $button = $this->findCss($page, '[name=bulkActionForm] .btn-group [name=print]'); + $button = $this->findCss($page, '[name=bulkActionForm] [name=print]'); $button->click(); $this->snooze(); $warning = $this->findCss($page, '.flash-message'); @@ -543,17 +551,17 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $button = $this->findAndAssertLink($page, 'Edit List'); $button->click(); $this->snooze(); - $this->findCss($page, '#list_public_1')->click(); // radio button - $this->findCss($page, 'input[name="submit"]')->click(); // submit button + $this->clickCss($page, '#list_public_1'); // radio button + $this->clickCss($page, 'input[name="submit"]'); // submit button $this->snooze(); // Now log out: - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // Now try to email the list: $this->selectAllItemsInList($page); - $this->findCss($page, '[name=bulkActionForm] .btn-group [name=email]') + $this->findCss($page, '[name=bulkActionForm] [name=email]') ->click(); $this->snooze(); @@ -565,7 +573,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->findCssAndSetValue($page, '.modal #email_to', 'tester@vufind.org'); $this->findCssAndSetValue($page, '.modal #email_from', 'asdf@vufind.org'); $this->findCssAndSetValue($page, '.modal #email_message', 'message'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->assertEquals( @@ -584,7 +592,7 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $page = $this->setupBulkTest(); // First try clicking without selecting anything: - $button = $this->findCss($page, '[name=bulkActionForm] .btn-group [name=delete]'); + $button = $this->findCss($page, '[name=bulkActionForm] [name=delete]'); $button->click(); $this->snooze(); $this->checkForNonSelectedMessage($page); @@ -593,18 +601,29 @@ class FavoritesTest extends \VuFindTest\Unit\MinkTestCase $this->selectAllItemsInList($page); $button->click(); $this->snooze(); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->assertEquals( 'Your favorite(s) were deleted.', $this->findCss($page, '.modal .alert-success')->getText() ); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); $this->snooze(); $this->assertFalse(is_object($page->find('css', '.result'))); } + /** + * Retry cleanup method in case of failure during + * testAddSearchItemToFavoritesNewAccount. + * + * @return void + */ + protected function removeUsername2() + { + static::removeUsers(['username2']); + } + /** * Standard teardown method. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php index b816448e040ec3804f647f4bcb41a0c89724fdc9..42e1df94789997aefc56877613314a28b480087e 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/FeedbackTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class FeedbackTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Standard setup method. * @@ -102,13 +105,13 @@ class FeedbackTest extends \VuFindTest\Unit\MinkTestCase { // By default, no OpenURL on record page: $page = $this->setupPage(); - $this->findCss($page, '#feedbackLink')->click(); + $this->clickCss($page, '#feedbackLink'); $this->snooze(); $this->findCss($page, '#modal .form-control[name="name"]')->setValue('Me'); $this->findCss($page, '#modal .form-control[name="email"]') ->setValue('test@test.com'); $this->findCss($page, "#modal #message")->setValue('test test test'); - $this->findCss($page, '#modal input[type="submit"]')->click(); + $this->clickCss($page, '#modal input[type="submit"]'); $this->snooze(); $this->assertEquals( 'Thank you for your feedback.', diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php index 7e72aa87ab6689bc3bafe8840947ee9c40ee83af..d38774fcceeed6ca6348417fa1797d055484b915 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/IlsActionsTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + use \VuFindTest\Unit\DemoDriverTestTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -82,93 +85,6 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ]; } - /** - * Get transaction JSON for Demo.ini. - * - * @param string $bibId Bibliographic record ID to create fake item info for. - * - * @return array - */ - protected function getFakeTransactions($bibId) - { - $rawDueDate = strtotime("now +5 days"); - return json_encode( - [ - [ - 'duedate' => $rawDueDate, - 'rawduedate' => $rawDueDate, - 'dueStatus' => 'due', - 'barcode' => 1234567890, - 'renew' => 0, - 'renewLimit' => 1, - 'request' => 0, - 'id' => $bibId, - 'source' => 'Solr', - 'item_id' => 0, - 'renewable' => true, - ] - ] - ); - } - - /** - * Get Demo.ini override settings for testing ILS functions. - * - * @param string $bibId Bibliographic record ID to create fake item info for. - * - * @return array - */ - public function getDemoIniOverrides($bibId = 'testsample1') - { - return [ - 'Records' => [ - 'transactions' => $this->getFakeTransactions($bibId), - ], - 'Failure_Probabilities' => [ - 'cancelHolds' => 0, - 'cancelILLRequests' => 0, - 'cancelStorageRetrievalRequests' => 0, - 'checkILLRequestIsValid' => 0, - 'checkRenewBlock' => 0, - 'checkRequestIsValid' => 0, - 'checkStorageRetrievalRequestIsValid' => 0, - 'getAccountBlocks' => 0, - 'getDefaultRequestGroup' => 0, - 'getHoldDefaultRequiredDate' => 0, - 'getRequestBlocks' => 0, - 'placeHold' => 0, - 'placeILLRequest' => 0, - 'placeStorageRetrievalRequest' => 0, - 'renewMyItems' => 0, - ], - 'Holdings' => [$bibId => json_encode([$this->getFakeItem()])], - 'Users' => ['catuser' => 'catpass'], - ]; - } - - /** - * Get a fake item record for inclusion in the Demo driver configuration. - * - * @return array - */ - public function getFakeItem() - { - return [ - 'barcode' => '12345678', - 'availability' => true, - 'status' => 'Available', - 'location' => 'Test Location', - 'locationhref' => false, - 'reserve' => 'N', - 'callnumber' => 'Test Call Number', - 'duedate' => '', - 'is_holdable' => true, - 'addLink' => true, - 'addStorageRetrievalRequestLink' => 'check', - 'addILLRequestLink' => 'check', - ]; - } - /** * Move the current page to a record by performing a search. * @@ -196,7 +112,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase { $this->findCss($page, '#profile_cat_username')->setValue($username); $this->findCss($page, '#profile_cat_password')->setValue($password); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); } @@ -210,13 +126,13 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase protected function placeHoldAndGoToHoldsScreen($page) { // Open the "place hold" dialog - $this->findCss($page, 'a.placehold')->click(); + $this->clickCss($page, 'a.placehold'); $this->snooze(); // Set pickup location to a non-default value so we can confirm that // the element is being passed through correctly, then submit form: $this->findCss($page, '#pickUpLocation')->setValue('B'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // If successful, we should now have a link to review the hold: @@ -242,7 +158,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase { // Open the "place hold" dialog $this->snooze(); - $this->findCss($page, 'a.placeILLRequest')->click(); + $this->clickCss($page, 'a.placeILLRequest'); $this->snooze(); // Set pickup location to a non-default value so we can confirm that @@ -250,7 +166,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '#pickupLibrary')->setValue('2'); $this->snooze(); $this->findCss($page, '#pickupLibraryLocation')->setValue('3'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // If successful, we should now have a link to review the hold: @@ -265,17 +181,25 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); } + /** + * Support method to place a storage retrieval request and end up on the SRR + * screen. + * + * @param Element $page Page element. + * + * @return void + */ protected function placeStorageRetrievalRequestAndGoToSRRScreen($page) { // Open the "place hold" dialog $this->snooze(); - $this->findCss($page, 'a.placeStorageRetrievalRequest')->click(); + $this->clickCss($page, 'a.placeStorageRetrievalRequest'); $this->snooze(); // Set pickup location to a non-default value so we can confirm that // the element is being passed through correctly, then submit form: $this->findCss($page, '.modal-body select')->setValue('C'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // If successful, we should now have a link to review the hold: @@ -290,6 +214,13 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); } + /** + * Test placing a hold + * + * @retryCallback tearDownAfterClass + * + * @return void + */ public function testPlaceHold() { $this->changeConfigs( @@ -303,10 +234,10 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals('Login for hold and recall information', $element->getText()); $element->click(); $this->snooze(); - $this->findCss($page, '.createAccountLink')->click(); + $this->clickCss($page, '.createAccountLink'); $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); // Test invalid patron login @@ -379,7 +310,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase $this->placeHoldAndGoToHoldsScreen($page); // Test empty selection - $this->findCss($page, '#cancelSelected')->click(); + $this->clickCss($page, '#cancelSelected'); $this->clickButtonGroupLink($page, 'Yes'); $this->snooze(); $this->assertEquals( @@ -396,7 +327,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); // Click cancel but bail out with no... item should still be there. - $this->findCss($page, '#cancelAll')->click(); + $this->clickCss($page, '#cancelAll'); $this->clickButtonGroupLink($page, 'No'); $this->snooze(); $this->assertEquals( @@ -406,7 +337,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); // Now cancel for real: - $this->findCss($page, '#cancelAll')->click(); + $this->clickCss($page, '#cancelAll'); $this->clickButtonGroupLink($page, 'Yes'); $this->snooze(); $this->assertEquals( @@ -544,7 +475,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase $this->submitLoginForm($page, false); // Test submitting with no selected checkboxes: - $this->findCss($page, '#renewSelected')->click(); + $this->clickCss($page, '#renewSelected'); $this->snooze(); $this->assertEquals( 'No items were selected', @@ -552,7 +483,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); // Test "renew all": - $this->findCss($page, '#renewAll')->click(); + $this->clickCss($page, '#renewAll'); $this->snooze(); $this->assertEquals( 'Renewal Successful', @@ -567,6 +498,8 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase * that Apache is configured with "AllowEncodedSlashes on" inside the * VirtualHost used for your VuFind test instance! * + * @retryCallback removeUsername2 + * * @return void */ public function testHoldsAll() @@ -588,12 +521,12 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase $element->click(); $this->snooze(); // Since we're not logged in... - $this->findCss($page, '.createAccountLink')->click(); + $this->clickCss($page, '.createAccountLink'); $this->snooze(); $this->fillInAccountForm( $page, ['username' => 'username2', 'email' => 'u2@vufind.org'] ); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); // Test valid patron login @@ -603,7 +536,7 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase // Set pickup location to a non-default value so we can confirm that // the element is being passed through correctly, then submit form: $this->findCss($page, '#pickUpLocation')->setValue('B'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // If successful, we should now have a link to review the hold: @@ -618,6 +551,16 @@ class IlsActionsTest extends \VuFindTest\Unit\MinkTestCase ); } + /** + * Retry cleanup method in case of failure during testHoldsAll. + * + * @return void + */ + protected function removeUsername2() + { + static::removeUsers(['username2']); + } + /** * Standard teardown method. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php index 1b8c54336e5c73afed237c37be6c3db544679a9e..837e7bd609eea7f08f641cda7dbc1ab9515010c1 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/JumpToRecordTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class JumpToRecordTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Test that we can jump to the first record in a single-record result set. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php index 69cd495cb92e40bc523a352aecd8fd899c04f2e6..a188b4674c152aadc5f80ca229f777377fffc104 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/LinkResolverTest.php @@ -37,9 +37,12 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Standard setup method. * @@ -107,14 +110,17 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase { // Click the OpenURL link: if ($click) { - $this->findCss($page, '.fulltext')->click(); + $this->clickCss($page, '.fulltext'); } $this->snooze(); // Confirm that the expected fake demo driver data is there: $electronic = $this->findCss($page, 'a.access-open'); $this->assertEquals('Electronic', $electronic->getText()); - $this->assertEquals('Electronic fake2', $electronic->getParent()->getText()); + $this->assertEquals( + 'Electronic fake2 General notes Authentication notes', + $electronic->getParent()->getText() + ); $openUrl = 'url_ver=Z39.88-2004&ctx_ver=Z39.88-2004' . '&ctx_enc=info%3Aofi%2Fenc%3AUTF-8' . '&rfr_id=info%3Asid%2Fvufind.svn.sourceforge.net%3Agenerator' @@ -130,7 +136,9 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase $print = $this->findCss($page, 'a.access-unknown'); $this->assertEquals('Print', $print->getText()); - $this->assertEquals('Print fake1', $print->getParent()->getText()); + $this->assertEquals( + 'Print fake1 General notes', $print->getParent()->getText() + ); $this->assertEquals( 'https://vufind.org/wiki?' . $openUrl . '#print', $print->getAttribute('href') @@ -157,7 +165,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:testsample1'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); // Verify the OpenURL @@ -186,7 +194,7 @@ class LinkResolverTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:testsample1'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); // Verify the OpenURL $this->assertOpenUrl($page, false /* do not click link */); diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php index c58101e08009ec9dbb6038255af03d4a5c2edc82..28260bc8075d5d662fbf15763fc36c089c759fe4 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/ListViewsTest.php @@ -37,9 +37,11 @@ use Behat\Mink\Element\Element; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://www.vufind.org Main Page + * @retry 4 */ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -77,7 +79,7 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('id:testdeweybrowse'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); $this->snooze(); return $page; } @@ -91,7 +93,7 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase protected function gotoRecord() { $page = $this->gotoSearch(); - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); $this->snooze(); return $page; } @@ -99,6 +101,8 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase /** * Test that we can save a favorite from tab mode. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testFavoritesInTabMode() @@ -111,18 +115,19 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase $page = $this->gotoRecord(); // Click save inside the tools tab - $this->findCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a')->click(); - $this->findCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a-content .save-record')->click(); + $this->clickCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a'); + $this->clickCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a-content .save-record'); // Make an account - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.modal-body .createAccountLink'); + $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $this->findCss($page, '#save_list'); // Save to list - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); - $this->findCss($page, '#modal .close')->click(); + $this->clickCss($page, '#modal .close'); $this->snooze(); // Check saved items status $this->findCss($page, '#information_cd588d8723d65ca0ce9439e79755fa0a-content .savedLists ul'); @@ -143,23 +148,24 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase $page = $this->gotoRecord(); // Click save inside the tools tab - $this->findCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a')->click(); - $this->findCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a-content .save-record')->click(); + $this->clickCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a'); + $this->snooze(); + $this->clickCss($page, '#tools_cd588d8723d65ca0ce9439e79755fa0a-content .save-record'); $this->snooze(); // Login $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); // Make list - $this->findCss($page, '#make-list')->click(); + $this->clickCss($page, '#make-list'); $this->snooze(); $this->findCss($page, '#list_title')->setValue('Test List'); $this->findCss($page, '#list_desc')->setValue('Just. THE BEST.'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Save to list - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); - $this->findCss($page, '#modal .close')->click(); + $this->clickCss($page, '#modal .close'); $this->snooze(); // Check saved items status // Not visible, but still exists @@ -178,25 +184,27 @@ class ListViewsTest extends \VuFindTest\Unit\MinkTestCase // Reload the page to close all results $session->reload(); + $this->snooze(); // Did our saved one open automatically? $this->findCss($page, '.result.embedded'); // Close it - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); // Did our result stay closed? $session->reload(); + $this->snooze(); $result = $page->find('css', '.result.embedded'); $this->assertFalse(is_object($result)); // Open it - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); $this->snooze(); // Search for anything else $session->visit($this->getVuFindUrl() . '/Search/Home'); $page = $session->getPage(); $this->findCss($page, '#searchForm_lookfor') ->setValue('anything else'); - $this->findCss($page, '.btn.btn-primary')->click(); + $this->clickCss($page, '.btn.btn-primary'); // Come back $page = $this->gotoSearch(); // Did our result close after not being being in the last search? diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php index 7eb106e97af4eef4ecb46da0467777fba498e959..8b33761f4117df6b0d12e10a59bd32634971c4b1 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Conor Sheehan <csheehan@nli.ie> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class NextPrevNavTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * if next_prev_navigation and first_last_navigation are set to true * and a search which returns no results is run diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php index 2883b66b768b39a577ef5fc0fc84f0d50fe4a445..3835313dcb47347ae98012d3057a2d7e97b706ed 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordActionsTest.php @@ -35,9 +35,11 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -71,29 +73,34 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase protected function gotoRecord() { $page = $this->performSearch('Dewey'); - $this->findCss($page, '.result a.title')->click(); + $this->clickCss($page, '.result a.title'); return $page; } /** * Make new account * + * @param \Behat\Mink\Element\Element $page Page element + * @param string $username Username to create + * * @return void */ protected function makeAccount($page, $username) { - $this->findCss($page, '.modal-body .createAccountLink')->click(); + $this->clickCss($page, '.modal-body .createAccountLink'); $this->snooze(); $this->fillInAccountForm( $page, ['username' => $username, 'email' => $username . '@vufind.org'] ); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); } /** * Test adding comments on records. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testAddComment() @@ -102,43 +109,49 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase $page = $this->gotoRecord(); // Click add comment without logging in // TODO Rewrite for comment and login coming - $this->findCss($page, '.record-tabs .usercomments')->click(); + $this->clickCss($page, '.record-tabs .usercomments'); $this->snooze(); $this->findCss($page, '.comment-form'); $this->assertEquals(// Can Comment? 'You must be logged in first', $this->findCss($page, 'form.comment-form .btn.btn-primary')->getText() ); - $this->findCss($page, 'form.comment-form .btn-primary')->click(); + $this->clickCss($page, 'form.comment-form .btn-primary'); + $this->snooze(); $this->findCss($page, '.modal.in'); // Lightbox open $this->findCss($page, '.modal [name="username"]'); // Create new account $this->makeAccount($page, 'username1'); // Make sure page updated for login // $page = $this->gotoRecord(); - $this->findCss($page, '.record-tabs .usercomments')->click(); + $this->clickCss($page, '.record-tabs .usercomments'); + $this->snooze(); $this->assertEquals(// Can Comment? 'Add your comment', $this->findCss($page, 'form.comment-form .btn.btn-primary')->getValue() ); // "Add" empty comment - $this->findCss($page, 'form.comment-form .btn-primary')->click(); + $this->clickCss($page, 'form.comment-form .btn-primary'); + $this->snooze(); $this->assertNull($page->find('css', '.comment')); // Add comment $this->findCss($page, 'form.comment-form [name="comment"]')->setValue('one'); - $this->findCss($page, 'form.comment-form .btn-primary')->click(); + $this->clickCss($page, 'form.comment-form .btn-primary'); + $this->snooze(); $this->findCss($page, '.comment'); // Remove comment - $this->findCss($page, '.comment .delete')->click(); + $this->clickCss($page, '.comment .delete'); $this->snooze(); // wait for UI update $this->assertNull($page->find('css', '.comment')); // Logout - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** * Test adding tags on records. * + * @retryCallback removeUsername2 + * * @return void */ public function testAddTag() @@ -147,7 +160,7 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase $page = $this->gotoRecord(); // Click to add tag $this->snooze(); - $this->findCss($page, '.tag-record')->click(); + $this->clickCss($page, '.tag-record'); $this->snooze(); // Lightbox login open? $this->findCss($page, '.modal.in [name="username"]'); @@ -155,23 +168,23 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase $this->makeAccount($page, 'username2'); // Add tag exists? $this->findCss($page, '.modal #addtag_tag'); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); $this->snooze(); // wait for display to update - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // Login // $page = $this->gotoRecord(); - $this->findCss($page, '.tag-record')->click(); + $this->clickCss($page, '.tag-record'); $this->snooze(); $this->fillInLoginForm($page, 'username2', 'test'); $this->submitLoginForm($page); // Add tags $this->findCss($page, '.modal #addtag_tag')->setValue('one 2 "three 4" five'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $success = $this->findCss($page, '.modal-body .alert-success'); $this->assertEquals('Tags Saved', $success->getText()); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); // Count tags $this->snooze(); // wait for UI update $tags = $page->findAll('css', '.tagList .tag'); @@ -197,18 +210,18 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase } $this->assertEquals(3, $sum); // Log out - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); $this->snooze(); // wait for UI update // Flat tags $this->assertNull($page->find('css', '.tagList .tag.selected')); $this->assertNull($page->find('css', '.tagList .tag .fa')); // Login with second account - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->findCss($page, '.modal.in [name="username"]'); $this->fillInLoginForm($page, 'username1', 'test'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // $page = $this->gotoRecord(); // Check selected == 0 @@ -216,20 +229,22 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase $this->findCss($page, '.tagList .tag'); $this->findCss($page, '.tagList .tag .fa-plus'); // Click one - $this->findCss($page, '.tagList .tag button')->click(); + $this->clickCss($page, '.tagList .tag button'); $this->snooze(); // Check selected == 1 $this->findCss($page, '.tagList .tag.selected'); // Click again - $this->findCss($page, '.tagList .tag button')->click(); + $this->clickCss($page, '.tagList .tag button'); $this->snooze(); // Check selected == 0 $this->assertNull($page->find('css', '.tagList .tag.selected')); - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** * Test searching for one of the tags created above. + * + * @return void */ public function testTagSearch() { @@ -264,17 +279,17 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase ); // Login $page = $this->gotoRecord(); - $this->findCss($page, '.tag-record')->click(); + $this->clickCss($page, '.tag-record'); $this->snooze(); $this->fillInLoginForm($page, 'username2', 'test'); $this->submitLoginForm($page); // Add tags $this->findCss($page, '.modal #addtag_tag')->setValue('one ONE "new tag" ONE "THREE 4"'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); $success = $this->findCss($page, '.modal-body .alert-success'); $this->assertEquals('Tags Saved', $success->getText()); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); // Count tags $this->snooze(); $tags = $page->findAll('css', '.tagList .tag'); @@ -284,6 +299,8 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase /** * Test record view email. * + * @retryCallback removeEmailManiac + * * @return void */ public function testEmail() @@ -300,7 +317,7 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase // Go to a record view $page = $this->gotoRecord(); // Click email record without logging in - $this->findCss($page, '.mail-record')->click(); + $this->clickCss($page, '.mail-record'); $this->snooze(); $this->findCss($page, '.modal.in [name="username"]'); // Make account @@ -314,18 +331,18 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase // Send text to false email $this->snooze(); $this->findCss($page, '.modal #email_to')->setValue('asdf@vufind.org'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->findCss($page, '.modal .alert-success'); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); // Logout - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); // Go to a record view $page = $this->gotoRecord(); // Click email record without logging in - $this->findCss($page, '.mail-record')->click(); + $this->clickCss($page, '.mail-record'); $this->snooze(); $this->findCss($page, '.modal.in [name="username"]'); // Login in Lightbox @@ -334,23 +351,23 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase // Make sure Lightbox redirects to email view $this->findCss($page, '.modal #email_to'); // Close lightbox - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); $this->snooze(); // Click email - $this->findCss($page, '.mail-record')->click(); + $this->clickCss($page, '.mail-record'); $this->snooze(); $this->findCss($page, '.modal #email_to'); // Send text to false email $this->findCss($page, '.modal #email_to')->setValue('asdf@vufind.org'); $this->findCss($page, '.modal #email_from')->setValue('asdf@vufind.org'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message and close lightbox $this->findCss($page, '.modal .alert-success'); - $this->findCss($page, '.modal .close')->click(); + $this->clickCss($page, '.modal .close'); $this->snooze(); // Logout - $this->findCss($page, '.logoutOptions a.logout')->click(); + $this->clickCss($page, '.logoutOptions a.logout'); } /** @@ -372,44 +389,64 @@ class RecordActionsTest extends \VuFindTest\Unit\MinkTestCase // Go to a record view $page = $this->gotoRecord(); // Click SMS - $this->findCss($page, '.sms-record')->click(); + $this->clickCss($page, '.sms-record'); // Type invalid phone numbers // - too empty $this->findCss($page, '.modal #sms_to')->setValue(''); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCss($page, '.modal .sms-error'); // - too short $this->findCss($page, '.modal #sms_to')->setValue('123'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCss($page, '.modal .sms-error'); // - too long $this->findCss($page, '.modal #sms_to')->setValue('12345678912345678912345679'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCss($page, '.modal .sms-error'); // - too lettery $this->findCss($page, '.modal #sms_to')->setValue('123abc'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->findCss($page, '.modal .sms-error'); // - just right $this->findCss($page, '.modal #sms_to')->setValue('8005555555'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // wait for form submission to catch missing carrier $this->assertNull($page->find('css', '.modal .sms-error')); // - pretty just right $this->findCss($page, '.modal #sms_to')->setValue('(800) 555-5555'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // wait for form submission to catch missing carrier $this->assertNull($page->find('css', '.modal .sms-error')); // Send text to false number $this->findCss($page, '.modal #sms_to')->setValue('(800) 555-5555'); $optionElement = $this->findCss($page, '.modal #sms_provider option'); $page->selectFieldOption('sms_provider', 'verizon'); - $this->findCss($page, '.modal-body .btn.btn-primary')->click(); + $this->clickCss($page, '.modal-body .btn.btn-primary'); $this->snooze(); // Check for confirmation message $this->findCss($page, '.modal .alert-success'); } + /** + * Retry cleanup method in case of failure during testAddTag. + * + * @return void + */ + protected function removeUsername2() + { + static::removeUsers(['username2']); + } + + /** + * Retry cleanup method in case of failure during testEmail. + * + * @return void + */ + protected function removeEmailManiac() + { + static::removeUsers(['emailmaniac']); + } + /** * Standard teardown method. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php index 7d60daae4f510103b03a410986c38f0e440e85ff..69d42a478dbf5b466ccf63e4a6e9ae909715448f 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/RecordTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class RecordTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Test record tabs for a particular ID. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php index a4056d8d5ef66cde67904d77e3b6ba7fab3eb9d9..f63e4ddca411e231b4c14b1962c47dc29f1fdb36 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SavedSearchesTest.php @@ -35,9 +35,11 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; use \VuFindTest\Unit\UserCreationTrait; /** @@ -66,17 +68,19 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase /** * Test saving and clearing a search. * + * @retryCallback tearDownAfterClass + * * @return void */ public function testSaveSearch() { $page = $this->performSearch('test'); - $this->findCss($page, '.fa.fa-save')->click(); + $this->clickCss($page, '.fa.fa-save'); $this->snooze(); - $this->findCss($page, '.createAccountLink')->click(); + $this->clickCss($page, '.createAccountLink'); $this->snooze(); $this->fillInAccountForm($page); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); $this->assertEquals( 'Search saved successfully.', @@ -110,7 +114,7 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase // Now log in and see if our saved search shows up (without making the // unsaved search go away): - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); @@ -137,6 +141,8 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase /** * Test that user A cannot delete user B's favorites. * + * @retryCallback removeUsername2 + * * @return void */ public function testSavedSearchSecurity() @@ -145,7 +151,7 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase $session = $this->getMinkSession(); $session->visit($this->getVuFindUrl() . '/Search/History'); $page = $session->getPage(); - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); @@ -157,12 +163,12 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase list($base, $params) = explode('?', $delete); $session->visit($this->getVuFindUrl() . '/MyResearch/SaveSearch?' . $params); $page = $session->getPage(); - $this->findCss($page, '.createAccountLink')->click(); + $this->clickCss($page, '.createAccountLink'); $this->snooze(); $this->fillInAccountForm( $page, ['username' => 'username2', 'email' => 'username2@example.com'] ); - $this->findCss($page, 'input.btn.btn-primary')->click(); + $this->clickCss($page, 'input.btn.btn-primary'); $this->snooze(); $this->findAndAssertLink($page, 'Log Out')->click(); $this->snooze(); @@ -170,7 +176,7 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase // Go back in as user A -- see if the saved search still exists. $this->findAndAssertLink($page, 'Search History')->click(); $this->snooze(); - $this->findCss($page, '#loginOptions a')->click(); + $this->clickCss($page, '#loginOptions a'); $this->snooze(); $this->fillInLoginForm($page, 'username1', 'test'); $this->submitLoginForm($page); @@ -182,6 +188,76 @@ class SavedSearchesTest extends \VuFindTest\Unit\MinkTestCase ); } + /** + * Test that notification settings work correctly. + * + * @return void + */ + public function testNotificationSettings() + { + // Add a search to history... + $page = $this->performSearch('journal'); + + // Now log in and go to search history... + $this->clickCss($page, '#loginOptions a'); + $this->snooze(); + $this->fillInLoginForm($page, 'username1', 'test'); + $this->submitLoginForm($page); + $this->findAndAssertLink($page, 'Search History')->click(); + $this->snooze(); + + // By default, there should be no alert option at all: + $scheduleSelector = 'select[name="schedule"]'; + $this->assertNull($page->find('css', $scheduleSelector)); + + // Now reconfigure to allow alerts, and refresh the page: + $this->changeConfigs( + [ + 'config' => ['Account' => ['schedule_searches' => true]] + ] + ); + $session = $this->getMinkSession(); + $session->reload(); + $this->snooze(); + $page = $session->getPage(); + + // Now there should be two alert options visible (one in saved, one in + // unsaved): + $this->assertEquals(2, count($page->findAll('css', $scheduleSelector))); + $this->assertEquals( + 1, count($page->findAll('css', '#recent-searches ' . $scheduleSelector)) + ); + $this->assertEquals( + 1, count($page->findAll('css', '#saved-searches ' . $scheduleSelector)) + ); + + // At this point, our journals search should be in the unsaved list; let's + // set it up for alerts and confirm that this auto-saves it. + $select = $this->findCss($page, '#recent-searches ' . $scheduleSelector); + $select->selectOption(7); + $this->snooze(); + $this->assertEquals( + 2, count($page->findAll('css', '#saved-searches ' . $scheduleSelector)) + ); + + // Now let's delete the saved search and confirm that this clears the + // alert subscription. + $this->findAndAssertLink($page, 'Delete')->click(); + $this->snooze(); + $select = $this->findCss($page, '#recent-searches ' . $scheduleSelector); + $this->assertEquals(0, $select->getValue()); + } + + /** + * Retry cleanup method in case of failure during testSavedSearchSecurity. + * + * @return void + */ + protected function removeUsername2() + { + static::removeUsers(['username2']); + } + /** * Standard teardown method. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php index a07d27613063f39765334dc22327c4a24b92d4fe..e469283c2cc858aa2416bec93ca6eab93c11dfb6 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php @@ -35,9 +35,19 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + + /** + * CSS selector for finding active filters + * + * @var string + */ + protected $activeFilterSelector = '.active-filters.hidden-xs .filters .filter-value'; + /** * Standard setup method. * @@ -63,6 +73,36 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase return $session->getPage(); } + /** + * Helper function for simple facet application test + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function facetApplyProcedure($page) + { + // Confirm that we have 9 results and no filters to begin with: + $time = $this->findCss($page, '.search-query-time'); + $stats = $this->findCss($page, '.search-stats'); + $this->assertEquals("Showing 1 - 9 results of 9 for search 'building:weird_ids.mrc'" . $time->getText(), $stats->getText()); + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(0, count($items)); + + // Facet to Fiction (after making sure we picked the right link): + $facetList = $this->findCss($page, '#side-collapse-genre_facet a[data-title="Fiction"]'); + $this->assertEquals('Fiction 7', $facetList->getText()); + $facetList->click(); + $this->snooze(); + + // Check that when the page reloads, we have fewer results and a filter: + $time = $this->findCss($page, '.search-query-time'); + $stats = $this->findCss($page, '.search-stats'); + $this->assertEquals("Showing 1 - 7 results of 7 for search 'building:weird_ids.mrc'" . $time->getText(), $stats->getText()); + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(1, count($items)); + } + /** * Helper function for facets lists * @@ -81,7 +121,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ->findAll('css', '#modal #facet-list-count .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // more - $this->findCss($page, '#modal .js-facet-next-page')->click(); + $this->clickCss($page, '#modal .js-facet-next-page'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); @@ -103,7 +143,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals($exclusionActive ? $limit * 2 : 0, count($excludes)); // sort by title - $this->findCss($page, '[data-sort="index"]')->click(); + $this->clickCss($page, '[data-sort="index"]'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-index .js-facet-item'); $this->assertEquals($limit, count($items)); // reset number of items @@ -119,7 +159,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ->findAll('css', '#modal #facet-list-index .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // sort by index again - $this->findCss($page, '[data-sort="count"]')->click(); + $this->clickCss($page, '[data-sort="count"]'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); // maintain number of items @@ -134,6 +174,49 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $this->snooze(); } + /** + * Test applying a facet to filter results (standard facet sidebar) + * + * @return void + */ + public function testApplyFacet() + { + $page = $this->performSearch('building:weird_ids.mrc'); + + // Confirm that we are NOT using the AJAX sidebar: + $ajaxContainer = $page->findAll('css', '.side-facets-container-ajax'); + $this->assertEquals(0, count($ajaxContainer)); + + // Now run the body of the test procedure: + $this->facetApplyProcedure($page); + } + + /** + * Test applying a facet to filter results (deferred facet sidebar) + * + * @return void + */ + public function testApplyFacetDeferred() + { + $this->changeConfigs( + [ + 'searches' => [ + 'General' => [ + 'default_side_recommend[]' => 'SideFacetsDeferred:Results:CheckboxFacets', + ] + ] + ] + ); + $page = $this->performSearch('building:weird_ids.mrc'); + + // Confirm that we ARE using the AJAX sidebar: + $ajaxContainer = $page->findAll('css', '.side-facets-container-ajax'); + $this->assertEquals(1, count($ajaxContainer)); + + // Now run the body of the test procedure: + $this->facetApplyProcedure($page); + } + /** * Test expanding facets into the lightbox * @@ -158,10 +241,10 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $genreMore->click(); $this->facetListProcedure($page, $limit); $genreMore->click(); - $this->findCss($page, '#modal .js-facet-item.active')->click(); + $this->clickCss($page, '#modal .js-facet-item.active'); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.active-filters')); + $this->assertNull($page->find('css', $this->activeFilterSelector)); } /** @@ -186,14 +269,14 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase // Open the geographic facet $genreMore = $this->findCss($page, '#more-narrowGroupHidden-genre_facet'); $genreMore->click(); - $this->findCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]')->click(); + $this->clickCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]'); $this->facetListProcedure($page, $limit); $genreMore->click(); - $this->findCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]')->click(); - $this->findCss($page, '#modal .js-facet-item.active')->click(); + $this->clickCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]'); + $this->clickCss($page, '#modal .js-facet-item.active'); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.active-filters')); + $this->assertNull($page->find('css', $this->activeFilterSelector)); } /** @@ -220,7 +303,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $genreMore = $this->findCss($page, '#more-narrowGroupHidden-genre_facet'); $genreMore->click(); $this->facetListProcedure($page, $limit, true); - $this->assertEquals(1, count($page->findAll('css', '.active-filters'))); + $this->assertEquals(1, count($page->findAll('css', $this->activeFilterSelector))); } /** @@ -236,10 +319,12 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $session = $this->getMinkSession(); $session->executeScript("$('#j1_1.jstree-closed .jstree-icon').click();"); $this->findCss($page, '#j1_1.jstree-open .jstree-icon'); - $this->findCss($page, '#j1_2 a')->click(); + $this->clickCss($page, '#j1_2 a'); $this->snooze(); - $filter = $this->findCss($page, '.active-filters .facet'); - $this->assertEquals('Clear Filter hierarchy: 1/level1a/level2a/', $filter->getText()); + $filter = $this->findCss($page, $this->activeFilterSelector); + $label = $this->findCss($page, '.filters .filters-title'); + $this->assertEquals('hierarchy:', $label->getText()); + $this->assertEquals('1/level1a/level2a/', $filter->getText()); $this->findCss($page, '#j1_2 .fa-check'); } @@ -290,43 +375,165 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ); $page = $this->performSearch('building:"hierarchy.mrc"'); // Uncollapse format so we can check if it is still open after reload: - $this->findCss($page, '#side-panel-format .collapsed')->click(); + $this->clickCss($page, '#side-panel-format .collapsed'); // Uncollapse hierarchical facet so we can click it: - $this->findCss($page, '#side-panel-hierarchical_facet_str_mv .collapsed')->click(); + $this->clickCss($page, '#side-panel-hierarchical_facet_str_mv .collapsed'); $this->clickHierarchyFacet($page); // We have now reloaded the page. Let's toggle format off and on to confirm // that it was opened, and let's also toggle building on to confirm that // it was not alread opened. - $this->findCss($page, '#side-panel-format .title')->click(); // off + $this->clickCss($page, '#side-panel-format .title'); // off $this->snooze(); // wait for animation - $this->findCss($page, '#side-panel-format .collapsed')->click(); // on - $this->findCss($page, '#side-panel-building .collapsed')->click(); // on + $this->clickCss($page, '#side-panel-format .collapsed'); // on + $this->clickCss($page, '#side-panel-building .collapsed'); // on + } + + /** + * Assert that the filter used by these tests is still applied. + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function assertFilterIsStillThere($page) + { + $filter = $this->findCss($page, $this->activeFilterSelector); + $this->assertEquals('weird_ids.mrc', $filter->getText()); + } + + /** + * Assert that no filters are applied. + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function assertNoFilters($page) + { + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(0, count($items)); } /** - * Test retrain current filters checkbox + * Assert that the "reset filters" button is not present. + * + * @param \Behat\Mink\Element\Element $page Mink page object * * @return void */ - public function testRetainFilters() + protected function assertNoResetFiltersButton($page) + { + $reset = $page->findAll('css', '.reset-filters-btn'); + $this->assertEquals(0, count($reset)); + } + + /** + * Test retain current filters default behavior + * + * @return void + */ + public function testDefaultRetainFiltersBehavior() { $page = $this->getFilteredSearch(); - $this->findCss($page, '.active-filters'); // Make sure we're filtered - // Perform search with retain - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); + $this->assertFilterIsStillThere($page); + // Re-click the search button and confirm that filters are still there + $this->clickCss($page, '#searchForm .btn.btn-primary'); $this->snooze(); - $this->findCss($page, '.active-filters'); - // Perform search double click retain - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); + $this->assertFilterIsStillThere($page); + // Click the "reset filters" button and confirm that filters are gone and + // that the button disappears when no longer needed. + $this->clickCss($page, '.reset-filters-btn'); $this->snooze(); - $this->findCss($page, '.active-filters'); - // Perform search without retain - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); - $items = $page->findAll('css', '.active-filters'); - $this->assertEquals(0, count($items)); + $this->assertNoFilters($page); + $this->assertNoResetFiltersButton($page); + } + + /** + * Test that filters carry over to selected records and are retained + * from there. + * + * @return void + */ + public function testFiltersOnRecord() + { + $page = $this->getFilteredSearch(); + $this->assertFilterIsStillThere($page); + // Now click the first result: + $this->clickCss($page, '.result-body a.title'); + $this->snooze(); + // Confirm that filters are still visible: + $this->assertFilterIsStillThere($page); + // Re-click the search button... + $this->clickCss($page, '#searchForm .btn.btn-primary'); + $this->snooze(); + // Confirm that filter is STILL applied + $this->assertFilterIsStillThere($page); + } + + /** + * Test "never retain filters" configurable behavior + * + * @return void + */ + public function testNeverRetainFiltersBehavior() + { + $this->changeConfigs( + [ + 'searches' => [ + 'General' => ['retain_filters_by_default' => false] + ] + ] + ); + $page = $this->getFilteredSearch(); + $this->assertFilterIsStillThere($page); + // Confirm that there is no reset button: + $this->assertNoResetFiltersButton($page); + // Re-click the search button and confirm that filters go away + $this->clickCss($page, '#searchForm .btn.btn-primary'); + $this->snooze(); + $this->assertNoFilters($page); + } + + /** + * Test resetting to a default filter state + * + * @return void + */ + public function testDefaultFiltersWithResetButton() + { + // Unlike the other tests, which use $this->getFilteredSearch() to set up + // the weird_ids.mrc filter through a URL parameter, this test sets up the + // filter as a default through the configuration. + $this->changeConfigs( + [ + 'searches' => [ + 'General' => ['default_filters' => ['building:weird_ids.mrc']] + ] + ] + ); + + // Do a blank search to confirm that default filter is applied: + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl() . '/Search/Results'); + $page = $session->getPage(); + $this->snooze(); + $this->assertFilterIsStillThere($page); + + // Confirm that the reset button is NOT present: + $this->assertNoResetFiltersButton($page); + + // Now manually clear the filter: + $this->clickCss($page, '.search-filter-remove'); + $this->snooze(); + + // Confirm that no filters are displayed: + $this->assertNoFilters($page); + + // Now click the reset button to bring back the default: + $this->clickCss($page, '.reset-filters-btn'); + $this->snooze(); + $this->assertFilterIsStillThere($page); + $this->assertNoResetFiltersButton($page); } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php index 5e0024f5bca9cd21efa9bc163aa135585e56545b..8861230498164d456178518a983cfbb13672b792 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/VisualizationTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Demian Katz <demian.katz@villanova.edu> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class VisualizationTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * Test that combined results work in mixed AJAX/non-AJAX mode. * diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php index 3a22733b1151da229cfc8b6c825741226c5914c4..f5646150fda616ec31eafd54f04e69fb1e6aa139 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/View/Helper/Root/ResultFeedTest.php @@ -60,15 +60,14 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getPlugins() { - $currentPath = $this->createMock('VuFind\View\Helper\Root\CurrentPath'); + $currentPath = $this->createMock(\VuFind\View\Helper\Root\CurrentPath::class); $currentPath->expects($this->any())->method('__invoke') ->will($this->returnValue('/test/path')); - $recordLink = $this->getMockBuilder('VuFind\View\Helper\Root\RecordLink') + $recordLink = $this->getMockBuilder(\VuFind\View\Helper\Root\RecordLink::class) ->setConstructorArgs( [ new \VuFind\Record\Router( - $this->getServiceManager()->get('VuFind\Record\Loader'), new \Zend\Config\Config([]) ) ] @@ -76,7 +75,7 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase $recordLink->expects($this->any())->method('getUrl') ->will($this->returnValue('test/url')); - $serverUrl = $this->createMock('Zend\View\Helper\ServerUrl'); + $serverUrl = $this->createMock(\Zend\View\Helper\ServerUrl::class); $serverUrl->expects($this->any())->method('__invoke') ->will($this->returnValue('http://server/url')); @@ -94,7 +93,7 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getMockTranslator() { - $mock = $this->getMockBuilder('Zend\I18n\Translator\TranslatorInterface') + $mock = $this->getMockBuilder(\Zend\I18n\Translator\TranslatorInterface::class) ->getMock(); $mock->expects($this->at(1))->method('translate') ->with($this->equalTo('showing_results_of_html'), $this->equalTo('default')) @@ -119,7 +118,7 @@ class ResultFeedTest extends \VuFindTest\Unit\ViewHelperTestCase $request->set('view', 'rss'); $results = $this->getServiceManager() - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get('Solr'); $results->getParams()->initFromRequest($request); $helper = new ResultFeed(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php index 38bc7407e14f616ff7fd10f510a45f1f5cc0df62..b38066da576f5fa04cc70dae2f493309e1998519 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ChoiceAuthTest.php @@ -211,7 +211,7 @@ class ChoiceAuthTest extends \VuFindTest\Unit\TestCase */ protected function getSessionContainer($method = null) { - $mock = $this->getMockBuilder('Zend\Session\Container') + $mock = $this->getMockBuilder(\Zend\Session\Container::class) ->setMethods(['__get', '__isset', '__set', '__unset']) ->disableOriginalConstructor()->getMock(); if ($method) { @@ -248,10 +248,10 @@ class ChoiceAuthTest extends \VuFindTest\Unit\TestCase protected function getMockPluginManager() { $pm = new PluginManager($this->getServiceManager()); - $mockDb = $this->getMockBuilder('VuFind\Auth\Database') + $mockDb = $this->getMockBuilder(\VuFind\Auth\Database::class) ->disableOriginalConstructor() ->getMock(); - $mockShib = $this->getMockBuilder('VuFind\Auth\Shibboleth') + $mockShib = $this->getMockBuilder(\VuFind\Auth\Shibboleth::class) ->disableOriginalConstructor() ->getMock(); $pm->setService('VuFind\Auth\Database', $mockDb); @@ -266,7 +266,7 @@ class ChoiceAuthTest extends \VuFindTest\Unit\TestCase */ protected function getMockUser() { - return $this->getMockBuilder('VuFind\Db\Row\User') + return $this->getMockBuilder(\VuFind\Db\Row\User::class) ->disableOriginalConstructor() ->getMock(); } @@ -278,7 +278,7 @@ class ChoiceAuthTest extends \VuFindTest\Unit\TestCase */ protected function getMockRequest() { - return $this->getMockBuilder('Zend\Http\PhpEnvironment\Request') + return $this->getMockBuilder(\Zend\Http\PhpEnvironment\Request::class) ->disableOriginalConstructor() ->getMock(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php index 3f068b4aaaeab22029c9ae053313ea93c2c307a8..e2d2b85d3f33782e08f2804d3c2e12c209107741 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/DatabaseUnitTest.php @@ -195,7 +195,7 @@ class DatabaseUnitTest extends \VuFindTest\Unit\DbTestCase */ protected function getMockRow() { - return $this->getMockBuilder('VuFind\Db\Row\User') + return $this->getMockBuilder(\VuFind\Db\Row\User::class) ->disableOriginalConstructor() ->getMock(); } @@ -210,7 +210,7 @@ class DatabaseUnitTest extends \VuFindTest\Unit\DbTestCase protected function getMockTable($methods = []) { $methods[] = 'getResultSetPrototype'; - $mock = $this->getMockBuilder('VuFind\Db\Table\User') + $mock = $this->getMockBuilder(\VuFind\Db\Table\User::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); @@ -235,7 +235,7 @@ class DatabaseUnitTest extends \VuFindTest\Unit\DbTestCase protected function getRequest($post = []) { $post = new Parameters($post); - $request = $this->getMockBuilder('Zend\Http\PhpEnvironment\Request') + $request = $this->getMockBuilder(\Zend\Http\PhpEnvironment\Request::class) ->setMethods(['getPost'])->getMock(); $request->expects($this->any())->method('getPost') ->will($this->returnValue($post)); @@ -251,7 +251,7 @@ class DatabaseUnitTest extends \VuFindTest\Unit\DbTestCase */ protected function getDatabase($table) { - $tableManager = $this->getMockBuilder('VuFind\Db\Table\PluginManager') + $tableManager = $this->getMockBuilder(\VuFind\Db\Table\PluginManager::class) ->disableOriginalConstructor()->setMethods(['get'])->getMock(); $tableManager->expects($this->once())->method('get') ->with($this->equalTo('User')) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php index ff702404ca47a9c6e7091003a1cb0357974a7f95..77ea52b3e29025f6b9152bd36b2972139b3d5dc7 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ILSAuthenticatorTest.php @@ -202,7 +202,7 @@ class ILSAuthenticatorTest extends \VuFindTest\Unit\TestCase */ protected function getMockUser($methods = null) { - return $this->getMockBuilder('VuFind\Db\Row\User') + return $this->getMockBuilder(\VuFind\Db\Row\User::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); @@ -217,7 +217,7 @@ class ILSAuthenticatorTest extends \VuFindTest\Unit\TestCase */ protected function getMockManager($methods = null) { - return $this->getMockBuilder('VuFind\Auth\Manager') + return $this->getMockBuilder(\VuFind\Auth\Manager::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); @@ -232,7 +232,7 @@ class ILSAuthenticatorTest extends \VuFindTest\Unit\TestCase */ protected function getMockConnection($methods = null) { - return $this->getMockBuilder('VuFind\ILS\Connection') + return $this->getMockBuilder(\VuFind\ILS\Connection::class) ->disableOriginalConstructor() ->setMethods($methods) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php index a27c81cdde738ca7ce5cc7896ee4ef68a1d627a7..ec66ea344099f020db77edbbc0cd6f08431175cb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/ManagerTest.php @@ -86,7 +86,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase // Advanced case -- ChoiceAuth's getSelectableAuthOptions returns false. $pm = $this->getMockPluginManager(); - $mockChoice = $this->getMockBuilder('VuFind\Auth\ChoiceAuth') + $mockChoice = $this->getMockBuilder(\VuFind\Auth\ChoiceAuth::class) ->disableOriginalConstructor() ->getMock(); $mockChoice->expects($this->any())->method('getSelectableAuthOptions')->will($this->returnValue(false)); @@ -249,6 +249,24 @@ class ManagerTest extends \VuFindTest\Unit\TestCase $this->assertTrue($this->getManager($config, null, null, $pm)->supportsRecovery()); } + /** + * Test supportsEmailChange + * + * @return void + */ + public function testSupportsEmailChange() + { + // Most common case -- no: + $this->assertFalse($this->getManager()->supportsEmailChange()); + + // Less common case -- yes: + $pm = $this->getMockPluginManager(); + $config = ['Authentication' => ['change_email' => true]]; + $this->assertTrue($this->getManager($config, null, null, $pm)->supportsEmailChange()); + $config = ['Authentication' => ['change_email' => false]]; + $this->assertFalse($this->getManager($config, null, null, $pm)->supportsEmailChange()); + } + /** * Test supportsPasswordChange * @@ -262,9 +280,11 @@ class ManagerTest extends \VuFindTest\Unit\TestCase // Less common case -- yes: $pm = $this->getMockPluginManager(); $db = $pm->get('Database'); - $db->expects($this->once())->method('supportsPasswordChange')->will($this->returnValue(true)); + $db->expects($this->any())->method('supportsPasswordChange')->will($this->returnValue(true)); $config = ['Authentication' => ['change_password' => true]]; $this->assertTrue($this->getManager($config, null, null, $pm)->supportsPasswordChange()); + $config = ['Authentication' => ['change_password' => false]]; + $this->assertFalse($this->getManager($config, null, null, $pm)->supportsPasswordChange()); } /** @@ -481,7 +501,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase $manager = $this->getManager([], $table); // Fake the session inside the manager: - $mockSession = $this->getMockBuilder('Zend\Session\Container') + $mockSession = $this->getMockBuilder(\Zend\Session\Container::class) ->setMethods(['__get', '__isset', '__set', '__unset']) ->disableOriginalConstructor()->getMock(); $mockSession->expects($this->any())->method('__isset')->with($this->equalTo('userId'))->will($this->returnValue(true)); @@ -532,7 +552,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase */ protected function getMockUserTable() { - return $this->getMockBuilder('VuFind\Db\Table\User') + return $this->getMockBuilder(\VuFind\Db\Table\User::class) ->disableOriginalConstructor() ->getMock(); } @@ -544,7 +564,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase */ protected function getMockSessionManager() { - return $this->getMockBuilder('Zend\Session\SessionManager') + return $this->getMockBuilder(\Zend\Session\SessionManager::class) ->disableOriginalConstructor() ->getMock(); } @@ -557,17 +577,19 @@ class ManagerTest extends \VuFindTest\Unit\TestCase protected function getMockPluginManager() { $pm = new PluginManager($this->getServiceManager()); - $mockChoice = $this->getMockBuilder('VuFind\Auth\ChoiceAuth') + $mockChoice = $this->getMockBuilder(\VuFind\Auth\ChoiceAuth::class) ->disableOriginalConstructor() ->getMock(); $mockChoice->expects($this->any())->method('getSelectableAuthOptions')->will($this->returnValue(['Database', 'Shibboleth'])); - $mockDb = $this->getMockBuilder('VuFind\Auth\Database') + $mockDb = $this->getMockBuilder(\VuFind\Auth\Database::class) ->disableOriginalConstructor() ->getMock(); - $mockMulti = $this->getMockBuilder('VuFind\Auth\MultiILS') + $mockDb->expects($this->any())->method('needsCsrfCheck') + ->will($this->returnValue(true)); + $mockMulti = $this->getMockBuilder(\VuFind\Auth\MultiILS::class) ->disableOriginalConstructor() ->getMock(); - $mockShib = $this->getMockBuilder('VuFind\Auth\Shibboleth') + $mockShib = $this->getMockBuilder(\VuFind\Auth\Shibboleth::class) ->disableOriginalConstructor() ->getMock(); $pm->setService('VuFind\Auth\ChoiceAuth', $mockChoice); @@ -584,7 +606,7 @@ class ManagerTest extends \VuFindTest\Unit\TestCase */ protected function getMockUser() { - return $this->getMockBuilder('VuFind\Db\Row\User') + return $this->getMockBuilder(\VuFind\Db\Row\User::class) ->disableOriginalConstructor() ->getMock(); } @@ -596,12 +618,15 @@ class ManagerTest extends \VuFindTest\Unit\TestCase */ protected function getMockRequest() { - $mock = $this->getMockBuilder('Zend\Http\PhpEnvironment\Request') + $mock = $this->getMockBuilder(\Zend\Http\PhpEnvironment\Request::class) ->disableOriginalConstructor() ->getMock(); $post = new \Zend\Stdlib\Parameters(); $mock->expects($this->any())->method('getPost') ->will($this->returnValue($post)); + $get = new \Zend\Stdlib\Parameters(); + $mock->expects($this->any())->method('getQuery') + ->will($this->returnValue($get)); return $mock; } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php index bf05faf91a833320e986af600ffaa48d030ba482..e82670cbf903da9d8e9a0417de7f26bbc0a181ef 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Auth/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php index 7fc9a56232843a63e0bb9e703c72e00700adf145..8a4d5ed2c0ae9434dbad83208a3372100eb2939b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/EdsTest.php @@ -51,7 +51,7 @@ class EdsTest extends \VuFindTest\Unit\TestCase */ protected function getMockBackend($id = 'EDS') { - return $this->getMockBuilder('VuFindSearch\Backend\EDS\Backend') + return $this->getMockBuilder(\VuFindSearch\Backend\EDS\Backend::class) ->setMethods(['autocomplete']) ->disableOriginalConstructor()->getMock(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php index fd435c9047e3a50368acc365b1a2732c01c2c7be..24c3016e8fef315a4e5888d6cf9ab2c5839ea74f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php index c1e1e54721b92841288b2433c00a752e8dc37c27..935f3b6e1b7cc101ece452988b3f78b8435b49bf 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/SolrTest.php @@ -47,7 +47,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ protected function getMockOptions() { - return $this->getMockBuilder('VuFind\Search\Solr\Options') + return $this->getMockBuilder(\VuFind\Search\Solr\Options::class) ->disableOriginalConstructor()->getMock(); } @@ -58,7 +58,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ protected function getMockResults() { - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor() ->setMethods(['getOptions']) ->getMock(); @@ -74,7 +74,7 @@ class SolrTest extends \VuFindTest\Unit\TestCase */ protected function getMockResultsPluginManager() { - $rpm = $this->getMockBuilder('VuFind\Search\Results\PluginManager') + $rpm = $this->getMockBuilder(\VuFind\Search\Results\PluginManager::class) ->disableOriginalConstructor() ->setMethods(['get']) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php index 1b0080a408d0bdf4537ad213bfdfa07601050f6a..00dcc4af575be984645a96ec01e824c098d3dfd8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Autocomplete/TagTest.php @@ -68,12 +68,12 @@ class TagTest extends \VuFindTest\Unit\DbTestCase ]; // Fake services: - $tagTable = $this->getMockBuilder('VuFind\Db\Table\Tags') + $tagTable = $this->getMockBuilder(\VuFind\Db\Table\Tags::class) ->disableOriginalConstructor()->setMethods(['matchText'])->getMock(); $tagTable->expects($this->once())->method('matchText') ->with($this->equalTo('foo')) ->will($this->returnValue($tags)); - $tableManager = $this->getMockBuilder('VuFind\Db\Table\PluginManager') + $tableManager = $this->getMockBuilder(\VuFind\Db\Table\PluginManager::class) ->disableOriginalConstructor()->setMethods(['get'])->getMock(); $tableManager->expects($this->once())->method('get') ->with($this->equalTo('Tags')) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php index c5f3c76b752eeacfd9bb5bf27593e7a5f5755e74..756a517751c1b744d9f4575bf51cff8d10ba00df 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/CartTest.php @@ -54,11 +54,11 @@ class CartTest extends \PHPUnit\Framework\TestCase */ public function setUp() { - $this->loader = $this->getMockBuilder('VuFind\Record\Loader') + $this->loader = $this->getMockBuilder(\VuFind\Record\Loader::class) ->setMethods([]) ->setConstructorArgs([ - $this->createMock('VuFindSearch\Service'), - $this->createMock('VuFind\RecordDriver\PluginManager') + $this->createMock(\VuFindSearch\Service::class), + $this->createMock(\VuFind\RecordDriver\PluginManager::class) ])->getMock(); } @@ -75,7 +75,7 @@ class CartTest extends \PHPUnit\Framework\TestCase protected function getMockCookieManager($cookies = [], $path = '/', $domain = null, $secure = false, $httpOnly = false ) { - return $this->getMockBuilder('VuFind\Cookie\CookieManager') + return $this->getMockBuilder(\VuFind\Cookie\CookieManager::class) ->setMethods(['set']) ->setConstructorArgs([$cookies, $path, $domain, $secure, $httpOnly]) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php index 277f8fb3e4ce54b528513a7afe6a3f9a3f161b00..0c5250109b3ef9a1869c12cae98634e37ec2566a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/PluginFactoryTest.php @@ -135,7 +135,7 @@ class PluginFactoryTest extends \VuFindTest\Unit\TestCase protected function getConfig($name) { return $this->factory->__invoke( - $this->createMock('Interop\Container\ContainerInterface'), $name + $this->createMock(\Interop\Container\ContainerInterface::class), $name ); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php index d970a9ef65c1f557de1e0fd724d1b484e9819f1c..3ccda6821d87240746cf07a9633d7702fc219a32 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/Reader/CacheDecoratorTest.php @@ -48,13 +48,13 @@ class CacheDecoratorTest extends \PHPUnit\Framework\TestCase */ public function testFromFileAndString() { - $cache = $this->getMockForAbstractClass('Zend\Cache\Storage\StorageInterface', ['setItem', 'hasItem']); + $cache = $this->getMockForAbstractClass('Zend\Cache\Storage\StorageInterface'); $cache->expects($this->exactly(2)) ->method('setItem'); $cache->expects($this->exactly(2)) ->method('hasItem') ->will($this->returnValue(false)); - $reader = $this->getMockForAbstractClass('Zend\Config\Reader\ReaderInterface', ['fromFile', 'fromString']); + $reader = $this->getMockForAbstractClass('Zend\Config\Reader\ReaderInterface'); $reader->expects($this->once()) ->method('fromFile') ->will($this->returnValue([])); @@ -73,7 +73,7 @@ class CacheDecoratorTest extends \PHPUnit\Framework\TestCase */ public function testFromFileAndStringCached() { - $cache = $this->getMockForAbstractClass('Zend\Cache\Storage\StorageInterface', ['setItem', 'hasItem', 'getItem']); + $cache = $this->getMockForAbstractClass('Zend\Cache\Storage\StorageInterface'); $cache->expects($this->never()) ->method('setItem'); $cache->expects($this->exactly(2)) @@ -82,7 +82,7 @@ class CacheDecoratorTest extends \PHPUnit\Framework\TestCase $cache->expects($this->exactly(2)) ->method('getItem') ->will($this->returnValue([])); - $reader = $this->getMockForAbstractClass('Zend\Config\Reader\ReaderInterface', ['fromFile', 'fromString']); + $reader = $this->getMockForAbstractClass('Zend\Config\Reader\ReaderInterface'); $deco = new CacheDecorator($reader, $cache); $deco->fromFile('ignore'); $deco->fromString('ignore'); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php index dcb6988f85dbd181f31a213ab495892d252d22fb..1571d1990d880d58683ce58ee812a33647dba679 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Config/SearchSpecsReaderTest.php @@ -94,7 +94,7 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase { // The searchspecs.yaml file should define author dismax fields (among many // other things). - $reader = $this->getServiceManager()->get('VuFind\Config\SearchSpecsReader'); + $reader = $this->getServiceManager()->get(\VuFind\Config\SearchSpecsReader::class); $specs = $reader->get('searchspecs.yaml'); $this->assertTrue( isset($specs['Author']['DismaxFields']) @@ -109,7 +109,7 @@ class SearchSpecsReaderTest extends \VuFindTest\Unit\TestCase */ public function testMissingFileRead() { - $reader = $this->getServiceManager()->get('VuFind\Config\SearchSpecsReader'); + $reader = $this->getServiceManager()->get(\VuFind\Config\SearchSpecsReader::class); $specs = $reader->get('notreallyasearchspecs.yaml'); $this->assertEquals([], $specs); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php index 1ec05109fdcc56c80e2685c1b725ce2995d866d8..42fcd571553f420cc2d8b47b2d035e81930136f2 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/AuthorNotes/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php index b077ebb050e9654d3580a8fd2edf3f72390a488c..1fa58849f1030619d36d907cc662ef99ead7c080 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Covers/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php index 92f73643e687a4f80b1b8a241c4b62274714e798..3472a32850a73a320e636bca90d0effdbf3fbcd9 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Excerpts/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php index 1d44017a85e7aafc4d0b0f37179141750aaad876..8d42bbe68dfa0da7bdf355b83735c5bc058c8e80 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php index 9784550a2237db3a86e7d58182d555e3536cc1eb..7bd4ea03659f0b4ec2bcacbfec4542caebcb3373 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Content/Reviews/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php index 7ee166d6df4a8b0c6627f11e43bfe2809aaa8e83..42183d039d520f9698c3b7f358668d6f36e6201d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/FollowupTest.php @@ -100,7 +100,7 @@ class FollowupTest extends TestCase */ protected function getMockController($url = 'http://localhost/default-url') { - $controller = $this->getMockBuilder('VuFind\Controller\AbstractBase') + $controller = $this->getMockBuilder(\VuFind\Controller\AbstractBase::class) ->disableOriginalConstructor()->getMock(); $controller->expects($this->any())->method('getServerUrl')->will($this->returnValue($url)); return $controller; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php index 215a23b2b7d97430d27c905492e940a01966e7e4..e37f439bb0cac43ded1f3ffce828132ddbe8decb 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/NewItemsTest.php @@ -50,7 +50,7 @@ class NewItemsTest extends TestCase */ public function testGetBibIDsFromCatalog() { - $flash = $this->createMock('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); + $flash = $this->createMock(\Zend\Mvc\Plugin\FlashMessenger\FlashMessenger::class); $config = new Config(['result_pages' => 10]); $newItems = new NewItems($config); $bibs = $newItems->getBibIDsFromCatalog( @@ -66,7 +66,7 @@ class NewItemsTest extends TestCase */ public function testGetBibIDsFromCatalogWithIDLimit() { - $flash = $this->createMock('Zend\Mvc\Plugin\FlashMessenger\FlashMessenger'); + $flash = $this->createMock(\Zend\Mvc\Plugin\FlashMessenger\FlashMessenger::class); $flash->expects($this->once())->method('addMessage') ->with($this->equalTo('too_many_new_items'), $this->equalTo('info')); $config = new Config(['result_pages' => 10]); @@ -92,7 +92,7 @@ class NewItemsTest extends TestCase ->with($this->equalTo('getFunds'))->will($this->returnValue(true)); $catalog->expects($this->once())->method('getFunds') ->will($this->returnValue(['a', 'b', 'c'])); - $controller = $this->getMockBuilder('VuFind\Controller\SearchController') + $controller = $this->getMockBuilder(\VuFind\Controller\SearchController::class) ->disableOriginalConstructor()->getMock(); $controller->expects($this->once())->method('getILS') ->will($this->returnValue($catalog)); @@ -235,7 +235,7 @@ class NewItemsTest extends TestCase */ protected function getMockParams($idLimit = 1024) { - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->once())->method('getLimit') ->will($this->returnValue(20)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php index ca66e6141c711db4b1084c71f80db7d626371972..6810ebaf9a659f7763b557bdf30d37657429eaba 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Controller/Plugin/ResultScrollerTest.php @@ -73,7 +73,7 @@ class ResultScrollerTest extends TestCase */ public function testDisabled() { - $mockManager = $this->getMockBuilder('VuFind\Search\Results\PluginManager') + $mockManager = $this->getMockBuilder(\VuFind\Search\Results\PluginManager::class) ->disableOriginalConstructor()->getMock(); $plugin = new ResultScroller(new Container('test'), $mockManager, false); $results = $this->getMockResults(); @@ -349,7 +349,7 @@ class ResultScrollerTest extends TestCase protected function getMockResults($page = 1, $limit = 20, $total = 0, $firstLast = true, $sort = null ) { - $pm = $this->getMockBuilder('VuFind\Config\PluginManager')->disableOriginalConstructor()->getMock(); + $pm = $this->getMockBuilder(\VuFind\Config\PluginManager::class)->disableOriginalConstructor()->getMock(); $config = new \Zend\Config\Config( $firstLast ? $this->getFirstLastConfig() : [] ); @@ -361,9 +361,9 @@ class ResultScrollerTest extends TestCase if (null !== $sort) { $params->setSort($sort, true); } - $ss = $this->getMockBuilder('VuFindSearch\Service') + $ss = $this->getMockBuilder(\VuFindSearch\Service::class) ->disableOriginalConstructor()->getMock(); - $rl = $this->getMockBuilder('VuFind\Record\Loader') + $rl = $this->getMockBuilder(\VuFind\Record\Loader::class) ->disableOriginalConstructor()->getMock(); $results = new \VuFindTest\Search\TestHarness\Results( $params, $ss, $rl, $total @@ -382,7 +382,7 @@ class ResultScrollerTest extends TestCase */ protected function getMockResultScroller($results) { - $mockManager = $this->getMockBuilder('VuFind\Search\Results\PluginManager') + $mockManager = $this->getMockBuilder(\VuFind\Search\Results\PluginManager::class) ->disableOriginalConstructor()->getMock(); return new ResultScrollerMock($mockManager, $results); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php index c93f7c84d03166ca670de56fa40bcb855b74b2e6..163f17835e36c395c7bdf24406ac21facddef1ee 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Cover/LoaderTest.php @@ -59,7 +59,7 @@ class LoaderTest extends \VuFindTest\Unit\TestCase */ public function testUtterFailure() { - $theme = $this->getMockBuilder('VuFindTheme\ThemeInfo') + $theme = $this->getMockBuilder(\VuFindTheme\ThemeInfo::class) ->setConstructorArgs(['foo', 'bar'])->getMock(); $theme->expects($this->once())->method('findContainingTheme')->with($this->equalTo(['images/noCover2.gif']))->will($this->returnValue(false)); $loader = $this->getLoader([], null, $theme); @@ -142,13 +142,13 @@ class LoaderTest extends \VuFindTest\Unit\TestCase { $config = new Config($config); if (null === $manager) { - $manager = $this->createMock('VuFind\Content\Covers\PluginManager'); + $manager = $this->createMock(\VuFind\Content\Covers\PluginManager::class); } if (null === $theme) { $theme = new ThemeInfo($this->getThemeDir(), $this->testTheme); } if (null === $httpService) { - $httpService = $this->getMockBuilder('VuFindHttp\HttpService')->getMock(); + $httpService = $this->getMockBuilder(\VuFindHttp\HttpService::class)->getMock(); } if ($mock) { return $this->getMockBuilder(__NAMESPACE__ . '\MockLoader') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php index dc5b4e12575be21f19d9c2ebfa5ee3aad3387f90..529a1781056b6dfd68ac2f70d875eb2300f5dd7f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Table/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/DoiLinker/UnpaywallTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/DoiLinker/UnpaywallTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f76d4f161ee3d65b86f1a8f0d0f34f4102e7131e --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/DoiLinker/UnpaywallTest.php @@ -0,0 +1,90 @@ +<?php +/** + * Unpaywall Test Class + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\DoiLinker; + +use VuFind\DoiLinker\Unpaywall; +use Zend\Http\Client\Adapter\Test as TestAdapter; +use Zend\Http\Response as HttpResponse; + +/** + * Unpaywall Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class UnpaywallTest extends \VuFindTest\Unit\TestCase +{ + /** + * Test configuration validation. + * + * @return void + * + * @expectedException Exception + * @expectedExceptionMessage Missing configuration for Unpaywall DOI linker: unpaywall_email + */ + public function testConfigValidation() + { + new Unpaywall(new \Zend\Config\Config([])); + } + + /** + * Test an API response. + * + * @return void + */ + public function testApiSuccess() + { + $adapter = new TestAdapter(); + $file = realpath( + __DIR__ . '/../../../../../tests/fixtures/unpaywall/goodresponse' + ); + $response = file_get_contents($file); + $responseObj = HttpResponse::fromString($response); + $adapter->setResponse($responseObj); + $service = new \VuFindHttp\HttpService(); + $service->setDefaultAdapter($adapter); + $config = [ + 'unpaywall_email' => 'foo@myuniversity.edu', + ]; + $unpaywall = new Unpaywall(new \Zend\Config\Config($config)); + $unpaywall->setHttpService($service); + $this->assertEquals( + ['10.7553/66-4-1434' => [ + [ + 'link' => 'http://sajlis.journals.ac.za/pub/article/download/1434/1332', + 'label' => 'PDF Full Text', + ] + ] + ], + $unpaywall->getLinks(['10.7553/66-4-1434']) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php index e3f57eea0bd81724920daf7402ce7b4473e35f03..a4e598765fa7227578c1c1b6493e8db6c9c2910b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ExportTest.php @@ -42,7 +42,7 @@ use Zend\Config\Config; class ExportTest extends \PHPUnit\Framework\TestCase { /** - * Test bulk options using legacy (deprecated) configuration. + * Test options using legacy (deprecated) configuration. * * @return void */ @@ -61,26 +61,7 @@ class ExportTest extends \PHPUnit\Framework\TestCase ], ]; $export = $this->getExport($config); - $this->assertEquals(['foo', 'bar'], $export->getBulkOptions()); - } - - /** - * Test bulk options. - * - * @return void - */ - public function testGetBulkOptions() - { - $config = [ - 'Export' => [ - 'foo' => 'record,bulk', - 'bar' => 'record,bulk', - 'baz' => 0, - 'xyzzy' => 'record', - ], - ]; - $export = $this->getExport($config); - $this->assertEquals(['foo', 'bar'], $export->getBulkOptions()); + $this->assertEquals(['foo', 'bar'], $export->getActiveFormats('bulk')); } /** diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Form/FormTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Form/FormTest.php index 0961fcd91c6073ce1b26af5d20622a23a7e826af..0706f6ad9ae430e729309f22ea9f07dc2a2b89c2 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Form/FormTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Form/FormTest.php @@ -53,7 +53,9 @@ class FormTest extends \VuFindTest\Unit\TestCase $this->assertTrue($form->useCaptcha()); $this->assertFalse($form->showOnlyForLoggedUsers()); $this->assertEquals([], $form->getElements()); - $this->assertEquals([null, null], $form->getRecipient()); + $this->assertEquals( + [['email' => null, 'name' => null]], $form->getRecipient() + ); $this->assertNull($form->getTitle()); $this->assertNull($form->getHelp()); $this->assertEquals('VuFind Feedback', $form->getEmailSubject([])); @@ -79,7 +81,9 @@ class FormTest extends \VuFindTest\Unit\TestCase 'email_subject' => 'subject', ]; $form = new Form(new YamlReader(), $defaults); - $this->assertEquals(['me', 'me@example.com'], $form->getRecipient()); + $this->assertEquals( + [['name' => 'me', 'email' => 'me@example.com']], $form->getRecipient() + ); $this->assertEquals('subject', $form->getEmailSubject([])); } @@ -140,7 +144,9 @@ class FormTest extends \VuFindTest\Unit\TestCase ], $form->getElements() ); - $this->assertEquals([null, null], $form->getRecipient()); + $this->assertEquals( + [['email' => null, 'name' => null]], $form->getRecipient() + ); $this->assertEquals('Send us your feedback!', $form->getTitle()); $this->assertNull($form->getHelp()); $this->assertEquals('VuFind Feedback', $form->getEmailSubject([])); @@ -150,9 +156,9 @@ class FormTest extends \VuFindTest\Unit\TestCase $this->assertEquals( [ [ - 'Comments' => ['type' => 'textarea', 'value' => 'x'], - 'feedback_name' => ['type' => 'text', 'value' => 'y'], - 'feedback_email' => ['type' => 'email', 'value' => 'z@foo.com'], + ['type' => 'textarea', 'value' => 'x', 'label' => 'Comments'], + ['type' => 'text', 'value' => 'y', 'label' => 'feedback_name'], + ['type' => 'email', 'value' => 'z@foo.com', 'label' => 'feedback_email'], ], 'Email/form.phtml' ], diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php index 697e2a2acc69977be3015e133a1ea0bbb089303a..c107beec8677a662ecedba35175362cac7348830 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/Driver/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php index 5389a660b5f7df6e5030f318e1363d14d9244951..49bd6222b0d609f4a7d58e659b5a8dec99f66f9b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeDataSource/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php index 425ce89c9a95577a47088196d90ac338110b8830..d9d9db51281da279ab9fe81bc068c49071f4aa47 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Hierarchy/TreeRenderer/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php index 62b14e0da16d740023b945e69b06466adaadc4c0..9f94c7867a817af1a2c0ee28219fe981082e1acf 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/DemoTest.php @@ -54,10 +54,10 @@ class DemoTest extends \VuFindTest\Unit\TestCase */ public function setUp() { - $session = $this->getMockBuilder('Zend\Session\Container') + $session = $this->getMockBuilder(\Zend\Session\Container::class) ->disableOriginalConstructor()->getMock(); $this->driver = new Demo( - new \VuFind\Date\Converter(), $this->createMock('VuFindSearch\Service'), + new \VuFind\Date\Converter(), $this->createMock(\VuFindSearch\Service::class), function () use ($session) { return $session; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/FolioTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/FolioTest.php index 8242d4da2e9dbd144fcec604d2109d1f7b5763c7..fc7f4d9dc93866333f22cedc2b9c51ca11ca2b75 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/FolioTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/FolioTest.php @@ -158,7 +158,7 @@ class FolioTest extends \VuFindTest\Unit\TestCase public function testCheckValidToken() { $this->createConnector('check-valid-token'); - $profile = $this->driver->getMyTransactions(['username' => 'whatever']); + $profile = $this->driver->getMyTransactions(['id' => 'whatever']); // Check token $this->assertEquals('/users', $this->testRequestLog[0]['path']); // Move to method call diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php index 2fa754c75815d0a4c502a2ed0b3bd37655d6440b..b24510e4e5058038989c29c8daa2e695bfafbffd 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/MultiBackendTest.php @@ -84,7 +84,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $writer = new \Zend\Log\Writer\Mock(); $logger->addWriter($writer); - $mockPM = $this->createMock('\VuFind\Config\PluginManager'); + $mockPM = $this->createMock(\VuFind\Config\PluginManager::class); $mockPM->expects($this->any()) ->method('get') ->will( @@ -202,7 +202,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $this->assertEquals($configData, $val); $config = new \Zend\Config\Config($configData); - $mockPM = $this->createMock('\VuFind\Config\PluginManager'); + $mockPM = $this->createMock(\VuFind\Config\PluginManager::class); $mockPM->expects($this->any()) ->method('get') ->will( @@ -2220,7 +2220,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase $dummyILS = new DummyILS(); - $sm = $this->getMockBuilder('VuFind\ILS\Driver\PluginManager') + $sm = $this->getMockBuilder(\VuFind\ILS\Driver\PluginManager::class) ->disableOriginalConstructor()->getMock(); $sm->expects($this->any()) ->method('get') @@ -2282,7 +2282,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase */ protected function getMockILSAuthenticator($userSource = '') { - $mockAuth = $this->getMockBuilder('VuFind\Auth\ILSAuthenticator') + $mockAuth = $this->getMockBuilder(\VuFind\Auth\ILSAuthenticator::class) ->disableOriginalConstructor() ->getMock(); if ($userSource) { @@ -2309,7 +2309,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase { $configData = ['config' => 'values']; $config = new \Zend\Config\Config($configData); - $mockPM = $this->createMock('\VuFind\Config\PluginManager'); + $mockPM = $this->createMock(\VuFind\Config\PluginManager::class); $mockPM->expects($this->any()) ->method('get') ->will($this->returnValue($config)); @@ -2352,7 +2352,7 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase */ protected function getMockSM($times = null, $driver = 'Voyager', $return = null) { - $sm = $this->getMockBuilder('VuFind\ILS\Driver\PluginManager') + $sm = $this->getMockBuilder(\VuFind\ILS\Driver\PluginManager::class) ->disableOriginalConstructor()->getMock(); $sm->expects($times === null ? $this->any() : $times) ->method('get') @@ -2368,14 +2368,14 @@ class MultiBackendTest extends \VuFindTest\Unit\TestCase */ protected function getMockDemoDriver($methods) { - $session = $this->getMockBuilder('Zend\Session\Container') + $session = $this->getMockBuilder(\Zend\Session\Container::class) ->disableOriginalConstructor()->getMock(); return $this->getMockBuilder(__NAMESPACE__ . '\DemoMock') ->setMethods($methods) ->setConstructorArgs( [ new \VuFind\Date\Converter(), - $this->createMock('VuFindSearch\Service'), + $this->createMock(\VuFindSearch\Service::class), function () use ($session) { return $session; } @@ -2458,6 +2458,8 @@ class DummyILS extends \VuFind\ILS\Driver\AbstractBase * @throws \VuFind\Exception\ILS * @return mixed On success, an associative array with the following keys: * id, availability (boolean), status, location, reserve, callnumber. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getStatus($id) { @@ -2474,6 +2476,8 @@ class DummyILS extends \VuFind\ILS\Driver\AbstractBase * * @throws \VuFind\Exception\ILS * @return array An array of getStatus() return values on success. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getStatuses($ids) { @@ -2488,13 +2492,16 @@ class DummyILS extends \VuFind\ILS\Driver\AbstractBase * * @param string $id The record id to retrieve the holdings for * @param array $patron Patron data + * @param array $options Extra options (not currently used) * * @throws \VuFind\Exception\ILS * @return array On success, an associative array with the following * keys: id, availability (boolean), status, location, reserve, callnumber, * duedate, number, barcode. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getHolding($id, array $patron = null) + public function getHolding($id, array $patron = null, array $options = []) { return []; } @@ -2509,6 +2516,8 @@ class DummyILS extends \VuFind\ILS\Driver\AbstractBase * * @throws \VuFind\Exception\ILS * @return array An array with the acquisitions data on success. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function getPurchaseHistory($id) { diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php index 510f548ca8376c90dff07fa07939addee60533db..0e36604525e0873839bdce9b57237ed0a419ede8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/NoILSTest.php @@ -61,11 +61,11 @@ class NoILSTest extends \VuFindTest\Unit\TestCase */ public function setUp() { - $this->loader = $this->getMockBuilder('VuFind\Record\Loader') + $this->loader = $this->getMockBuilder(\VuFind\Record\Loader::class) ->setConstructorArgs( [ - $this->createMock('VuFindSearch\Service'), - $this->createMock('VuFind\RecordDriver\PluginManager') + $this->createMock(\VuFindSearch\Service::class), + $this->createMock(\VuFind\RecordDriver\PluginManager::class) ] )->getMock(); $this->driver = new NoILS($this->loader); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php index e4ad2042867f40ba7417253958ed4b7234684a4a..20e0e523d656e6a10644faf1a92ffaa8808ce6a3 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PAIATest.php @@ -248,6 +248,12 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase 'duedate' => '', 'message' => '', 'borrowingLocation' => 'Ausleihe', + 'type' => 'held', + 'location' => 'Ausleihe', + 'position' => 0, + 'available' => false, + 'create' => '11-15-2013', + 'cancel_details' => '', ], 1 => [ @@ -265,6 +271,12 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase 'message' => '', 'borrowingLocation' => 'Ausleihe', 'callnumber' => '22:2227-8001', + 'type' => 'held', + 'location' => 'Ausleihe', + 'position' => 0, + 'available' => false, + 'create' => '12-22-2011', + 'cancel_details' => '', ] ]; @@ -293,13 +305,14 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase protected $profileTestResult = [ 'firstname' => "Nobody", 'lastname' => "Nothing", - 'address1' => null, + 'address1' => "No street at all 8, D-21073 Hamburg", 'address2' => null, 'city' => null, 'country' => null, 'zip' => null, 'phone' => null, - 'group' => null, + 'group' => "de-830:user-type:2", + 'mobile_phone' => null, 'expires' => "12-31-9999", 'statuscode' => 0, 'canWrite' => true @@ -346,9 +359,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase "newPassword" => "newsecret" ]; - $conn = $this->createConnector('changePassword.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('changePassword.json'); $result = $conn->changePassword($changePasswordTestdata); $this->assertEquals($this->pwchangeTestResult, $result); } @@ -360,9 +371,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ public function testFees() { - $conn = $this->createConnector('fees.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('fees.json'); $result = $conn->getMyFines($this->patron); $this->assertEquals($this->feeTestResult, $result); @@ -375,9 +384,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ public function testHolds() { - $conn = $this->createConnector('items.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('items.json'); $result = $conn->getMyHolds($this->patron); $this->assertEquals($this->holdsTestResult, $result); @@ -390,9 +397,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ public function testRequests() { - $conn = $this->createConnector('items.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('items.json'); $result = $conn->getMyStorageRetrievalRequests($this->patron); $this->assertEquals($this->requestsTestResult, $result); @@ -405,9 +410,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ public function testTransactions() { - $conn = $this->createConnector('items.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('items.json'); $result = $conn->getMyTransactions($this->patron); $this->assertEquals($this->transactionsTestResult, $result); @@ -469,9 +472,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ public function testRenewDetails() { - $conn = $this->createConnector(''); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector(''); $result = $conn->getRenewDetails($this->transactionsTestResult[1]); $this->assertEquals('', $result); @@ -491,9 +492,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase ] ]; - $conn = $this->createConnector('storageretrieval.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('storageretrieval.json'); $result = $conn->placeHold($sr_request); $this->assertEquals($this->storageRetrievalTestResult, $result); } @@ -512,9 +511,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase ] ]; - $conn = $this->createConnector('storageretrieval.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('storageretrieval.json'); $result = $conn->placeStorageRetrievalRequest($sr_request); $this->assertEquals($this->storageRetrievalTestResult, $result); } @@ -535,9 +532,7 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase ] ]; - $conn = $this->createConnector('renew_ok.json'); - $conn->setConfig($this->validConfig); - $conn->init(); + $conn = $this->createMockConnector('renew_ok.json'); $result = $conn->renewMyItems($renew_request); $this->assertEquals($this->renewTestResult, $result); @@ -553,15 +548,15 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase } /** - * Create connector with fixture file. + * Create HTTP service for testing. * * @param string $fixture Fixture file * - * @return Connector + * @return \VuFindHttp\HttpService * * @throws InvalidArgumentException Fixture file does not exist */ - protected function createConnector($fixture = null) + protected function getHttpService($fixture = null) { $adapter = new TestAdapter(); if ($fixture) { @@ -580,6 +575,21 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase } $service = new \VuFindHttp\HttpService(); $service->setDefaultAdapter($adapter); + return $service; + } + + /** + * Create connector with fixture file. + * + * @param string $fixture Fixture file + * + * @return Connector + * + * @throws InvalidArgumentException Fixture file does not exist + */ + protected function createConnector($fixture = null) + { + $service = $this->getHttpService($fixture); $conn = new PAIA( new \VuFind\Date\Converter(), new \Zend\Session\SessionManager() @@ -599,31 +609,21 @@ class PAIATest extends \VuFindTest\Unit\ILSDriverTestCase */ protected function createMockConnector($fixture = null) { - $adapter = new TestAdapter(); - if ($fixture) { - $file = realpath( - __DIR__ . - '/../../../../../../tests/fixtures/paia/response/' . $fixture - ); - if (!is_string($file) || !file_exists($file) || !is_readable($file)) { - throw new InvalidArgumentException( - sprintf('Unable to load fixture file: %s ', $file) - ); - } - $response = file_get_contents($file); - $responseObj = HttpResponse::fromString($response); - $adapter->setResponse($responseObj); - } - $service = new \VuFindHttp\HttpService(); - $service->setDefaultAdapter($adapter); - $conn = $this->getMockBuilder('VuFind\ILS\Driver\PAIA') - ->setConstructorArgs([ new \VuFind\Date\Converter(), - new \Zend\Session\SessionManager() - ]) + $service = $this->getHttpService($fixture); + $dateConverter = new \VuFind\Date\Converter(); + $sessionManager = new \Zend\Session\SessionManager(); + $conn = $this->getMockBuilder(\VuFind\ILS\Driver\PAIA::class) + ->setConstructorArgs([$dateConverter, $sessionManager]) ->setMethods(['getScope']) ->getMock(); $conn->expects($this->any())->method('getScope') - ->will($this->returnValue([ 'write_items' ])); + ->will($this->returnValue([ + 'write_items', + 'change_password', + 'read_fees', + 'read_items', + 'read_patron' + ])); $conn->setHttpService($service); $conn->setConfig($this->validConfig); $conn->init(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php index 5dd4651532613545fc8f93220b739e0fc9e8b329..41b9397c0c8db1f0b2b1b7fc031191f3c00a178a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SierraRestTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SierraRestTest.php new file mode 100644 index 0000000000000000000000000000000000000000..32f962bba9d82141d04de755d8e8aa194a8512a9 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SierraRestTest.php @@ -0,0 +1,123 @@ +<?php +/** + * SierraRest ILS driver test + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\ILS\Driver; + +use VuFind\ILS\Driver\SierraRest; + +/** + * SierraRest ILS driver test + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class SierraRestTest extends \VuFindTest\Unit\ILSDriverTestCase +{ + /** + * Test bib IDs (raw value => formatted value) + * + * @var array + */ + protected $bibIds = [ + '12345' => '.b123456', + '23456' => '.b234564', + '34567' => '.b345672', + '45678' => '.b456780', + '56789' => '.b567899', + '191456' => '.b191456x', + ]; + + /** + * Standard setup method. + * + * @return void + */ + public function setUp() + { + $sessionFactory = function ($namespace) { + return new \Zend\Session\Container($namespace); + }; + $this->driver = new SierraRest( + new \VuFind\Date\Converter(), $sessionFactory + ); + } + + /** + * Test ID extraction. + * + * @return void + */ + public function testIdExtraction() + { + foreach ($this->bibIds as $raw => $formatted) { + // Extraction should return the same result whether we extract from + // the raw value or the formatted value: + $this->assertEquals( + $raw, $this->callMethod($this->driver, 'extractBibId', [$raw]) + ); + $this->assertEquals( + $raw, $this->callMethod($this->driver, 'extractBibId', [$formatted]) + ); + } + } + + /** + * Test default ID formatting (no prefixing). + * + * @return void + */ + public function testDefaultBibFormatting() + { + foreach (array_keys($this->bibIds) as $id) { + $this->assertEquals( + $id, + $this->callMethod($this->driver, 'formatBibId', [$id]) + ); + } + } + + /** + * Test default ID formatting (no prefixing). + * + * @return void + */ + public function testPrefixedBibFormatting() + { + $this->driver->setConfig( + ['Catalog' => ['use_prefixed_ids' => true]] + ); + foreach ($this->bibIds as $raw => $formatted) { + $this->assertEquals( + $formatted, + $this->callMethod($this->driver, 'formatBibId', [$raw]) + ); + } + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php index 5ffbe0d459a8d119018001d73a1e3220074c06c8..88434bb9ce9b1bb90aa6c51bea172cd0ada3fb98 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/ILS/Driver/SymphonyTest.php @@ -54,7 +54,7 @@ class SymphonyTest extends \VuFindTest\Unit\TestCase */ public function setUp() { - $loader = $this->getMockBuilder('VuFind\Record\Loader') + $loader = $this->getMockBuilder(\VuFind\Record\Loader::class) ->disableOriginalConstructor()->getMock(); $this->driver = new Symphony($loader); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php index 1b96dd4f93256c9c7aa6c01fb502d99cda03dc0e..2662a8b5707d2f8a0ee41854b0e3949842113929 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Log/LoggerTest.php @@ -78,7 +78,7 @@ CONTEXT; && false !== strpos($a[5], 'function =') && count($a) == 5; }; - $logger = $this->getMockBuilder('VuFind\Log\Logger') + $logger = $this->getMockBuilder(\VuFind\Log\Logger::class) ->setMethods(['log']) ->getMock(); $logger->expects($this->once())->method('log')->with($this->equalTo(Logger::CRIT), $this->callback($callback)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php index e61579f131c3b6b226c0c38b2561d558c8c88f83..a5d49a5128b7bee186f377fadd3fde760e640001 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Mailer/MailerTest.php @@ -27,7 +27,9 @@ */ namespace VuFindTest\Mailer; +use VuFind\Mailer\Factory as MailerFactory; use VuFind\Mailer\Mailer; +use VuFindTest\Container\MockContainer; use Zend\Mail\Address; use Zend\Mail\AddressList; @@ -42,6 +44,43 @@ use Zend\Mail\AddressList; */ class MailerTest extends \VuFindTest\Unit\TestCase { + /** + * Test that the factory configures the object correctly. + * + * @return void + */ + public function testFactoryConfiguration() + { + $config = new \Zend\Config\Config( + [ + 'Mail' => [ + 'host' => 'vufindtest.localhost', + 'port' => 123, + 'connection_time_limit' => 600, + 'name' => 'foo', + 'username' => 'vufinduser', + 'password' => 'vufindpass', + ] + ] + ); + $cm = new MockContainer($this); + $cm->set('config', $config); + $sm = new MockContainer($this); + $sm->set(\VuFind\Config\PluginManager::class, $cm); + $factory = new MailerFactory(); + $mailer = $factory($sm, Mailer::class); + $options = $mailer->getTransport()->getOptions(); + $this->assertEquals('vufindtest.localhost', $options->getHost()); + $this->assertEquals('foo', $options->getName()); + $this->assertEquals(123, $options->getPort()); + $this->assertEquals(600, $options->getConnectionTimeLimit()); + $this->assertEquals('login', $options->getConnectionClass()); + $this->assertEquals( + ['username' => 'vufinduser', 'password' => 'vufindpass'], + $options->getConnectionConfig() + ); + } + /** * Test sending an email. * @@ -55,7 +94,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'subject' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $mailer = new Mailer($transport); $mailer->send('to@example.com', 'from@example.com', 'subject', 'body'); @@ -75,7 +114,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'subject' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $address = new Address('from@example.com', 'Sender TextName'); $mailer = new Mailer($transport); @@ -96,7 +135,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'subject' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $address = new Address('to@example.com', 'Recipient TextName'); $mailer = new Mailer($transport); @@ -117,7 +156,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'subject' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $list = new AddressList(); $list->add(new Address('to@example.com', 'Recipient TextName')); @@ -140,7 +179,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'subject' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $address = new Address('me@example.com'); $mailer = new Mailer($transport); @@ -148,6 +187,52 @@ class MailerTest extends \VuFindTest\Unit\TestCase $mailer->send('to@example.com', $address, 'subject', 'body'); } + /** + * Test sending an email using an explicitly set reply-to address. + * + * @return void + */ + public function testSendWithReplyTo() + { + $callback = function ($message) { + $fromString = $message->getFrom()->current()->toString(); + return '<to@example.com>' == $message->getTo()->current()->toString() + && '<reply-to@example.com>' == $message->getReplyTo()->current()->toString() + && '<me@example.com>' == $fromString + && 'body' == $message->getBody() + && 'subject' == $message->getSubject(); + }; + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); + $transport->expects($this->once())->method('send')->with($this->callback($callback)); + $address = new Address('me@example.com'); + $mailer = new Mailer($transport); + $mailer->send('to@example.com', $address, 'subject', 'body', null, 'reply-to@example.com'); + } + + /** + * Test sending an email using a from address override + * and an explicitly set reply-to address. + * + * @return void + */ + public function testSendWithFromOverrideAndReplyTo() + { + $callback = function ($message) { + $fromString = $message->getFrom()->current()->toString(); + return '<to@example.com>' == $message->getTo()->current()->toString() + && '<reply-to@example.com>' == $message->getReplyTo()->current()->toString() + && 'me <no-reply@example.com>' == $fromString + && 'body' == $message->getBody() + && 'subject' == $message->getSubject(); + }; + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); + $transport->expects($this->once())->method('send')->with($this->callback($callback)); + $address = new Address('me@example.com'); + $mailer = new Mailer($transport); + $mailer->setFromAddressOverride('no-reply@example.com'); + $mailer->send('to@example.com', $address, 'subject', 'body', null, 'reply-to@example.com'); + } + /** * Test bad to address. * @@ -158,7 +243,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testBadTo() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send('bad@bad', 'from@example.com', 'subject', 'body'); } @@ -173,7 +258,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testBadReplyTo() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send( 'good@good.com', 'from@example.com', 'subject', 'body', null, 'bad@bad' @@ -190,7 +275,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testEmptyTo() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send('', 'from@example.com', 'subject', 'body'); } @@ -205,7 +290,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testTooManyRecipients() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send('one@test.com;two@test.com', 'from@example.com', 'subject', 'body'); } @@ -220,7 +305,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testBadFrom() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send('to@example.com', 'bad@bad', 'subject', 'body'); } @@ -235,7 +320,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testBadFromInAddressObject() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $mailer = new Mailer($transport); $mailer->send('to@example.com', new Address('bad@bad'), 'subject', 'body'); } @@ -250,7 +335,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testTransportException() { - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->will($this->throwException(new \Exception('Boom'))); $mailer = new Mailer($transport); $mailer->send('to@example.com', 'from@example.com', 'subject', 'body'); @@ -285,7 +370,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'Library Catalog Search Result' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $mailer = new Mailer($transport); $mailer->setMaxRecipients(2); @@ -302,7 +387,7 @@ class MailerTest extends \VuFindTest\Unit\TestCase */ public function testSendRecord() { - $driver = $this->createMock('VuFind\RecordDriver\AbstractBase'); + $driver = $this->createMock(\VuFind\RecordDriver\AbstractBase::class); $driver->expects($this->once())->method('getBreadcrumb')->will($this->returnValue('breadcrumb')); $viewCallback = function ($in) use ($driver) { @@ -323,11 +408,24 @@ class MailerTest extends \VuFindTest\Unit\TestCase && 'body' == $message->getBody() && 'Library Catalog Record: breadcrumb' == $message->getSubject(); }; - $transport = $this->createMock('Zend\Mail\Transport\TransportInterface'); + $transport = $this->createMock(\Zend\Mail\Transport\TransportInterface::class); $transport->expects($this->once())->method('send')->with($this->callback($callback)); $mailer = new Mailer($transport); $mailer->sendRecord('to@example.com', 'from@example.com', 'message', $driver, $view); } + + /** + * Test connection reset + * + * @return void + */ + public function testResetConnection() + { + $transport = $this->createMock(\Zend\Mail\Transport\Smtp::class); + $transport->expects($this->once())->method('disconnect'); + $mailer = new Mailer($transport); + $mailer->resetConnection(); + } } class MockEmailRenderer extends \Zend\View\Renderer\PhpRenderer diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/BEPressTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/BEPressTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c6407f6474b05a1aad7206d6332881f957e79376 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/BEPressTest.php @@ -0,0 +1,71 @@ +<?php +/** + * BEPress Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFind\MetadataVocabulary\BEPress; + +/** + * BEPress Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class BEPressTest extends \VuFindTest\Unit\TestCase +{ + use FakeDriverTrait; + + /** + * Test basic functionality of the class. + * + * @return void + */ + public function testMappings() + { + $meta = new BEPress(); + $this->assertEquals( + [ + 'bepress_citation_title' => ['Fake Title'], + 'bepress_citation_doi' => ['FakeDOI'], + 'bepress_citation_lastpage' => [10], + 'bepress_citation_isbn' => ['FakeISBN'], + 'bepress_citation_issn' => ['FakeISSN'], + 'bepress_citation_firstpage' => [1], + 'bepress_citation_volume' => [7], + 'bepress_citation_author' => ['Mr. Person'], + 'bepress_citation_issue' => [5], + 'bepress_citation_journal_title' => ['My Journal'], + 'bepress_citation_publisher' => ['Fake Publisher'], + 'bepress_citation_date' => [2020], + ], + $meta->getMappedData($this->getDriver()) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/DublinCoreTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/DublinCoreTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c53c958496f986554058d3dd8b64a04c93b20384 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/DublinCoreTest.php @@ -0,0 +1,70 @@ +<?php +/** + * DublinCore Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFind\MetadataVocabulary\DublinCore; + +/** + * DublinCore Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class DublinCoreTest extends \VuFindTest\Unit\TestCase +{ + use FakeDriverTrait; + + /** + * Test basic functionality of the class. + * + * @return void + */ + public function testMappings() + { + $meta = new DublinCore(); + $this->assertEquals( + [ + 'DC.title' => ['Fake Title'], + 'DC.language' => ['English'], + 'DC.citation.epage' => [10], + 'DC.identifier' => ['FakeDOI', 'FakeISBN', 'FakeISSN'], + 'DC.citation.spage' => [1], + 'DC.citation.volume' => [7], + 'DC.creator' => ['Mr. Person'], + 'DC.citation.issue' => [5], + 'DC.relation.ispartof' => ['My Journal'], + 'DC.publisher' => ['Fake Publisher'], + 'DC.issued' => [2020], + ], + $meta->getMappedData($this->getDriver()) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/EprintsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/EprintsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eefb15ac84da29370be4106d36e69237790fb8e1 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/EprintsTest.php @@ -0,0 +1,67 @@ +<?php +/** + * Eprints Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFind\MetadataVocabulary\Eprints; + +/** + * Eprints Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class EprintsTest extends \VuFindTest\Unit\TestCase +{ + use FakeDriverTrait; + + /** + * Test basic functionality of the class. + * + * @return void + */ + public function testMappings() + { + $meta = new Eprints(); + $this->assertEquals( + [ + 'eprints.title' => ['Fake Title'], + 'eprints.issn' => ['FakeISSN'], + 'eprints.pagerange' => ['1-10'], + 'eprints.number' => [7], + 'eprints.creators_name' => ['Mr. Person'], + 'eprints.publication' => ['My Journal'], + 'eprints.publisher' => ['Fake Publisher'], + 'eprints.date' => [2020], + ], + $meta->getMappedData($this->getDriver()) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/FakeDriverTrait.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/FakeDriverTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..aa98ee0825e8b794a1fbe207dc938cb4bf5139ae --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/FakeDriverTrait.php @@ -0,0 +1,69 @@ +<?php +/** + * Trait containing method to generate fake drivers for metadata testing. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFindTest\RecordDriver\TestHarness; + +/** + * Trait containing method to generate fake drivers for metadata testing. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +trait FakeDriverTrait +{ + /** + * Get a fake record driver + * + * @return TestHarness + */ + protected function getDriver() + { + $data = [ + 'Title' => 'Fake Title', + 'PrimaryAuthors' => ['Mr. Person'], + 'ContainerTitle' => 'My Journal', + 'PublicationDates' => [2020], + 'CleanDOI' => 'FakeDOI', + 'ContainerEndPage' => 10, + 'CleanISBN' => 'FakeISBN', + 'CleanISSN' => 'FakeISSN', + 'ContainerIssue' => 5, + 'Languages' => ['English'], + 'Publishers' => ['Fake Publisher'], + 'ContainerStartPage' => 1, + 'ContainerVolume' => 7, + ]; + $driver = new TestHarness(); + $driver->setRawData($data); + return $driver; + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/HighwirePressTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/HighwirePressTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1654865994938263f762dfe9778fd3a7de21aa6 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/HighwirePressTest.php @@ -0,0 +1,72 @@ +<?php +/** + * HighwirePress Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFind\MetadataVocabulary\HighwirePress; + +/** + * HighwirePress Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class HighwirePressTest extends \VuFindTest\Unit\TestCase +{ + use FakeDriverTrait; + + /** + * Test basic functionality of the class. + * + * @return void + */ + public function testMappings() + { + $meta = new HighwirePress(); + $this->assertEquals( + [ + 'citation_title' => ['Fake Title'], + 'citation_doi' => ['FakeDOI'], + 'citation_lastpage' => [10], + 'citation_isbn' => ['FakeISBN'], + 'citation_issn' => ['FakeISSN'], + 'citation_firstpage' => [1], + 'citation_volume' => [7], + 'citation_author' => ['Mr. Person'], + 'citation_issue' => [5], + 'citation_journal_title' => ['My Journal'], + 'citation_publisher' => ['Fake Publisher'], + 'citation_date' => [2020], + 'citation_language' => ['English'], + ], + $meta->getMappedData($this->getDriver()) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/PRISMTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/PRISMTest.php new file mode 100644 index 0000000000000000000000000000000000000000..4c9b1165bd153eb0830f2635c45e0349c59477a4 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/MetadataVocabulary/PRISMTest.php @@ -0,0 +1,66 @@ +<?php +/** + * PRISM Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\MetadataVocabulary; + +use VuFind\MetadataVocabulary\PRISM; + +/** + * PRISM Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class PRISMTest extends \VuFindTest\Unit\TestCase +{ + use FakeDriverTrait; + + /** + * Test basic functionality of the class. + * + * @return void + */ + public function testMappings() + { + $meta = new PRISM(); + $this->assertEquals( + [ + 'prism.title' => ['Fake Title'], + 'prism.doi' => ['FakeDOI'], + 'prism.endingPage' => [10], + 'prism.isbn' => ['FakeISBN'], + 'prism.issn' => ['FakeISSN'], + 'prism.startingPage' => [1], + 'prism.volume' => [7], + ], + $meta->getMappedData($this->getDriver()) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php index f575bdb8c4badd85a14df38525db412a15865b61..03192ed6a1d409730dce55a8d79a1ef78ca413d3 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/OAI/ServerTest.php @@ -89,7 +89,7 @@ class ServerTest extends \VuFindTest\Unit\TestCase */ protected function getMockResultsManager() { - return $this->getMockBuilder('VuFind\Search\Results\PluginManager') + return $this->getMockBuilder(\VuFind\Search\Results\PluginManager::class) ->disableOriginalConstructor() ->getMock(); } @@ -101,7 +101,7 @@ class ServerTest extends \VuFindTest\Unit\TestCase */ protected function getMockRecordLoader() { - return $this->getMockBuilder('VuFind\Record\Loader') + return $this->getMockBuilder(\VuFind\Record\Loader::class) ->disableOriginalConstructor() ->getMock(); } @@ -113,7 +113,7 @@ class ServerTest extends \VuFindTest\Unit\TestCase */ protected function getMockTableManager() { - return $this->getMockBuilder('VuFind\Db\Table\PluginManager') + return $this->getMockBuilder(\VuFind\Db\Table\PluginManager::class) ->disableOriginalConstructor() ->getMock(); } @@ -125,7 +125,7 @@ class ServerTest extends \VuFindTest\Unit\TestCase */ protected function getMockRecordFormatter() { - return $this->getMockBuilder('VuFindApi\Formatter\RecordFormatter') + return $this->getMockBuilder(\VuFindApi\Formatter\RecordFormatter::class) ->disableOriginalConstructor() ->getMock(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php index c7f5e3113a17f7470eefe6dd7b452fcff5ba0b45..6277b28eb2d0c66c06f40181341773ad285b22f9 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/QRCode/LoaderTest.php @@ -59,7 +59,7 @@ class LoaderTest extends \VuFindTest\Unit\TestCase */ public function testUtterFailure() { - $theme = $this->getMockBuilder('VuFindTheme\ThemeInfo') + $theme = $this->getMockBuilder(\VuFindTheme\ThemeInfo::class) ->setConstructorArgs(['foo', 'bar'])->getMock(); $theme->expects($this->once())->method('findContainingTheme')->with($this->equalTo(['images/noQRCode.gif']))->will($this->returnValue(false)); $loader = $this->getLoader([], $theme); @@ -95,7 +95,7 @@ class LoaderTest extends \VuFindTest\Unit\TestCase $theme = new ThemeInfo($this->getThemeDir(), $this->testTheme); } if ($mock) { - return $this->getMockBuilder('VuFind\QRCode\Loader') + return $this->getMockBuilder(\VuFind\QRCode\Loader::class) ->setMethods($mock) ->setConstructorArgs([$config, $theme]) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php index 4d0644325249ea12b7a3081f6e1d46f0a7d5498c..5e11d90f71c24f749262c01abe0343787dbf3fd2 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/CollectionSideFacetsTest.php @@ -96,7 +96,7 @@ class CollectionSideFacetsTest extends \VuFindTest\Unit\TestCase */ protected function getMockConfigLoader($config = [], $key = 'facets') { - $loader = $this->getMockBuilder('VuFind\Config\PluginManager') + $loader = $this->getMockBuilder(\VuFind\Config\PluginManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->once())->method('get')->with($this->equalTo($key)) ->will($this->returnValue(new \Zend\Config\Config($config))); @@ -115,7 +115,7 @@ class CollectionSideFacetsTest extends \VuFindTest\Unit\TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -134,7 +134,7 @@ class CollectionSideFacetsTest extends \VuFindTest\Unit\TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php index c62f2368b266cc4b21863f1d070b55758b3c1837..851ed35cc20f5947b1a1f76797ec05986e2c425e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/EuropeanaResultsTest.php @@ -140,7 +140,7 @@ class EuropeanaResultsTest extends TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -159,7 +159,7 @@ class EuropeanaResultsTest extends TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php index 738dfcdf999c03a1a74d0329d7fb9271f5bd47a4..f0cb075ee5ef9979172181ceeda527e2c614fa8d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExpandFacetsTest.php @@ -116,7 +116,7 @@ class ExpandFacetsTest extends \VuFindTest\Unit\TestCase */ protected function getMockConfigLoader($config = [], $key = 'facets') { - $loader = $this->getMockBuilder('VuFind\Config\PluginManager') + $loader = $this->getMockBuilder(\VuFind\Config\PluginManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->once())->method('get')->with($this->equalTo($key)) ->will($this->returnValue(new \Zend\Config\Config($config))); @@ -135,7 +135,7 @@ class ExpandFacetsTest extends \VuFindTest\Unit\TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -154,7 +154,7 @@ class ExpandFacetsTest extends \VuFindTest\Unit\TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php new file mode 100644 index 0000000000000000000000000000000000000000..092f7a97242ee2a1ac5cc0170c0b52c954d8db5e --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/ExternalSearchTest.php @@ -0,0 +1,99 @@ +<?php +/** + * ExternalSearch recommendation module Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Recommend; + +use VuFind\Recommend\ExternalSearch; + +/** + * ExternalSearch recommendation module Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class ExternalSearchTest extends \VuFindTest\Unit\TestCase +{ + /** + * Run a test scenario + * + * @param string $label Link text + * @param string $template Link template + * @param string $lookfor Search query + * @param string $expectedUrl Expected URL + * + * @return void + */ + protected function runProcedure($label, $template, $lookfor, $expectedUrl) + { + $rec = new ExternalSearch(); + $rec->setConfig($label . ':' . $template); + $params = new \Zend\StdLib\Parameters(); + $params->set('lookfor', $lookfor); + $rec->init( + $this->createMock(\VuFind\Search\Solr\Params::class), + $params + ); + $rec->process( + $this->createMock(\VuFind\Search\Solr\Results::class) + ); + $this->assertEquals($label, $rec->getLinkText()); + $this->assertEquals($expectedUrl, $rec->getUrl()); + } + + /** + * Test concatenation behavior + * + * @return void + */ + public function testDefaultConcatenation() + { + $this->runProcedure( + 'my label', + 'http://foo?q=', + 'beep', + 'http://foo?q=beep' + ); + } + + /** + * Test template insertion behavior + * + * @return void + */ + public function testTemplateBehavior() + { + $this->runProcedure( + 'my label', + 'http://foo?q=%%lookfor%%&z=xyzzy', + 'beep', + 'http://foo?q=beep&z=xyzzy' + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php index 2999bbd7c80b5cc1a9390527aa0d711edf9e9f26..8b8fe791c780f58958f3890e84c4843c5a13bd4a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FacetCloudTest.php @@ -96,7 +96,7 @@ class FacetCloudTest extends \VuFindTest\Unit\TestCase */ protected function getMockConfigLoader($config = [], $key = 'facets') { - $loader = $this->getMockBuilder('VuFind\Config\PluginManager') + $loader = $this->getMockBuilder(\VuFind\Config\PluginManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->once())->method('get')->with($this->equalTo($key)) ->will($this->returnValue(new \Zend\Config\Config($config))); @@ -115,7 +115,7 @@ class FacetCloudTest extends \VuFindTest\Unit\TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -134,7 +134,7 @@ class FacetCloudTest extends \VuFindTest\Unit\TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php index 526ef0baf8a1a79c5de805bd9114875989f3fcc5..6e3e95391f3f1442b8aed6fc0310cb9aad1e0468 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/FavoriteFacetsTest.php @@ -109,7 +109,7 @@ class FavoriteFacetsTest extends \VuFindTest\Unit\TestCase */ protected function getMockConfigLoader($config = [], $key = 'config') { - $loader = $this->getMockBuilder('VuFind\Config\PluginManager') + $loader = $this->getMockBuilder(\VuFind\Config\PluginManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->any())->method('get')->with($this->equalTo($key)) ->will($this->returnValue(new \Zend\Config\Config($config))); @@ -128,7 +128,7 @@ class FavoriteFacetsTest extends \VuFindTest\Unit\TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -147,7 +147,7 @@ class FavoriteFacetsTest extends \VuFindTest\Unit\TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php index 3c652a665d8a1bca92d0dfb441739e929c1b2e15..f6113819add427f16966aa3acc240a489f69da82 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php index 380e48d15b4770b9fd570023f64694527512f833..c114544a7cc9b5a50c80ffe29a8d0a460f1895f7 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RandomRecommendTest.php @@ -51,8 +51,8 @@ class RandomRecommendTest extends TestCase public function setup() { $this->recommend = new Random( - $this->createMock('VuFindSearch\Service'), - $this->createMock('VuFind\Search\Params\PluginManager') + $this->createMock(\VuFindSearch\Service::class), + $this->createMock(\VuFind\Search\Params\PluginManager::class) ); } @@ -123,23 +123,23 @@ class RandomRecommendTest extends TestCase */ public function testCanInitialise() { - $service = $this->createMock('VuFindSearch\Service'); - $paramManager = $this->createMock('VuFind\Search\Params\PluginManager'); + $service = $this->createMock(\VuFindSearch\Service::class); + $paramManager = $this->createMock(\VuFind\Search\Params\PluginManager::class); $recommend = new Random($service, $paramManager); // Use Solr since some Base components are abstract: $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); - $request = $this->createMock('\Zend\StdLib\Parameters'); + $request = $this->createMock(\Zend\StdLib\Parameters::class); $service->expects($this->once())->method('random') ->with( $this->equalTo("Solr"), $this->equalTo($params->getQuery()), $this->equalTo(10) - )->will($this->returnValue($this->createMock('VuFindSearch\Response\RecordCollectionInterface'))); + )->will($this->returnValue($this->createMock(\VuFindSearch\Response\RecordCollectionInterface::class))); $recommend->setConfig("Solr:10:mixed:retain:20:facet1:value1:facet2:value2"); $recommend->init($params, $request); @@ -152,8 +152,8 @@ class RandomRecommendTest extends TestCase */ public function testCanInitialiseInDisregardMode() { - $service = $this->createMock('VuFindSearch\Service'); - $paramManager = $this->createMock('VuFind\Search\Params\PluginManager'); + $service = $this->createMock(\VuFindSearch\Service::class); + $paramManager = $this->createMock(\VuFind\Search\Params\PluginManager::class); $recommend = new Random($service, $paramManager); $paramManager->expects($this->once())->method('get') @@ -161,20 +161,20 @@ class RandomRecommendTest extends TestCase ->will( $this->returnValue( $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr') + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr') ) ); // Use Solr since some Base components are abstract: $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); - $request = $this->createMock('\Zend\StdLib\Parameters'); + $request = $this->createMock(\Zend\StdLib\Parameters::class); $service->expects($this->once())->method('random') ->with($this->equalTo("Solr")) - ->will($this->returnValue($this->createMock('VuFindSearch\Response\RecordCollectionInterface'))); + ->will($this->returnValue($this->createMock(\VuFindSearch\Response\RecordCollectionInterface::class))); $recommend->setConfig("Solr:10:mixed:disregard:20:facet1:value1:facet2:value2"); $recommend->init($params, $request); @@ -187,20 +187,20 @@ class RandomRecommendTest extends TestCase */ public function testWillReturnEmptyForMinimumResultLimit() { - $service = $this->createMock('VuFindSearch\Service'); - $paramManager = $this->createMock('VuFind\Search\Params\PluginManager'); + $service = $this->createMock(\VuFindSearch\Service::class); + $paramManager = $this->createMock(\VuFind\Search\Params\PluginManager::class); $recommend = new Random($service, $paramManager); $records = ["1", "2", "3", "4", "5"]; // Use Solr since some Base components are abstract: $results = $this->getServiceManager() - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get('Solr'); $params = $results->getParams(); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); - $request = $this->createMock('\Zend\StdLib\Parameters'); + $request = $this->createMock(\Zend\StdLib\Parameters::class); - $results = $this->createMock('VuFindSearch\Response\RecordCollectionInterface'); + $results = $this->createMock(\VuFindSearch\Response\RecordCollectionInterface::class); $results->expects($this->once())->method('getRecords') ->will($this->returnValue($records)); @@ -225,20 +225,20 @@ class RandomRecommendTest extends TestCase */ public function testWillReturnResults() { - $service = $this->createMock('VuFindSearch\Service'); - $paramManager = $this->createMock('VuFind\Search\Params\PluginManager'); + $service = $this->createMock(\VuFindSearch\Service::class); + $paramManager = $this->createMock(\VuFind\Search\Params\PluginManager::class); $recommend = new Random($service, $paramManager); $records = ["1", "2", "3", "4", "5"]; // Use Solr since some Base components are abstract: $results = $this->getServiceManager() - ->get('VuFind\Search\Results\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Results\PluginManager::class)->get('Solr'); $params = $results->getParams(); $query = $this->getFixture('query'); $params->setBasicSearch($query->getString(), $query->getHandler()); - $request = $this->createMock('\Zend\StdLib\Parameters'); + $request = $this->createMock(\Zend\StdLib\Parameters::class); - $results = $this->createMock('VuFindSearch\Response\RecordCollectionInterface'); + $results = $this->createMock(\VuFindSearch\Response\RecordCollectionInterface::class); $results->expects($this->once())->method('getRecords') ->will($this->returnValue($records)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RecommendLinksTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RecommendLinksTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f514247bedb524fb70626ad3fff0eb664708b009 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/RecommendLinksTest.php @@ -0,0 +1,114 @@ +<?php +/** + * RecommendLinks recommendation module Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Recommend; + +use VuFind\Recommend\RecommendLinks; + +/** + * RecommendLinks recommendation module Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class RecommendLinksTest extends \VuFindTest\Unit\TestCase +{ + /** + * Test configuration data. + * + * @var array + */ + protected $sampleLinks = [ + 'foo' => 'http://foo', + 'bar' => 'http://bar', + ]; + + /** + * Run a test scenario + * + * @param \VuFind\Config\PluginManager $cm Configuration manager + * @param string $config Recommendation config + * + * @return void + */ + protected function runTestProcedure($cm, $config) + { + $rec = new RecommendLinks($cm); + $rec->setConfig($config); + $rec->init( + $this->createMock(\VuFind\Search\Solr\Params::class), + new \Zend\StdLib\Parameters() + ); + $rec->process( + $this->createMock(\VuFind\Search\Solr\Results::class) + ); + $this->assertEquals($this->sampleLinks, $rec->getLinks()); + } + + /** + * Test with default configuration. + * + * @return void + */ + public function testRecommendLinksWithDefaultConfiguration() + { + $cm = $this->getConfigManager('RecommendLinks', 'searches'); + $this->runTestProcedure($cm, ''); + } + + /** + * Test with custom configuration. + * + * @return void + */ + public function testRecommendLinksWithCustomConfiguration() + { + $cm = $this->getConfigManager('bar', 'foo'); + $this->runTestProcedure($cm, 'bar:foo'); + } + + /** + * Build a mock config manager to support the test. + * + * @return \VuFind\Config\PluginManager + */ + protected function getConfigManager($section, $ini) + { + $config = new \Zend\Config\Config([$section => $this->sampleLinks]); + $mock = $this->getMockBuilder(\VuFind\Config\PluginManager::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + $mock->expects($this->once())->method('get') + ->with($this->equalTo($ini)) + ->will($this->returnValue($config)); + return $mock; + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php index 8fe36e25349108d490710f84469041f89fb67ced..f8427d2b9500b6fd91d1a917858d21a6cf6d2ec4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SideFacetsTest.php @@ -172,32 +172,6 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase $this->assertTrue($sf->excludeAllowed('format')); } - /** - * Test getVisibleFilters - * - * @return void - */ - public function testGetVisibleFilters() - { - $filters = [ - 'format' => [ - ['value' => 'foo'], - ['value' => 'bar', 'suppressDisplay' => true], - ], - ]; - $results = $this->getMockResults(); - $results->getParams()->expects($this->once())->method('getFilterList') - ->with($this->equalTo(true))->will($this->returnValue($filters)); - $sf = $this->getSideFacets(null, $results); - $this->assertEquals( - [ - 'format' => [['value' => 'foo']], - 'extra' => [['value' => 'baz']], - ], - $sf->getVisibleFilters(['extra' => [['value' => 'baz']]]) - ); - } - /** * Test getAllRangeFacets() * @@ -220,7 +194,7 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase 'numeric' => ['[1 TO 9]'], ]; $results = $this->getMockResults(); - $results->getParams()->expects($this->any())->method('getFilters') + $results->getParams()->expects($this->any())->method('getRawFilters') ->will($this->returnValue($filters)); $sf = $this->getSideFacets($this->getMockConfigLoader($config), $results); $expected = [ @@ -272,14 +246,10 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase $filters = [ 'format' => [ ['value' => 'foo'], - ['value' => 'bar', 'suppressDisplay' => true], + ['value' => 'bar'], ], ]; $results = $this->getMockResults(); - $response = ['format' => ['dummy']]; - $results->expects($this->once())->method('getFacetList') - ->with($this->equalTo(['format' => 'Format'])) - ->will($this->returnValue($response)); $sf = $this->getSideFacets($this->getMockConfigLoader($config), $results); $this->assertEquals(['format'], $sf->getCollapsedFacets()); } @@ -326,7 +296,7 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase */ protected function getMockConfigLoader($config = [], $key = 'facets') { - $loader = $this->getMockBuilder('VuFind\Config\PluginManager') + $loader = $this->getMockBuilder(\VuFind\Config\PluginManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->once())->method('get')->with($this->equalTo($key)) ->will($this->returnValue(new \Zend\Config\Config($config))); @@ -345,7 +315,7 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase if (null === $params) { $params = $this->getMockParams(); } - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -364,7 +334,7 @@ class SideFacetsTest extends \VuFindTest\Unit\TestCase if (null === $query) { $query = new \VuFindSearch\Query\Query('foo', 'bar'); } - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php index 1b1de8f9cf5ff376a4e18a7a5c55bf5fb40dcfea..fb32c0e55f57624c927a132be05caa4ca55bba81 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SummonResultsDeferredTest.php @@ -47,7 +47,7 @@ class SummonResultsDeferredTest extends \VuFindTest\Unit\RecommendDeferredTestCa { $results = $this->getMockResults(); $params = $results->getParams(); - $options = $this->getMockBuilder('VuFind\Search\Solr\Options') + $options = $this->getMockBuilder(\VuFind\Search\Solr\Options::class) ->disableOriginalConstructor()->getMock(); $options->expects($this->once())->method('getLabelForBasicHandler')->with($this->equalTo('bar'))->will($this->returnValue('baz')); $params->expects($this->once())->method('getOptions')->will($this->returnValue($options)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php index ac31c69799a0bf57e527a6675d5eae8733ef85da..019657b6f41636714d46a9263810f3953b8859b8 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Recommend/SwitchQueryTest.php @@ -212,15 +212,15 @@ class SwitchQueryTest extends \VuFindTest\Unit\TestCase protected function getMockBackendManager($csBools = true, $csRanges = true) { $helper = new \VuFindSearch\Backend\Solr\LuceneSyntaxHelper($csBools, $csRanges); - $queryBuilder = $this->getMockBuilder('VuFindSearch\Backend\Solr\QueryBuilder') + $queryBuilder = $this->getMockBuilder(\VuFindSearch\Backend\Solr\QueryBuilder::class) ->disableOriginalConstructor()->getMock(); $queryBuilder->expects($this->any())->method('getLuceneHelper') ->will($this->returnValue($helper)); - $backend = $this->getMockBuilder('VuFindSearch\Backend\Solr\Backend') + $backend = $this->getMockBuilder(\VuFindSearch\Backend\Solr\Backend::class) ->disableOriginalConstructor()->getMock(); $backend->expects($this->any())->method('getQueryBuilder') ->will($this->returnValue($queryBuilder)); - $loader = $this->getMockBuilder('VuFind\Search\BackendManager') + $loader = $this->getMockBuilder(\VuFind\Search\BackendManager::class) ->disableOriginalConstructor()->getMock(); $loader->expects($this->any())->method('get')->with($this->equalTo('Solr')) ->will($this->returnValue($backend)); @@ -238,7 +238,7 @@ class SwitchQueryTest extends \VuFindTest\Unit\TestCase protected function getMockResults($query = '', $type = 'basic') { $params = $this->getMockParams($query, $type); - $results = $this->getMockBuilder('VuFind\Search\Solr\Results') + $results = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $results->expects($this->any())->method('getParams') ->will($this->returnValue($params)); @@ -255,7 +255,7 @@ class SwitchQueryTest extends \VuFindTest\Unit\TestCase */ protected function getMockParams($query = '', $type = 'basic') { - $params = $this->getMockBuilder('VuFind\Search\Solr\Params') + $params = $this->getMockBuilder(\VuFind\Search\Solr\Params::class) ->disableOriginalConstructor()->getMock(); $params->expects($this->any())->method('getDisplayQuery') ->will($this->returnValue($query)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php index 815f3bf47d778559aea76f08fbd2d874ea65be77..3afbad93a918fed84c0f9b67554ac88b4fdb697c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/CacheTest.php @@ -257,7 +257,7 @@ class CacheTest extends TestCase return false; }; - $recordTable = $this->getMockBuilder('VuFind\Db\Table\Record') + $recordTable = $this->getMockBuilder(\VuFind\Db\Table\Record::class) ->disableOriginalConstructor()->getMock(); $recordTable->method('findRecords') ->will($this->returnCallback($findRecordsCallback)); @@ -325,7 +325,7 @@ class CacheTest extends TestCase */ protected function getDriver($id = 'test', $source = 'Solr') { - $driver = $this->createMock('VuFind\RecordDriver\AbstractBase'); + $driver = $this->createMock(\VuFind\RecordDriver\AbstractBase::class); $driver->expects($this->any()) ->method('getUniqueId') ->will($this->returnValue($id)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php index fa903566315d9fb8c57767fc8372de58a2c9373d..6e61abdf37c850c46a616a78c4bb58bef7416932 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/LoaderTest.php @@ -28,9 +28,12 @@ */ namespace VuFindTest\Record; +use VuFind\Record\Cache; +use VuFind\Record\FallbackLoader\PluginManager as FallbackLoader; use VuFind\Record\Loader; use VuFind\RecordDriver\AbstractBase as RecordDriver; use VuFind\RecordDriver\PluginManager as RecordFactory; +use VuFindSearch\ParamBag; use VuFindSearch\Response\RecordCollectionInterface; use VuFindSearch\Service as SearchService; use VuFindTest\Unit\TestCase as TestCase; @@ -57,7 +60,7 @@ class LoaderTest extends TestCase public function testMissingRecord() { $collection = $this->getCollection([]); - $service = $this->createMock('VuFindSearch\Service'); + $service = $this->createMock(\VuFindSearch\Service::class); $service->expects($this->once())->method('retrieve') ->with($this->equalTo('Solr'), $this->equalTo('test')) ->will($this->returnValue($collection)); @@ -65,6 +68,24 @@ class LoaderTest extends TestCase $loader->load('test'); } + /** + * Test that the fallback loader gets called successfully for a missing record. + * + * @return void + */ + public function testMissingRecordWithFallback() + { + $collection = $this->getCollection([]); + $service = $this->createMock(\VuFindSearch\Service::class); + $service->expects($this->once())->method('retrieve') + ->with($this->equalTo('Summon'), $this->equalTo('test')) + ->will($this->returnValue($collection)); + $driver = $this->getDriver(); + $fallbackLoader = $this->getFallbackLoader([$driver]); + $loader = $this->getLoader($service, null, null, $fallbackLoader); + $this->assertEquals($driver, $loader->load('test', 'Summon')); + } + /** * Test "tolerate missing records" feature. * @@ -73,12 +94,12 @@ class LoaderTest extends TestCase public function testToleratedMissingRecord() { $collection = $this->getCollection([]); - $service = $this->createMock('VuFindSearch\Service'); + $service = $this->createMock(\VuFindSearch\Service::class); $service->expects($this->once())->method('retrieve') ->with($this->equalTo('Solr'), $this->equalTo('test')) ->will($this->returnValue($collection)); $missing = $this->getDriver('missing', 'Missing'); - $factory = $this->createMock('VuFind\RecordDriver\PluginManager'); + $factory = $this->createMock(\VuFind\RecordDriver\PluginManager::class); $factory->expects($this->once())->method('get') ->with($this->equalTo('Missing')) ->will($this->returnValue($missing)); @@ -96,7 +117,7 @@ class LoaderTest extends TestCase { $driver = $this->getDriver(); $collection = $this->getCollection([$driver]); - $service = $this->createMock('VuFindSearch\Service'); + $service = $this->createMock(\VuFindSearch\Service::class); $service->expects($this->once())->method('retrieve') ->with($this->equalTo('Solr'), $this->equalTo('test')) ->will($this->returnValue($collection)); @@ -104,6 +125,30 @@ class LoaderTest extends TestCase $this->assertEquals($driver, $loader->load('test')); } + /** + * Test single record with backend parameters. + * + * @return void + */ + public function testSingleRecordWithBackendParameters() + { + $params = new ParamBag(); + $params->set('fq', 'id:test'); + + $driver = $this->getDriver(); + $collection = $this->getCollection([$driver]); + $service = $this->createMock(\VuFindSearch\Service::class); + $service->expects($this->once())->method('retrieve') + ->with( + $this->equalTo('Solr'), + $this->equalTo('test'), + $this->equalTo($params) + ) + ->will($this->returnValue($collection)); + $loader = $this->getLoader($service); + $this->assertEquals($driver, $loader->load('test', 'Solr', false, $params)); + } + /** * Test batch load. * @@ -120,20 +165,35 @@ class LoaderTest extends TestCase $collection2 = $this->getCollection([$driver3]); $collection3 = $this->getCollection([]); - $factory = $this->createMock('VuFind\RecordDriver\PluginManager'); + $solrParams = new ParamBag(); + $solrParams->set('fq', 'id:test1'); + + $worldCatParams = new ParamBag(); + $worldCatParams->set('fq', 'id:test4'); + + $factory = $this->createMock(\VuFind\RecordDriver\PluginManager::class); $factory->expects($this->once())->method('get') ->with($this->equalTo('Missing')) ->will($this->returnValue($missing)); - $service = $this->createMock('VuFindSearch\Service'); + $service = $this->createMock(\VuFindSearch\Service::class); $service->expects($this->at(0))->method('retrieveBatch') - ->with($this->equalTo('Solr'), $this->equalTo(['test1', 'test2'])) + ->with( + $this->equalTo('Solr'), $this->equalTo(['test1', 'test2']), + $this->equalTo($solrParams) + ) ->will($this->returnValue($collection1)); $service->expects($this->at(1))->method('retrieveBatch') - ->with($this->equalTo('Summon'), $this->equalTo(['test3'])) + ->with( + $this->equalTo('Summon'), $this->equalTo(['test3']), + $this->equalTo(null) + ) ->will($this->returnValue($collection2)); $service->expects($this->at(2))->method('retrieveBatch') - ->with($this->equalTo('WorldCat'), $this->equalTo(['test4'])) + ->with( + $this->equalTo('WorldCat'), $this->equalTo(['test4']), + $this->equalTo($worldCatParams) + ) ->will($this->returnValue($collection3)); $loader = $this->getLoader($service, $factory); @@ -141,7 +201,57 @@ class LoaderTest extends TestCase ['source' => 'Solr', 'id' => 'test1'], 'Solr|test2', 'Summon|test3', 'WorldCat|test4' ]; - $this->assertEquals([$driver1, $driver2, $driver3, $missing], $loader->loadBatch($input)); + $this->assertEquals( + [$driver1, $driver2, $driver3, $missing], + $loader->loadBatch( + $input, false, ['Solr' => $solrParams, 'WorldCat' => $worldCatParams] + ) + ); + } + + /** + * Test batch load with fallback loader. + * + * @return void + */ + public function testBatchLoadWithFallback() + { + $driver1 = $this->getDriver('test1', 'Solr'); + $driver2 = $this->getDriver('test2', 'Solr'); + $driver3 = $this->getDriver('test3', 'Summon'); + + $collection1 = $this->getCollection([$driver1, $driver2]); + $collection2 = $this->getCollection([]); + + $solrParams = new ParamBag(); + $solrParams->set('fq', 'id:test1'); + + $service = $this->createMock(\VuFindSearch\Service::class); + $service->expects($this->at(0))->method('retrieveBatch') + ->with( + $this->equalTo('Solr'), $this->equalTo(['test1', 'test2']), + $this->equalTo($solrParams) + ) + ->will($this->returnValue($collection1)); + $service->expects($this->at(1))->method('retrieveBatch') + ->with( + $this->equalTo('Summon'), $this->equalTo(['test3']), + $this->equalTo(null) + ) + ->will($this->returnValue($collection2)); + + $fallbackLoader = $this->getFallbackLoader([$driver3]); + $loader = $this->getLoader($service, null, null, $fallbackLoader); + $input = [ + ['source' => 'Solr', 'id' => 'test1'], + 'Solr|test2', 'Summon|test3' + ]; + $this->assertEquals( + [$driver1, $driver2, $driver3], + $loader->loadBatch( + $input, false, ['Solr' => $solrParams] + ) + ); } /** @@ -154,7 +264,7 @@ class LoaderTest extends TestCase */ protected function getDriver($id = 'test', $source = 'Solr') { - $driver = $this->createMock('VuFind\RecordDriver\AbstractBase'); + $driver = $this->createMock(\VuFind\RecordDriver\AbstractBase::class); $driver->expects($this->any())->method('getUniqueId') ->will($this->returnValue($id)); $driver->expects($this->any())->method('getSourceIdentifier') @@ -165,17 +275,55 @@ class LoaderTest extends TestCase /** * Build a loader to test. * - * @param SearchService $service Search service - * @param RecordFactory $factory Record factory (optional) + * @param SearchService $service Search service + * @param RecordFactory $factory Record factory (optional) + * @param Cache $recordCache Record Cache + * @param FallbackLoader $fallbackLoader Fallback record loader * * @return Loader */ - protected function getLoader(SearchService $service, RecordFactory $factory = null) - { + protected function getLoader(SearchService $service, + RecordFactory $factory = null, Cache $recordCache = null, + FallbackLoader $fallbackLoader = null + ) { if (null === $factory) { - $factory = $this->createMock('VuFind\RecordDriver\PluginManager'); + $factory = $this->createMock(\VuFind\RecordDriver\PluginManager::class); } - return new Loader($service, $factory); + return new Loader($service, $factory, $recordCache, $fallbackLoader); + } + + /** + * Get a fallback loader (currently assumes Summon plugin will be used). + * + * @param array $records Records to return from the fallback plugin + * + * @return FallbackLoader + */ + protected function getFallbackLoader($records) + { + $fallbackPlugin = $this + ->getMockBuilder(\VuFind\Record\FallbackLoader\Summon::class) + ->disableOriginalConstructor() + ->setMethods(['load']) + ->getMock(); + $callback = function ($r) { + return $r->getUniqueId(); + }; + $expectedIds = array_map($callback, $records); + $fallbackPlugin->expects($this->once())->method('load') + ->with($this->equalTo($expectedIds)) + ->will($this->returnValue($records)); + $fallbackLoader = $this->getMockBuilder(FallbackLoader::class) + ->disableOriginalConstructor() + ->setMethods(['get', 'has']) + ->getMock(); + $fallbackLoader->expects($this->once())->method('has') + ->with($this->equalTo('Summon')) + ->will($this->returnValue(true)); + $fallbackLoader->expects($this->once())->method('get') + ->with($this->equalTo('Summon')) + ->will($this->returnValue($fallbackPlugin)); + return $fallbackLoader; } /** @@ -187,7 +335,7 @@ class LoaderTest extends TestCase */ protected function getCollection($records) { - $collection = $this->createMock('VuFindSearch\Response\RecordCollectionInterface'); + $collection = $this->createMock(\VuFindSearch\Response\RecordCollectionInterface::class); $collection->expects($this->any())->method('getRecords')->will($this->returnValue($records)); $collection->expects($this->any())->method('count')->will($this->returnValue(count($records))); return $collection; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php index 8d52579f18c0455c1275c20e1285e48941ee37ca..accb462fa82b8b6b66c2b4c387d7b393e10ecd9a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Record/RouterTest.php @@ -52,9 +52,15 @@ class RouterTest extends TestCase public function testRoutingWithDriver() { $driver = $this->getDriver(); - $router = $this->getRouter($driver); + $router = $this->getRouter(); $this->assertEquals( - ['params' => ['id' => 'test'], 'route' => 'record'], + [ + 'params' => ['id' => 'test'], + 'route' => 'record', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getRouteDetails($driver) ); } @@ -68,7 +74,13 @@ class RouterTest extends TestCase { $router = $this->getRouter(); $this->assertEquals( - ['params' => ['id' => 'test'], 'route' => 'summonrecord'], + [ + 'params' => ['id' => 'test'], + 'route' => 'summonrecord', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getRouteDetails('Summon|test') ); } @@ -82,7 +94,13 @@ class RouterTest extends TestCase { $router = $this->getRouter(); $this->assertEquals( - ['params' => ['id' => 'test', 'tab' => 'foo'], 'route' => 'summonrecord'], + [ + 'params' => ['id' => 'test', 'tab' => 'foo'], + 'route' => 'summonrecord', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getTabRouteDetails('Summon|test', 'foo') ); } @@ -94,15 +112,40 @@ class RouterTest extends TestCase */ public function testCollectionSpecialCaseWithString() { - $driver = $this->getDriver(); - $driver->expects($this->once())->method('tryMethod')->with($this->equalTo('isCollection'))->will($this->returnValue(true)); - $router = $this->getRouter($driver, ['Collections' => ['collections' => true]]); + $router = $this->getRouter(['Collections' => ['collections' => true]]); $this->assertEquals( - ['params' => ['id' => 'test', 'tab' => 'foo'], 'route' => 'collection'], + [ + 'params' => ['id' => 'test', 'tab' => 'foo'], + 'route' => 'record', + 'options' => [ + 'normalize_path' => false, + 'query' => ['checkRoute' => 1] + ] + ], $router->getTabRouteDetails('Solr|test', 'foo') ); } + /** + * Test routing with source|id string including percent signs. + * + * @return void + */ + public function testRoutingWithIDContainingPercent() + { + $router = $this->getRouter(); + $this->assertEquals( + [ + 'params' => ['id' => 'test%2Fsub'], + 'route' => 'record', + 'options' => [ + 'normalize_path' => false + ] + ], + $router->getRouteDetails('Solr|test%2Fsub') + ); + } + /** * Test collection special case with id string having no source prefix. * @@ -110,11 +153,16 @@ class RouterTest extends TestCase */ public function testCollectionSpecialCaseWithStringMissingSource() { - $driver = $this->getDriver(); - $driver->expects($this->once())->method('tryMethod')->with($this->equalTo('isCollection'))->will($this->returnValue(true)); - $router = $this->getRouter($driver, ['Collections' => ['collections' => true]]); + $router = $this->getRouter(['Collections' => ['collections' => true]]); $this->assertEquals( - ['params' => ['id' => 'test', 'tab' => 'foo'], 'route' => 'collection'], + [ + 'params' => ['id' => 'test', 'tab' => 'foo'], + 'route' => 'record', + 'options' => [ + 'normalize_path' => false, + 'query' => ['checkRoute' => 1] + ] + ], $router->getTabRouteDetails('test', 'foo') ); } @@ -128,9 +176,15 @@ class RouterTest extends TestCase { $driver = $this->getDriver(); $driver->expects($this->once())->method('tryMethod')->with($this->equalTo('isCollection'))->will($this->returnValue(true)); - $router = $this->getRouter($driver, ['Collections' => ['collections' => true]]); + $router = $this->getRouter(['Collections' => ['collections' => true]]); $this->assertEquals( - ['params' => ['id' => 'test', 'tab' => 'foo'], 'route' => 'collection'], + [ + 'params' => ['id' => 'test', 'tab' => 'foo'], + 'route' => 'collection', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getTabRouteDetails($driver, 'foo') ); } @@ -144,7 +198,13 @@ class RouterTest extends TestCase { $router = $this->getRouter(); $this->assertEquals( - ['params' => ['id' => 'test'], 'route' => 'record'], + [ + 'params' => ['id' => 'test'], + 'route' => 'record', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getRouteDetails('test') ); } @@ -157,9 +217,15 @@ class RouterTest extends TestCase public function testActionRoutingWithDriver() { $driver = $this->getDriver(); - $router = $this->getRouter($driver); + $router = $this->getRouter(); $this->assertEquals( - ['params' => ['id' => 'test'], 'route' => 'record-sms'], + [ + 'params' => ['id' => 'test'], + 'route' => 'record-sms', + 'options' => [ + 'normalize_path' => false + ] + ], $router->getActionRouteDetails($driver, 'SMS') ); } @@ -174,7 +240,7 @@ class RouterTest extends TestCase */ protected function getDriver($id = 'test', $source = 'Solr') { - $driver = $this->createMock('VuFind\RecordDriver\AbstractBase'); + $driver = $this->createMock(\VuFind\RecordDriver\AbstractBase::class); $driver->expects($this->any())->method('getUniqueId') ->will($this->returnValue($id)); $driver->expects($this->any())->method('getSourceIdentifier') @@ -185,26 +251,12 @@ class RouterTest extends TestCase /** * Get test router object * - * @param RecordDriver $record Record to return from loader. - * @param array $config Configuration. + * @param array $config Configuration. * * @return Router */ - protected function getRouter($record = null, $config = []) + protected function getRouter($config = []) { - if (null === $record) { - $record = $this->getDriver(); - } - $loader = $this->getMockBuilder('VuFind\Record\Loader') - ->setConstructorArgs( - [ - $this->createMock('VuFindSearch\Service'), - $this->createMock('VuFind\RecordDriver\PluginManager') - ] - )->getMock(); - $loader->expects($this->any())->method('load') - ->will($this->returnValue($record)); - - return new Router($loader, new Config($config)); + return new Router(new Config($config)); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/DefaultRecordTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/DefaultRecordTest.php index a119ecf76c682d7192ba99ef8ef95ad16494033b..06a655e8230b5eb5eb5e3656e4d42671fc714112 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/DefaultRecordTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/DefaultRecordTest.php @@ -30,6 +30,7 @@ namespace VuFindTest\RecordDriver; use VuFind\RecordDriver\DefaultRecord; use VuFind\RecordDriver\Response\PublicationDetails; +use Zend\Config\Config; /** * DefaultRecord Record Driver Test Class @@ -383,14 +384,52 @@ class DefaultRecordTest extends \VuFindTest\Unit\TestCase $this->assertEquals($breadcrumb, $this->getDriver()->getBreadcrumb()); } + /** + * Test citation behavior. + * + * @return void + */ + public function testCitationBehavior() + { + // The DefaultRecord driver should have some supported formats: + $driver = $this->getDriver(); + $supported = $this->callMethod($driver, 'getSupportedCitationFormats'); + $this->assertNotEmpty($supported); + + // By default, all supported formats should be enabled: + $this->assertEquals($supported, $driver->getCitationFormats()); + + // Data table (citation_formats config, expected result): + $tests = [ + // No results: + [false, []], + ['false', []], + // All results: + [true, $supported], + ['true', $supported], + // Filtered results: + ['MLA,foo', ['MLA']], + ['bar , APA,MLA', ['APA', 'MLA']], + ]; + foreach ($tests as $current) { + list($input, $output) = $current; + $cfg = new Config(['Record' => ['citation_formats' => $input]]); + $this->assertEquals( + $output, + array_values($this->getDriver([], $cfg)->getCitationFormats()) + ); + } + } + /** * Get a record driver with fake data. * - * @param array $overrides Fixture fields to override. + * @param array $overrides Fixture fields to override. + * @param Config $mainConfig Main configuration (optional). * * @return SolrDefault */ - protected function getDriver($overrides = []) + protected function getDriver($overrides = [], Config $mainConfig = null) { $fixture = json_decode( file_get_contents( @@ -401,7 +440,7 @@ class DefaultRecordTest extends \VuFindTest\Unit\TestCase true ); - $record = new DefaultRecord(); + $record = new DefaultRecord($mainConfig); $record->setRawData($overrides + $fixture['response']['docs'][0]); return $record; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php index 27e833205ced22be4226e19f1283730bf5aab439..77dd971ab6f69a9f04f6b143bf461dc796ce04c5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordDriver/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php index df6b526337ba404d5bf6285dbafa4931c64a99f6..dfe106bbe6af6f61fbbea19de4ec47a87d278938 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ff3c31f0e40db8c34fb2bcaaf9e9a00e175fffba --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php @@ -0,0 +1,172 @@ +<?php +/** + * RecordTab Manager Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\RecordTab; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\RecordTab\PluginManager; +use VuFind\RecordTab\TabManager; + +/** + * RecordTab Manager Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class TabManagerTest extends \VuFindTest\Unit\TestCase +{ + /** + * Set up a tab manager for testing. + * + * @param PluginManager $pluginManager Plugin manager to use (null for default) + * @param ConfigManager $configManager Config manager to use (null for default) + * @return TabManager + */ + protected function getTabManager(PluginManager $pluginManager = null, + ConfigManager $configManager = null + ) { + $legacyConfig = [ + 'vufind' => [ + 'recorddriver_collection_tabs' => [ + 'VuFind\RecordDriver\AbstractBase' => [ + 'tabs' => [ + 'coll' => 'ection', + ], + 'defaultTab' => null, + ], + ], + 'recorddriver_tabs' => [ + 'VuFind\RecordDriver\AbstractBase' => [ + 'tabs' => [ + 'foo' => 'bar', + ], + 'defaultTab' => null, + ], + ], + ], + ]; + return new TabManager( + $pluginManager ?? $this->getMockPluginManager(), + $configManager ?? $this->getMockConfigManager(), + $legacyConfig + ); + } + + /** + * Build a mock plugin manager. + * + * @return PluginManager + */ + protected function getMockPluginManager() + { + $mockTab = $this->getMockBuilder(\VuFind\RecordTab\StaffViewArray::class) + ->disableOriginalConstructor()->setMethods(['isActive'])->getMock(); + $mockTab->expects($this->any())->method('isActive') + ->will($this->returnValue(true)); + $pm = $this->getMockBuilder(\VuFind\RecordTab\PluginManager::class) + ->disableOriginalConstructor()->getMock(); + $pm->expects($this->any())->method('has') + ->will($this->returnValue(true)); + $pm->expects($this->any())->method('get') + ->will($this->returnValue($mockTab)); + return $pm; + } + + /** + * Build a mock config manager. + * + * @return ConfigManager + */ + protected function getMockConfigManager() + { + $iniConfig = new \Zend\Config\Config( + [ + 'VuFind\RecordDriver\EDS' => [ + 'tabs' => [ + 'xyzzy' => 'yzzyx', + 'zip' => 'line', + ], + 'defaultTab' => 'zip', + 'backgroundLoadedTabs' => ['xyzzy'], + ], + ] + ); + $configManager = $this->getMockBuilder(\VuFind\Config\PluginManager::class) + ->disableOriginalConstructor() + ->setMethods(['has', 'get']) + ->getMock(); + $configManager->expects($this->any())->method('has') + ->will($this->returnValue(true)); + $configManager->expects($this->any())->method('get') + ->will($this->returnValue($iniConfig)); + return $configManager; + } + + /** + * Test that we get the expected tab service names. + * + * @return void + */ + public function testGetTabDetailsForRecord() + { + $tabManager = $this->getTabManager(); + $driver1 = $this->getMockBuilder(\VuFind\RecordDriver\EDS::class) + ->disableOriginalConstructor()->getMock(); + $details1 = $tabManager->getTabDetailsForRecord($driver1); + $this->assertEquals('zip', $details1['default']); + $this->assertEquals(['xyzzy', 'zip'], array_keys($details1['tabs'])); + $driver2 = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->disableOriginalConstructor()->getMock(); + $details2 = $tabManager->getTabDetailsForRecord($driver2); + $this->assertEquals('foo', $details2['default']); + $this->assertEquals(['foo'], array_keys($details2['tabs'])); + // Switch to collection mode to load a different configuration: + $tabManager->setContext('collection'); + $details2b = $tabManager->getTabDetailsForRecord($driver2); + $this->assertEquals('coll', $details2b['default']); + $this->assertEquals(['coll'], array_keys($details2b['tabs'])); + } + + /** + * Test getBackgroundTabNames. + * + * @return void + */ + public function testGetBackgroundTabNames() + { + $tabManager = $this->getTabManager(); + $driver1 = $this->getMockBuilder(\VuFind\RecordDriver\EDS::class) + ->disableOriginalConstructor()->getMock(); + $this->assertEquals(['xyzzy'], $tabManager->getBackgroundTabNames($driver1)); + $driver2 = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->disableOriginalConstructor()->getMock(); + $this->assertEquals([], $tabManager->getBackgroundTabNames($driver2)); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php index 8c15dd964e5aa6e628773729ca1fbc7a7cfa1236..1da043032ecdc53dc9dca5c4e0b4350e95751cec 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php index 2edd9b59ea248e33edc85f9b05f2da9d966e9237..339982f197ff49ce7e239e19a912ee9e8f1ab854 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php @@ -49,13 +49,13 @@ class SimilarTest extends \VuFindTest\Unit\TestCase { // Similar is really just a thin wrapper around the search service; make // sure it does its job properly with the help of some mocks. - $driver = $this->getMockBuilder('VuFind\RecordDriver\SolrDefault') + $driver = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) ->setMethods(['getUniqueId']) ->getMock(); $driver->expects($this->once()) ->method('getUniqueId') ->will($this->returnValue('fakeid')); - $service = $this->getMockBuilder('VuFindSearch\Service') + $service = $this->getMockBuilder(\VuFindSearch\Service::class) ->setMethods(['similar']) ->getMock(); $service->expects($this->once()) diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php index 83ca8b1ec20c00494349aff1d6daf22a51763dae..30d0a350250d663171192cc07c34b1490e522e50 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/WorldCatSimilarTest.php @@ -48,7 +48,7 @@ class WorldCatSimilarTest extends \VuFindTest\Unit\TestCase */ public function testGetResults() { - $driver = $this->getMockBuilder('VuFind\RecordDriver\WorldCat') + $driver = $this->getMockBuilder(\VuFind\RecordDriver\WorldCat::class) ->setMethods(['tryMethod', 'getPrimaryAuthor', 'getAllSubjectHeadings', 'getTitle', 'getUniqueId', 'getSourceIdentifier']) ->getMock(); $driver->expects($this->once()) @@ -70,10 +70,10 @@ class WorldCatSimilarTest extends \VuFindTest\Unit\TestCase $driver->expects($this->once()) ->method('getSourceIdentifier') ->will($this->returnValue('WorldCat')); - $service = $this->getMockBuilder('VuFindSearch\Service') + $service = $this->getMockBuilder(\VuFindSearch\Service::class) ->setMethods(['search'])->getMock(); $expectedQuery = new Query('(srw.dd any "fakedc" or srw.au all "fakepa" or srw.su all "fakesh1a fakesh1b" or srw.su all "fakesh2" or srw.ti any "faketitle") not srw.no all "fakeid"'); - $response = $this->getMockBuilder('VuFindSearch\Backend\WorldCat\Response\XML\RecordCollection') + $response = $this->getMockBuilder(\VuFindSearch\Backend\WorldCat\Response\XML\RecordCollection::class) ->setMethods(['getRecords']) ->setConstructorArgs([['offset' => 0, 'total' => 0]]) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/AlmaTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/AlmaTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3a51447d73811a271a0d1e8d0b205400c26aaeee --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/AlmaTest.php @@ -0,0 +1,196 @@ +<?php +/** + * Alma resolver driver test + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2015. + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Resolver\Driver; + +use InvalidArgumentException; + +use VuFind\Resolver\Driver\Alma; +use Zend\Http\Client\Adapter\Test as TestAdapter; + +use Zend\Http\Response as HttpResponse; + +/** + * Alma resolver driver test + * + * @category VuFind + * @package Tests + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Demian Katz <demian.katz@villanova.edu> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class AlmaTest extends \VuFindTest\Unit\TestCase +{ + /** + * Test-Config + * + * @var array + */ + protected $openUrlConfig = [ + 'OpenURL' => [ + 'url' => "http://na01.alma.exlibrisgroup.com/view/uresolver/TR_INTEGRATION_INST/openurl?debug=true&u.ignore_date_coverage=true&rft.mms_id=9942811800561&rfr_id=info:sid/primo.exlibrisgroup.com&svc_dat=CTO", + 'rfr_id' => "vufind.svn.sourceforge.net", + 'resolver' => "alma", + '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, + 'show_in_record' => false, + 'show_in_holdings' => true, + 'embed' => true, + 'replace_other_urls' => true + ], + ]; + + /** + * Test + * + * @return void + */ + public function testParseLinks() + { + $conn = $this->createConnector('alma.xml'); + + $openUrl = "url_ver=Z39.88-2004&ctx_ver=Z39.88-2004"; + $result = $conn->parseLinks($conn->fetchLinks($openUrl)); + + $testResult = [ + 0 => [ + 'title' => 'Ebook override', + 'coverage' => 'Available from 2019', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861830000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getFullTxt', + ], + 1 => [ + 'title' => 'ebrary Academic Complete Subscription UKI Edition', + 'coverage' => '', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861800000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getFullTxt', + ], + 2 => [ + 'title' => 'ebrary Science & Technology Subscription', + 'coverage' => '', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861790000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getFullTxt', + ], + 3 => [ + 'title' => 'EBSCOhost Academic eBook Collection (North America)', + 'coverage' => '', + 'access' => 'open', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861770000561&institutionId=561&customerId=550', + 'notes' => 'notessssssssssss SERVICE LEVEL PUBLIC NOTE', + 'authentication' => 'collection level auth SERVICE LEVEL AUTHE NOTE', + 'service_type' => 'getFullTxt', + ], + 4 => [ + 'title' => 'EBSCOhost eBook Community College Collection', + 'coverage' => '', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861780000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getHolding', + ], + 5 => [ + 'title' => 'Elsevier ScienceDirect Books', + 'coverage' => '', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861820000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getFullTxt', + ], + 6 => [ + 'title' => 'Request Assistance for this Resource!', + 'coverage' => '', + 'access' => '', + 'href' => 'https://www.google.com/search?Testingrft.oclcnum=437189463&q=Fundamental+Data+Compression&rft.archive=9942811800561', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getWebService', + ], + 7 => [ + 'title' => 'ProQuest Safari Tech Books Online', + 'coverage' => '', + 'access' => 'limited', + 'href' => 'https://na01.alma.exlibrisgroup.com/view/action/uresolver.do?operation=resolveService&package_service_id=5687861810000561&institutionId=561&customerId=550', + 'notes' => '', + 'authentication' => '', + 'service_type' => 'getFullTxt', + ], + ]; + + $this->assertEquals($result, $testResult); + } + + /** + * Create connector with fixture file. + * + * @param string $fixture Fixture file + * + * @return Connector + * + * @throws InvalidArgumentException Fixture file does not exist + */ + protected function createConnector($fixture = null) + { + $adapter = new TestAdapter(); + if ($fixture) { + $file = realpath( + __DIR__ . + '/../../../../../../tests/fixtures/resolver/response/' . $fixture + ); + if (!is_string($file) || !file_exists($file) || !is_readable($file)) { + throw new InvalidArgumentException( + sprintf('Unable to load fixture file: %s ', $file) + ); + } + $response = file_get_contents($file); + $responseObj = HttpResponse::fromString($response); + $adapter->setResponse($responseObj); + } + $_SERVER['REMOTE_ADDR'] = "127.0.0.1"; + + $client = new \Zend\Http\Client(); + $client->setAdapter($adapter); + + $conn = new Alma($this->openUrlConfig['OpenURL']['url'], $client); + return $conn; + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php index 26d7391bfcc32ce625b4994ef28730465ab41920..44369aced8f110d8f9a9ed6582d3053a86f1ca8c 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Resolver/Driver/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php index acaedee09ac977c6397084b8cc0fc8c79114219f..4c1bdbc072286bf475a2f379bdc180caa37d18cf 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/DynamicRoleProviderTest.php @@ -103,7 +103,7 @@ class DynamicRoleProviderTest extends \VuFindTest\Unit\TestCase { $pm = new PluginManager($this->getServiceManager()); foreach (['a', 'b', 'c'] as $name) { - $pm->setService($name, $this->createMock('VuFind\Role\PermissionProvider\PermissionProviderInterface')); + $pm->setService($name, $this->createMock(\VuFind\Role\PermissionProvider\PermissionProviderInterface::class)); } return $pm; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php index 64cd58bbf554fdd60814980e7ce460a3774e9d0b..d5845f1b6a74a9ed725cffcda7e049bae583be4b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php @@ -103,7 +103,7 @@ class PermissionManagerTest extends \VuFindTest\Unit\TestCase public function testGrantedPermission() { $pm = new PermissionManager($this->permissionConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -121,7 +121,7 @@ class PermissionManagerTest extends \VuFindTest\Unit\TestCase public function testDeniedPermission() { $pm = new PermissionManager($this->permissionConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php index 7827ba0082c2c61207bbd29ea1d68562e9ca68d5..d56ae165f6bd84201c2f0c4d076e6356afa4b8af 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php index d7db63f15b97e423715b7af3b5b98736c7b9060f..077f20fcf34c03a0b7f875f06600976d32177bce 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionProvider/UserTest.php @@ -124,7 +124,7 @@ class UserTest extends \VuFindTest\Unit\TestCase protected function getMockAuthorizationService() { $authorizationService - = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $authorizationService @@ -141,7 +141,7 @@ class UserTest extends \VuFindTest\Unit\TestCase */ protected function getMockUser() { - $user = $this->getMockBuilder('VuFind\Db\Row\User') + $user = $this->getMockBuilder(\VuFind\Db\Row\User::class) ->disableOriginalConstructor() ->getMock(); $user->method('__get') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php index a6e1eb12ebe61d6f846cbe7e112d3436c04a8991..33febe51c4704bc9932618a511dadd714ed7b422 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/SMS/ClickatellTest.php @@ -195,6 +195,6 @@ class ClickatellTest extends \VuFindTest\Unit\TestCase */ protected function getMockClient() { - return $this->createMock('Zend\Http\Client'); + return $this->createMock(\Zend\Http\Client::class); } } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php index 2ff6af9f3c189f6402a984ef65a3070a0d76f8ce..546591935456b2c0722debbc95a8f57d927ab28e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Base/ParamsTest.php @@ -50,7 +50,7 @@ class ParamsTest extends \VuFindTest\Unit\TestCase { // Use Solr since some Base components are abstract: $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); // Key test: word boundaries: $params->setBasicSearch('go good googler'); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/HistoryTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/HistoryTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f8eaa45234fbaa6f9f770e1a7660cc64aac876d7 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/HistoryTest.php @@ -0,0 +1,135 @@ +<?php + +/** + * History unit tests. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Search; + +use VuFind\Search\History; +use VuFindTest\Unit\TestCase as TestCase; + +/** + * History unit tests. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class HistoryTest extends TestCase +{ + /** + * Test that we get no schedule options when scheduled search is disabled + * (by default). + * + * @return void + */ + public function testDefaultDisabledScheduleOptions() + { + $this->assertEquals([], $this->getHistory()->getScheduleOptions()); + } + + /** + * Test that we get no schedule options when scheduled search is disabled + * (explicitly). + * + * @return void + */ + public function testExplicitlyDisabledScheduleOptions() + { + $config = new \Zend\Config\Config( + [ + 'Account' => [ + 'schedule_searches' => false, + ] + ] + ); + $history = $this->getHistory(null, null, $config); + $this->assertEquals([], $history->getScheduleOptions()); + } + + /** + * Test that we get reasonable default schedule options when scheduled search + * is enabled. + * + * @return void + */ + public function testDefaultScheduleOptions() + { + $config = new \Zend\Config\Config( + [ + 'Account' => [ + 'schedule_searches' => true, + ] + ] + ); + $history = $this->getHistory(null, null, $config); + $this->assertEquals( + [0 => 'schedule_none', 1 => 'schedule_daily', 7 => 'schedule_weekly'], + $history->getScheduleOptions() + ); + } + + /** + * Test that purging history proxies to the right place. + * + * @return void + */ + public function testPurgeHistory() + { + $table = $this->getMockBuilder(\VuFind\Db\Table\Search::class) + ->disableOriginalConstructor()->setMethods(['destroySession']) + ->getMock(); + $table->expects($this->once())->method('destroySession') + ->with($this->equalTo('foosession'), $this->equalTo(1234)); + $history = $this->getHistory($table); + $history->purgeSearchHistory(1234); + } + + /** + * Get object for testing. + * + * @param \VuFind\Db\Table\Search $searchTable Search table + * @param \VuFind\Search\Results\PluginManager $resultsManager Results manager + * @param \Zend\Config\Config $config Configuration + * + * @return History + */ + protected function getHistory($searchTable = null, + $resultsManager = null, \Zend\Config\Config $config = null + ) { + return new History( + $searchTable ?: $this->getMockBuilder(\VuFind\Db\Table\Search::class) + ->disableOriginalConstructor()->getMock(), + 'foosession', + $resultsManager ?: $this + ->getMockBuilder(\VuFind\Search\Results\PluginManager::class) + ->disableOriginalConstructor()->getMock(), + $config + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php index 3bca379c3238acdf1eee6b14bd4b1feeb0d8e9b6..4cd57405f2e66bdc908c015a56158d2f40ae3cb6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Options/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php index 152c368493d2da0763f9eba34bf5b9d291cc292c..e4621b55386509e2f3144f04126b535e77a5012b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Params/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php index 4d4840d1802f51937fa819f1e50bb5cae2048be1..c187af11321bd97e48e6ea3a9942eaad2ea94e0d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/OnCampusListenerTest.php @@ -73,7 +73,7 @@ class OnCampusListenerTest extends TestCase public function testAttach() { $listener = new InjectOnCampusListener(); - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('pre'), @@ -89,11 +89,11 @@ class OnCampusListenerTest extends TestCase */ public function testAttachWithParameter() { - $mockPermController = $this->getMockBuilder('PrimoPermissionHandler') + $mockPermController = $this->getMockBuilder(\PrimoPermissionHandler::class) ->disableOriginalConstructor() ->getMock(); $listener = new InjectOnCampusListener($mockPermController); - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('pre'), @@ -128,8 +128,8 @@ class OnCampusListenerTest extends TestCase public function testOnCampusDefaultSuccessfull() { $params = new ParamBag([ ]); - $mockPermController - = $this->getMockBuilder('VuFind\Search\Primo\PrimoPermissionHandler') + $mockPermController = $this + ->getMockBuilder(\VuFind\Search\Primo\PrimoPermissionHandler::class) ->disableOriginalConstructor() ->getMock(); $mockPermController->expects($this->any())->method('hasPermission') @@ -156,7 +156,7 @@ class OnCampusListenerTest extends TestCase { $params = new ParamBag([ ]); $mockPermController - = $this->getMockBuilder('VuFind\Search\Primo\PrimoPermissionHandler') + = $this->getMockBuilder(\VuFind\Search\Primo\PrimoPermissionHandler::class) ->disableOriginalConstructor() ->getMock(); @@ -179,7 +179,7 @@ class OnCampusListenerTest extends TestCase { $params = new ParamBag([ ]); $mockPermController - = $this->getMockBuilder('VuFind\Search\Primo\PrimoPermissionHandler') + = $this->getMockBuilder(\VuFind\Search\Primo\PrimoPermissionHandler::class) ->disableOriginalConstructor() ->getMock(); $mockPermController->expects($this->any())->method('hasPermission') @@ -203,8 +203,8 @@ class OnCampusListenerTest extends TestCase public function testOnCampusOutsideNetwork() { $params = new ParamBag([ ]); - $mockPermController - = $this->getMockBuilder('VuFind\Search\Primo\PrimoPermissionHandler') + $mockPermController = $this + ->getMockBuilder(\VuFind\Search\Primo\PrimoPermissionHandler::class) ->disableOriginalConstructor() ->getMock(); $mockPermController->expects($this->any())->method('hasPermission') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php index 6bc48e2bcf52a94e87478c5d7bb72d6439895470..92cf6336c4c902bffb1fe57ca5286891a77f8388 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Primo/PrimoPermissionHandlerTest.php @@ -30,6 +30,7 @@ namespace VuFindTest\Search\Primo; use VuFind\Search\Primo\PrimoPermissionHandler; use VuFindTest\Unit\TestCase; +use ZfcRbac\Service\AuthorizationService; /** * Unit tests for Primo Permission Handler. @@ -182,7 +183,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerCodeSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -203,7 +204,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultCode() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -238,7 +239,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerMemberAuthSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -261,7 +262,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerMemberAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -284,7 +285,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultAuthSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -307,7 +308,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -331,7 +332,7 @@ class PrimoPermissionHandlerTest extends TestCase { $handler = new PrimoPermissionHandler($this->primoConfig); $handler->setInstCode('NOTEXISTING'); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -352,7 +353,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultAuthSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -375,7 +376,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -398,7 +399,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultCodeSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -419,7 +420,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultCodeAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -438,7 +439,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultDefaultAuthSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -461,7 +462,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerWithoutDefaultDefaultAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigWithoutDefault); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -486,7 +487,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnly); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -511,7 +512,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyCodeAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnly); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -534,7 +535,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyDefaultAuthSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnly); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -557,7 +558,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyDefaultAuthNotSuccessfull() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnly); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -582,7 +583,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerMemberIsOnCampusWithDefault() { $handler = new PrimoPermissionHandler($this->primoConfigInstitutionCode); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -609,7 +610,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerMemberIsNotOnCampusWithDefault() { $handler = new PrimoPermissionHandler($this->primoConfigInstitutionCode); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -634,7 +635,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerIsNotAMemberAndNotDefaultOnCampus() { $handler = new PrimoPermissionHandler($this->primoConfigInstitutionCode); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -659,7 +660,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerIsNotAMemberButOnDefaultCampus() { $handler = new PrimoPermissionHandler($this->primoConfigInstitutionCode); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -688,7 +689,7 @@ class PrimoPermissionHandlerTest extends TestCase $handler = new PrimoPermissionHandler( $this->primoConfigWithoutDefaultWithInstCode ); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -716,7 +717,7 @@ class PrimoPermissionHandlerTest extends TestCase $handler = new PrimoPermissionHandler( $this->primoConfigWithoutDefaultWithInstCode ); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -742,7 +743,7 @@ class PrimoPermissionHandlerTest extends TestCase $handler = new PrimoPermissionHandler( $this->primoConfigWithoutDefaultWithInstCode ); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -768,7 +769,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyNoOncampus() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnlyNoOnCampusRule); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -788,7 +789,7 @@ class PrimoPermissionHandlerTest extends TestCase public function testHandlerDefaultOnlyCodeNoOncampus() { $handler = new PrimoPermissionHandler($this->primoConfigDefaultOnlyNoOnCampusRule); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php index a68acf7ffce638c3147de38a2f3b75720ad5e8f6..8ab15d6a4ced92eca778f208d1d73ee008d40964 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Results/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertFalse($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php index fe4933a71f68d91f4624d0e5e83e1405e8be0360..1b19096ba3e8dab335c5e62c071c22b45fc47b4d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/SearchTabsHelperTest.php @@ -208,17 +208,17 @@ class SearchTabsHelperTest extends TestCase protected function getSearchTabsHelper($config = 'default_unfiltered', $filters = null ) { - $mockRequest = $this->createMock('Zend\Http\Request'); + $mockRequest = $this->createMock(\Zend\Http\Request::class); $mockRequest->expects($this->any()) ->method('getQuery') ->with($this->equalTo('hiddenFilters')) ->willReturn($filters); - $configManager = $this->createMock('VuFind\Config\PluginManager'); + $configManager = $this->createMock(\VuFind\Config\PluginManager::class); - $mockSolrOptions = $this->getMockBuilder('VuFind\Search\Solr\Options') + $mockSolrOptions = $this->getMockBuilder(\VuFind\Search\Solr\Options::class) ->disableOriginalConstructor()->getMock(); - $mockSolr = $this->getMockBuilder('VuFind\Search\Solr\Results') + $mockSolr = $this->getMockBuilder(\VuFind\Search\Solr\Results::class) ->disableOriginalConstructor()->getMock(); $mockSolr->expects($this->any()) ->method('getParams') @@ -226,9 +226,9 @@ class SearchTabsHelperTest extends TestCase new \VuFind\Search\Solr\Params($mockSolrOptions, $configManager) ); - $mockPrimoOptions = $this->getMockBuilder('VuFind\Search\Primo\Options') + $mockPrimoOptions = $this->getMockBuilder(\VuFind\Search\Primo\Options::class) ->disableOriginalConstructor()->getMock(); - $mockPrimo = $this->getMockBuilder('VuFind\Search\Primo\Results') + $mockPrimo = $this->getMockBuilder(\VuFind\Search\Primo\Results::class) ->disableOriginalConstructor()->getMock(); $mockPrimo->expects($this->any()) ->method('getParams') @@ -236,7 +236,7 @@ class SearchTabsHelperTest extends TestCase new \VuFind\Search\Primo\Params($mockPrimoOptions, $configManager) ); - $mockResults = $this->createMock('VuFind\Search\Results\PluginManager'); + $mockResults = $this->createMock(\VuFind\Search\Results\PluginManager::class); $mockResults->expects($this->any()) ->method('get') ->will( diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php index 37c9013d35144c29b9cd167f710a8b0aacc7f8e8..dffe9fe5068ffac755afb5c51ca6e2937a5425bd 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/ConditionalFilterListenerTest.php @@ -92,7 +92,7 @@ class ConditionalFilterListenerTest extends TestCase public function testAttach() { $listener = new InjectConditionalFilterListener(self::$emptySearchConfig); - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('pre'), @@ -154,7 +154,7 @@ class ConditionalFilterListenerTest extends TestCase { $params = new ParamBag([ ]); $listener = new InjectConditionalFilterListener(self::$emptySearchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $listener->setAuthorizationService($mockAuth); @@ -180,7 +180,7 @@ class ConditionalFilterListenerTest extends TestCase ] ); $listener = new InjectConditionalFilterListener(self::$emptySearchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $listener->setAuthorizationService($mockAuth); @@ -205,7 +205,7 @@ class ConditionalFilterListenerTest extends TestCase { $params = new ParamBag([ ]); $listener = new InjectConditionalFilterListener(self::$searchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -233,7 +233,7 @@ class ConditionalFilterListenerTest extends TestCase $params = new ParamBag([ ]); $listener = new InjectConditionalFilterListener(self::$searchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -262,7 +262,7 @@ class ConditionalFilterListenerTest extends TestCase ); $listener = new InjectConditionalFilterListener(self::$searchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') @@ -296,7 +296,7 @@ class ConditionalFilterListenerTest extends TestCase ); $listener = new InjectConditionalFilterListener(self::$searchConfig); - $mockAuth = $this->getMockBuilder('ZfcRbac\Service\AuthorizationService') + $mockAuth = $this->getMockBuilder(\ZfcRbac\Service\AuthorizationService::class) ->disableOriginalConstructor() ->getMock(); $mockAuth->expects($this->any())->method('isGranted') diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php index faa65233eee3fec33b1f4a4981edcfddaeb58e86..880319e6adb98f37f736d6ae3d9de62789a39204 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/FilterFieldConversionListenerTest.php @@ -53,7 +53,7 @@ class FilterFieldConversionListenerTest extends TestCase public function testAttach() { $listener = new FilterFieldConversionListener(['foo' => 'bar']); - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('pre'), @@ -86,7 +86,7 @@ class FilterFieldConversionListenerTest extends TestCase ['foo' => 'bar', 'baz' => 'boo'] ); - $backend = $this->getMockBuilder('VuFindSearch\Backend\Solr\Backend') + $backend = $this->getMockBuilder(\VuFindSearch\Backend\Solr\Backend::class) ->disableOriginalConstructor()->getMock(); $event = new Event('pre', $backend, ['params' => $params]); $listener->onSearchPre($event); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php index 81c61ad31d1f2d6ad481dd68ab90ce7fea06dd4a..a058e3279141eee71e6de435d35f747e85b77d33 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HideFacetValueListenerTest.php @@ -53,7 +53,7 @@ class HideFacetValueListenerTest extends TestCase */ protected function getMockBackend($id = 'Solr') { - $backend = $this->getMockBuilder('VuFindSearch\Backend\Solr\Backend') + $backend = $this->getMockBuilder(\VuFindSearch\Backend\Solr\Backend::class) ->disableOriginalConstructor()->getMock(); $backend->expects($this->any())->method('getIdentifier')->will( $this->returnValue($id) @@ -120,7 +120,7 @@ class HideFacetValueListenerTest extends TestCase public function testAttach() { $listener = $this->getListener(); - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('post'), diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php index 4df48e826fc7172470409706f29fb4ea9f8c593c..4911c02966f5ec84d8582e3335d547da62c0340d 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/HierarchicalFacetHelperTest.php @@ -43,6 +43,11 @@ use VuFindTest\Unit\TestCase; */ class HierarchicalFacetHelperTest extends TestCase { + /** + * Test input data. + * + * @var array + */ protected $facetList = [ [ 'value' => '0/Book/', @@ -113,9 +118,39 @@ class HierarchicalFacetHelperTest extends TestCase } /** - * Tests for sortFacetList (top level only) + * Tests for sortFacetList (default/count sort -- at present these should + * make no changes to the input data and can thus both be tested in a single + * test method). + * + * @return void */ - public function testSortFacetListTopLevel() + public function testSortFacetListDefault() + { + $facetList = $this->facetList; + $this->helper->sortFacetList($facetList); + $this->assertEquals($facetList[0]['value'], '0/Book/'); + $this->assertEquals($facetList[1]['value'], '0/AV/'); + $this->assertEquals($facetList[2]['value'], '0/Audio/'); + $this->assertEquals($facetList[3]['value'], '1/Book/BookPart/'); + $this->assertEquals($facetList[4]['value'], '1/Book/Section/'); + $this->assertEquals($facetList[5]['value'], '1/Audio/Spoken/'); + $this->assertEquals($facetList[6]['value'], '1/Audio/Music/'); + $this->helper->sortFacetList($facetList, 'count'); + $this->assertEquals($facetList[0]['value'], '0/Book/'); + $this->assertEquals($facetList[1]['value'], '0/AV/'); + $this->assertEquals($facetList[2]['value'], '0/Audio/'); + $this->assertEquals($facetList[3]['value'], '1/Book/BookPart/'); + $this->assertEquals($facetList[4]['value'], '1/Book/Section/'); + $this->assertEquals($facetList[5]['value'], '1/Audio/Spoken/'); + $this->assertEquals($facetList[6]['value'], '1/Audio/Music/'); + } + + /** + * Tests for sortFacetList (top level only, specified with boolean) + * + * @return void + */ + public function testSortFacetListTopLevelBooleanTrue() { $facetList = $this->facetList; $this->helper->sortFacetList($facetList, true); @@ -129,9 +164,29 @@ class HierarchicalFacetHelperTest extends TestCase } /** - * Tests for sortFacetList (all levels) + * Tests for sortFacetList (top level only, specified with string) + * + * @return void */ - public function testSortFacetListAllLevels() + public function testSortFacetListTopLevelStringConfig() + { + $facetList = $this->facetList; + $this->helper->sortFacetList($facetList, 'top'); + $this->assertEquals($facetList[0]['value'], '0/AV/'); + $this->assertEquals($facetList[1]['value'], '0/Book/'); + $this->assertEquals($facetList[2]['value'], '0/Audio/'); + $this->assertEquals($facetList[3]['value'], '1/Book/BookPart/'); + $this->assertEquals($facetList[4]['value'], '1/Book/Section/'); + $this->assertEquals($facetList[5]['value'], '1/Audio/Spoken/'); + $this->assertEquals($facetList[6]['value'], '1/Audio/Music/'); + } + + /** + * Tests for sortFacetList (all levels, specified with boolean) + * + * @return void + */ + public function testSortFacetListAllLevelsBooleanFalse() { $facetList = $this->facetList; $this->helper->sortFacetList($facetList, false); @@ -144,8 +199,28 @@ class HierarchicalFacetHelperTest extends TestCase $this->assertEquals($facetList[6]['value'], '1/Audio/Spoken/'); } + /** + * Tests for sortFacetList (all levels, specified with string) + * + * @return void + */ + public function testSortFacetListAllLevelsStringConfig() + { + $facetList = $this->facetList; + $this->helper->sortFacetList($facetList, 'all'); + $this->assertEquals($facetList[0]['value'], '0/AV/'); + $this->assertEquals($facetList[1]['value'], '0/Book/'); + $this->assertEquals($facetList[2]['value'], '0/Audio/'); + $this->assertEquals($facetList[3]['value'], '1/Book/BookPart/'); + $this->assertEquals($facetList[4]['value'], '1/Book/Section/'); + $this->assertEquals($facetList[5]['value'], '1/Audio/Music/'); + $this->assertEquals($facetList[6]['value'], '1/Audio/Spoken/'); + } + /** * Tests for buildFacetArray + * + * @return void */ public function testBuildFacetArray() { @@ -183,6 +258,8 @@ class HierarchicalFacetHelperTest extends TestCase /** * Tests for flattenFacetHierarchy + * + * @return void */ public function testFlattenFacetHierarchy() { @@ -202,6 +279,8 @@ class HierarchicalFacetHelperTest extends TestCase /** * Tests for formatDisplayText + * + * @return void */ public function testFormatDisplayText() { diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php index 82d1773dfed84c67f5215a2c408bfefbfa500137..58e59a5d07e267081aebc4e109d409c777fbcd4a 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/MultiIndexListenerTest.php @@ -182,7 +182,7 @@ class MultiIndexListenerTest extends TestCase */ public function testAttach() { - $mock = $this->createMock('Zend\EventManager\SharedEventManagerInterface'); + $mock = $this->createMock(\Zend\EventManager\SharedEventManagerInterface::class); $mock->expects($this->once())->method('attach')->with( $this->equalTo('VuFind\Search'), $this->equalTo('pre'), diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php index b62166ad154836bfb00dc817ef61fb412cb5217f..6c9e6303cd5db813a613f7e6172a508cf39267cc 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/Solr/SpellingProcessorTest.php @@ -131,7 +131,7 @@ class SpellingProcessorTest extends TestCase $spelling = $this->getFixture('spell1'); $query = $this->getFixture('query1'); $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); $params->setBasicSearch($query->getString(), $query->getHandler()); $sp = new SpellingProcessor(); $this->assertEquals( @@ -183,6 +183,68 @@ class SpellingProcessorTest extends TestCase ); } + /** + * Test basic suggestions for an uppercase query. + * + * @return void + */ + public function testBasicSuggestionsForUppercaseQuery() + { + $spelling = $this->getFixture('spell6'); + $query = $this->getFixture('query6'); + $params = $this->getServiceManager() + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); + $params->setBasicSearch($query->getString(), $query->getHandler()); + $sp = new SpellingProcessor(); + $this->assertEquals( + [ + 'Grumble' => [ + 'freq' => 2, + 'suggestions' => [ + 'grumbler' => [ + 'freq' => 4, + 'new_term' => 'grumbler', + 'expand_term' => '(Grumble OR grumbler)', + ], + 'rumble' => [ + 'freq' => 40, + 'new_term' => 'rumble', + 'expand_term' => '(Grumble OR rumble)', + ], + 'crumble' => [ + 'freq' => 15, + 'new_term' => 'crumble', + 'expand_term' => '(Grumble OR crumble)', + ], + ], + ], + 'grimble' => [ + 'freq' => 7, + 'suggestions' => [ + 'trimble' => [ + 'freq' => 110, + 'new_term' => 'trimble', + 'expand_term' => '(grimble OR trimble)', + ], + 'gribble' => [ + 'freq' => 21, + 'new_term' => 'gribble', + 'expand_term' => '(grimble OR gribble)', + ], + 'grimsley' => [ + 'freq' => 24, + 'new_term' => 'grimsley', + 'expand_term' => '(grimble OR grimsley)', + ], + ], + ], + ], + $sp->processSuggestions( + $this->getExpectedQuery6Suggestions(), $spelling->getQuery(), $params + ) + ); + } + /** * Test basic suggestions with expansions disabled and phrase display on. * @@ -193,7 +255,7 @@ class SpellingProcessorTest extends TestCase $spelling = $this->getFixture('spell1'); $query = $this->getFixture('query1'); $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); $params->setBasicSearch($query->getString(), $query->getHandler()); $config = new Config(['expand' => false, 'phrase' => true]); $sp = new SpellingProcessor($config); @@ -429,7 +491,7 @@ class SpellingProcessorTest extends TestCase $spelling = $this->getFixture('spell' . $testNum); $query = $this->getFixture('query' . $testNum); $params = $this->getServiceManager() - ->get('VuFind\Search\Params\PluginManager')->get('Solr'); + ->get(\VuFind\Search\Params\PluginManager::class)->get('Solr'); $this->setProperty($params, 'query', $query); $sp = new SpellingProcessor(new Config($config)); $suggestions = $sp->getSuggestions($spelling, $query); @@ -468,6 +530,33 @@ class SpellingProcessorTest extends TestCase ]; } + /** + * Get expected suggestions for the "query6" example. + * + * @return array + */ + protected function getExpectedQuery6Suggestions() + { + return [ + 'Grumble' => [ + 'freq' => 2, + 'suggestions' => [ + 'grumbler' => 4, + 'rumble' => 40, + 'crumble' => 15, + ], + ], + 'grimble' => [ + 'freq' => 7, + 'suggestions' => [ + 'trimble' => 110, + 'gribble' => 21, + 'grimsley' => 24 + ], + ], + ]; + } + /** * Get a fixture object * diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php index ab512b0b5ed8994d961150de9285903cc50d3f40..b0eadd2804f94d3608635c4d2685b344b9f48393 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Search/UrlQueryHelperTest.php @@ -144,7 +144,7 @@ class UrlQueryHelperTest extends TestCase public function testFactory() { $factory = new UrlQueryHelperFactory(); - $config = $this->createMock('VuFind\Config\PluginManager'); + $config = $this->createMock(\VuFind\Config\PluginManager::class); $params = new \VuFindTest\Search\TestHarness\Params( new \VuFindTest\Search\TestHarness\Options($config), $config ); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php index e6633f6b39bbf850b881bc6a1beb5d39d8da85b2..99408faeaaad21028c2354b4f0c2c564c0079bc4 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Session/PluginManagerTest.php @@ -48,7 +48,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testShareByDefault() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $this->assertTrue($this->getProperty($pm, 'sharedByDefault')); } @@ -64,7 +64,7 @@ class PluginManagerTest extends \VuFindTest\Unit\TestCase public function testExpectedInterface() { $pm = new PluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $pm->validate(new \ArrayObject()); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php index 430dd1fb8021df6346e3ae2e0d80dfce7a5258ae..ddb96bee85224aa5b7cf497bf49597e02e0ff9ba 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Solr/WriterTest.php @@ -135,10 +135,10 @@ class WriterTest extends \VuFindTest\Unit\TestCase { $sm = new \Zend\ServiceManager\ServiceManager(); $pm = new BackendManager($sm); - $mockBackend = $this->getMockBuilder('VuFindSearch\Backend\Solr\Backend') + $mockBackend = $this->getMockBuilder(\VuFindSearch\Backend\Solr\Backend::class) ->disableOriginalConstructor() ->getMock(); - $mockConnector = $this->getMockBuilder('VuFindSearch\Backend\Solr\Connector') + $mockConnector = $this->getMockBuilder(\VuFindSearch\Backend\Solr\Connector::class) ->disableOriginalConstructor() ->setMethods(['getUrl', 'getTimeout', 'setTimeout', 'write']) ->getMock(); @@ -156,7 +156,7 @@ class WriterTest extends \VuFindTest\Unit\TestCase */ protected function getMockChangeTracker() { - return $this->getMockBuilder('VuFind\Db\Table\ChangeTracker') + return $this->getMockBuilder(\VuFind\Db\Table\ChangeTracker::class) ->disableOriginalConstructor() ->setMethods(['markDeleted']) ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..43ea7065d211bbb9ad0c0c0eaf5c694b50f39c25 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/DatabaseTest.php @@ -0,0 +1,132 @@ +<?php +/** + * "Database" URL shortener test. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\UrlShortener; + +use VuFind\UrlShortener\Database; + +/** + * "Database" URL shortener test. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class DatabaseTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get the object to test. + * + * @param object $table Database table object/mock + * + * @return Database + */ + public function getShortener($table) + { + return new Database('http://foo', $table); + } + + /** + * Get the mock table object. + * + * @param array $methods Methods to mock. + * + * @return object + */ + public function getMockTable($methods) + { + return $this->getMockBuilder(\VuFind\Db\Table\Shortlinks::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Test that the shortener works correctly under "happy path." + * + * @return void + */ + public function testShortener() + { + $table = $this->getMockTable(['insert', 'getLastInsertValue']); + $table->expects($this->once())->method('insert') + ->with($this->equalTo(['path' => '/bar'])); + $table->expects($this->once())->method('getLastInsertValue') + ->will($this->returnValue('10')); + $db = $this->getShortener($table); + $this->assertEquals('http://foo/short/A', $db->shorten('http://foo/bar')); + } + + /** + * Test that resolve is supported. + * + * @return void + */ + public function testResolution() + { + $table = $this->getMockTable(['select']); + $mockResults = $this->getMockBuilder(\Zend\Db\ResultSet::class) + ->setMethods(['count', 'current']) + ->disableOriginalConstructor() + ->getMock(); + $mockResults->expects($this->once())->method('count') + ->will($this->returnValue(1)); + $mockResults->expects($this->once())->method('current') + ->will($this->returnValue(['path' => '/bar'])); + $table->expects($this->once())->method('select') + ->with($this->equalTo(['id' => 10])) + ->will($this->returnValue($mockResults)); + $db = $this->getShortener($table); + $this->assertEquals('http://foo/bar', $db->resolve('A')); + } + + /** + * Test that resolve errors correctly when given bad input + * + * @return void + * + * @expectedException Exception + * @expectedExceptionMessage Shortlink could not be resolved: B + */ + public function testResolutionOfBadInput() + { + $table = $this->getMockTable(['select']); + $mockResults = $this->getMockBuilder(\Zend\Db\ResultSet::class) + ->setMethods(['count']) + ->disableOriginalConstructor() + ->getMock(); + $mockResults->expects($this->once())->method('count') + ->will($this->returnValue(0)); + $table->expects($this->once())->method('select') + ->with($this->equalTo(['id' => 11])) + ->will($this->returnValue($mockResults)); + $db = $this->getShortener($table); + $db->resolve('B'); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php new file mode 100644 index 0000000000000000000000000000000000000000..1b978f255e3da2641169abc5a446af9aee2b5338 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/UrlShortener/NoneTest.php @@ -0,0 +1,68 @@ +<?php +/** + * "None" URL shortener test. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\UrlShortener; + +use VuFind\UrlShortener\None; + +/** + * "None" URL shortener test. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class NoneTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that the shortener does nothing. + * + * @return void + */ + public function testShortener() + { + $none = new None(); + $url = 'http://foo'; + $this->assertEquals($url, $none->shorten($url)); + } + + /** + * Test that resolve is not supported. + * + * @return void + * + * @expectedException Exception + * @expectedExceptionMessage UrlShortener None is unable to resolve shortlinks. + */ + public function testNoResolution() + { + $none = new None(); + $none->resolve('foo'); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php index 4f943bfbf844e83e9f8033a897d0054306e4b0da..866558a9ef2a8dfa1bbafa6277aea22b0f2bf60e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/CartTest.php @@ -46,7 +46,7 @@ class CartTest extends \PHPUnit\Framework\TestCase public function testCart() { // Create a mock cart object: - $cart = $this->getMockBuilder('VuFind\Cart') + $cart = $this->getMockBuilder(\VuFind\Cart::class) ->disableOriginalConstructor() ->getMock(); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/LinkifyTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/LinkifyTest.php new file mode 100644 index 0000000000000000000000000000000000000000..084fb29b3e3d9ec6c9d6018c48f42f40e2dcf671 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/LinkifyTest.php @@ -0,0 +1,140 @@ +<?php +/** + * Linkify Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root; + +use VuFind\View\Helper\Root\Linkify; +use VuFind\View\Helper\Root\ProxyUrl; +use Zend\View\Helper\EscapeHtml; +use Zend\View\Helper\EscapeHtmlAttr; + +/** + * Linkify Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class LinkifyTest extends \VuFindTest\Unit\ViewHelperTestCase +{ + /** + * Get view helper to test. + * + * @return Linkify + */ + protected function getHelper() + { + $view = $this->getPhpRenderer( + [ + 'proxyUrl' => new ProxyUrl(), + 'escapeHtmlAttr' => new EscapeHtmlAttr(), + ] + ); + $linkify = new Linkify(); + $linkify->setView($view); + return $linkify; + } + + /** + * Run a simple test. + * + * @param string $text Raw input text + * @param string $expected Expected output HTML + * + * @return void + */ + protected function linkify($text, $expected) + { + $escaper = new EscapeHtml(); + // The linkify helper expects HTML-escaped input, because after linkify + // has been applied, we can no longer escape unlinked portions of the text + // without messing up the link HTML: + $html = $escaper->__invoke($text); + $this->assertEquals($expected, $this->getHelper()->__invoke($html)); + } + + /** + * Test linkification of HTTP URL. + * + * @return void + */ + public function testHttpLink() + { + $text = 'This has http://vufind.org in the middle of it'; + $expected = 'This has ' + . '<a href="http://vufind.org">http://vufind.org</a>' + . ' in the middle of it'; + $this->linkify($text, $expected); + } + + /** + * Test linkification of HTTPS URL. + * + * @return void + */ + public function testHttpsLink() + { + $text = "This has https://vufind.org in the middle of it"; + $expected = 'This has ' + . '<a href="https://vufind.org">https://vufind.org</a>' + . ' in the middle of it'; + $this->linkify($text, $expected); + } + + /** + * Test linkification of complex URL with parameters and hash. + * + * @return void + */ + public function testComplexLink() + { + $text = "This has https://vufind.org?foo=1&bar=2#xyzzy in the middle of it"; + $expected = 'This has ' + . '<a href="https://vufind.org' + . '?foo=1&bar=2#xyzzy">' + . 'https://vufind.org?foo=1&bar=2#xyzzy</a> in the middle of it'; + $this->linkify($text, $expected); + } + + /** + * Test linkification of multiple URLs. + * + * @return void + */ + public function testMultipleLinks() + { + $text = "This has https://vufind.org and http://vufind.org in it"; + $expected = 'This has ' + . '<a href="https://vufind.org">https://vufind.org</a>' + . ' and ' + . '<a href="http://vufind.org">http://vufind.org</a>' + . ' in it'; + $this->linkify($text, $expected); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/MetadataTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/MetadataTest.php new file mode 100644 index 0000000000000000000000000000000000000000..62b714f89c605d078a4ebf8566fd928fd9f22609 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/MetadataTest.php @@ -0,0 +1,111 @@ +<?php +/** + * Metadata Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2020. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root; + +use VuFind\MetadataVocabulary\PluginManager; +use VuFind\MetadataVocabulary\PRISM; +use VuFind\View\Helper\Root\Metadata; +use VuFindTest\RecordDriver\TestHarness; +use Zend\Config\Config; +use Zend\View\Helper\HeadMeta; + +/** + * Metadata Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class MetadataTest extends \VuFindTest\Unit\TestCase +{ + /** + * Get a fake record driver + * + * @param array $data Test data + * + * @return TestHarness + */ + protected function getDriver($data) + { + $driver = new TestHarness(); + $driver->setRawData($data); + return $driver; + } + + /** + * Get a mock HeadMeta helper + * + * @return HeadMeta + */ + protected function getMetaHelper() + { + $mock = $this->getMockBuilder(HeadMeta::class) + ->disableOriginalConstructor() + ->setMethods(['appendName']) + ->getMock(); + $mock->expects($this->once())->method('appendName') + ->with($this->equalTo('prism.title'), $this->equalTo('Fake Title')); + return $mock; + } + + /** + * Get a mock plugin manager + * + * @return PluginManager + */ + protected function getPluginManager() + { + $mock = $this->getMockBuilder(PluginManager::class) + ->disableOriginalConstructor() + ->setMethods(['get']) + ->getMock(); + $mock->expects($this->once())->method('get') + ->with($this->equalTo('PRISM')) + ->will($this->returnValue(new PRISM())); + return $mock; + } + + /** + * Test basic functionality of the helper. + * + * @return void + */ + public function testMetadata() + { + $helper = new Metadata( + $this->getPluginManager(), + new Config(['Vocabularies' => [TestHarness::class => ['PRISM']]]), + $this->getMetaHelper() + ); + $helper->generateMetatags( + $this->getDriver(['Title' => 'Fake Title']) + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php index 21b141d395c405d82ac02b0821d4f5e8734c59fb..e8d53f8de25033dcba3b7aec9539bef49417cf6f 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/OpenUrlTest.php @@ -223,7 +223,7 @@ class OpenUrlTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getMockContext() { - return $this->getMockBuilder('VuFind\View\Helper\Root\Context') + return $this->getMockBuilder(\VuFind\View\Helper\Root\Context::class) ->disableOriginalConstructor()->getMock(); } @@ -297,7 +297,7 @@ class OpenUrlTest extends \VuFindTest\Unit\ViewHelperTestCase if (null === $mockContext) { $mockContext = $this->getMockContext(); } - $mockPm = $this->getMockBuilder('VuFind\Resolver\Driver\PluginManager') + $mockPm = $this->getMockBuilder(\VuFind\Resolver\Driver\PluginManager::class) ->disableOriginalConstructor()->getMock(); $openUrl = new OpenUrl($mockContext, $rules, $mockPm, new Config($config)); $openUrl->setView($this->getPhpRenderer()); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php index ba64a52f0df32fc502e8767f9b6c44d74740b91c..b6e4a301ba0a0b9239b16dd1c253d7826c4e24b6 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/PermissionTest.php @@ -154,7 +154,7 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getMockPmd($config = false) { - $mockPmd = $this->getMockBuilder('\VuFind\Role\PermissionDeniedManager') + $mockPmd = $this->getMockBuilder(\VuFind\Role\PermissionDeniedManager::class) ->setConstructorArgs([$this->permissionDeniedConfig]) ->getMock(); $mockPmd->expects($this->any())->method('getDeniedTemplateBehavior') @@ -171,7 +171,7 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getMockPm($isAuthorized = false) { - $mockPm = $this->getMockBuilder('\VuFind\Role\PermissionManager') + $mockPm = $this->getMockBuilder(\VuFind\Role\PermissionManager::class) ->disableOriginalConstructor() ->getMock(); $mockPm->expects($this->any())->method('isAuthorized') @@ -189,7 +189,7 @@ class PermissionTest extends \VuFindTest\Unit\ViewHelperTestCase */ protected function getMockContext() { - return $this->getMockBuilder('VuFind\View\Helper\Root\Context') + return $this->getMockBuilder(\VuFind\View\Helper\Root\Context::class) ->disableOriginalConstructor()->getMock(); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php index daa50dbeb2f7080b0e1a9737178593d936b70905..98c3676a86f9629489484c4f465c5bd1af3354fd 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordDataFormatterTest.php @@ -41,6 +41,22 @@ use VuFind\View\Helper\Root\RecordDataFormatterFactory; */ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase { + /** + * Get a mock record router. + * + * @return \VuFind\Record\Router + */ + protected function getMockRecordRouter() + { + $mock = $this->getMockBuilder(\VuFind\Record\Router::class) + ->disableOriginalConstructor() + ->setMethods(['getActionRouteDetails']) + ->getMock(); + $mock->expects($this->any())->method('getActionRouteDetails') + ->will($this->returnValue(['route' => 'home', 'params' => []])); + return $mock; + } + /** * Get view helpers needed by test. * @@ -51,17 +67,17 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase $context = new \VuFind\View\Helper\Root\Context(); return [ 'auth' => new \VuFind\View\Helper\Root\Auth( - $this->getMockBuilder('VuFind\Auth\Manager')->disableOriginalConstructor()->getMock(), - $this->getMockBuilder('VuFind\Auth\ILSAuthenticator')->disableOriginalConstructor()->getMock() + $this->getMockBuilder(\VuFind\Auth\Manager::class)->disableOriginalConstructor()->getMock(), + $this->getMockBuilder(\VuFind\Auth\ILSAuthenticator::class)->disableOriginalConstructor()->getMock() ), 'context' => $context, 'doi' => new \VuFind\View\Helper\Root\Doi($context), - 'openUrl' => new \VuFind\View\Helper\Root\OpenUrl($context, [], $this->getMockBuilder('VuFind\Resolver\Driver\PluginManager')->disableOriginalConstructor()->getMock()), + 'openUrl' => new \VuFind\View\Helper\Root\OpenUrl($context, [], $this->getMockBuilder(\VuFind\Resolver\Driver\PluginManager::class)->disableOriginalConstructor()->getMock()), 'proxyUrl' => new \VuFind\View\Helper\Root\ProxyUrl(), 'record' => new \VuFind\View\Helper\Root\Record(), - 'recordLink' => new \VuFind\View\Helper\Root\RecordLink($this->getMockBuilder('VuFind\Record\Router')->disableOriginalConstructor()->getMock()), + 'recordLink' => new \VuFind\View\Helper\Root\RecordLink($this->getMockRecordRouter()), 'searchOptions' => new \VuFind\View\Helper\Root\SearchOptions(new \VuFind\Search\Options\PluginManager($this->getServiceManager())), - 'searchTabs' => $this->getMockBuilder('VuFind\View\Helper\Root\SearchTabs')->disableOriginalConstructor()->getMock(), + 'searchTabs' => $this->getMockBuilder(\VuFind\View\Helper\Root\SearchTabs::class)->disableOriginalConstructor()->getMock(), 'transEsc' => new \VuFind\View\Helper\Root\TransEsc(), 'translate' => new \VuFind\View\Helper\Root\Translate(), 'usertags' => new \VuFind\View\Helper\Root\UserTags(), @@ -81,7 +97,7 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase $methods = [ 'getBuilding', 'getDeduplicatedAuthors', 'getContainerTitle', 'getTags' ]; - $record = $this->getMockBuilder('VuFind\RecordDriver\SolrDefault') + $record = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) ->setMethods($methods) ->getMock(); $record->expects($this->any())->method('getTags') @@ -136,7 +152,7 @@ class RecordDataFormatterTest extends \VuFindTest\Unit\ViewHelperTestCase $match = new \Zend\Router\RouteMatch([]); $match->setMatchedRouteName('foo'); $view->plugin('url') - ->setRouter($this->createMock('Zend\Router\RouteStackInterface')) + ->setRouter($this->createMock(\Zend\Router\RouteStackInterface::class)) ->setRouteMatch($match); // Inject the view object into all of the helpers: diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordLinkTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordLinkTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7fa28a5237deb1330181fc22448f54026385e820 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordLinkTest.php @@ -0,0 +1,122 @@ +<?php +/** + * RecordLink view helper Test Class + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root; + +use VuFind\Record\Router; +use VuFind\View\Helper\Root\RecordLink; +use Zend\Config\Config; + +/** + * RecordLink view helper Test Class + * + * @category VuFind + * @package Tests + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class RecordLinkTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test record URL creation. + * + * @return void + */ + public function testRecordUrl() + { + $recordLink = $this->getRecordLink(); + $this->assertEquals( + '/Record/foo', + $recordLink->getUrl('Solr|foo') + ); + + // Make sure any percent signs in record ID are properly URL-encoded + $this->assertEquals( + '/Record/foo%252fbar', + $recordLink->getUrl('Solr|foo%2fbar') + ); + $this->assertEquals( + '/Record/foo%252fbar?checkRoute=1', + $recordLink->getTabUrl('Solr|foo%2fbar', null, ['checkRoute' => 1]) + ); + } + + /** + * Get a RecordLink object ready for testing. + * + * @return Record + */ + protected function getRecordLink() + { + $view = $this->getMockBuilder(\Zend\View\Renderer\PhpRenderer::class) + ->disableOriginalConstructor() + ->setMethods(['plugin']) + ->getMock(); + $view->expects($this->any())->method('plugin') + ->will($this->returnValue($this->getUrl())); + + $recordLink = new RecordLink(new Router(new Config([]))); + $recordLink->setView($view); + return $recordLink; + } + + /** + * Get a URL helper. + * + * @return \VuFind\View\Helper\Root\Url + */ + protected function getUrl() + { + $request = $this->getMockBuilder(\Zend\Http\PhpEnvironment\Request::class) + ->setMethods(['getQuery'])->getMock(); + $request->expects($this->any())->method('getQuery') + ->will($this->returnValue(new \Zend\StdLib\Parameters())); + + $url = new \VuFind\View\Helper\Root\Url($request); + + // Create router + $router = new \Zend\Router\Http\TreeRouteStack(); + $router->setRequestUri(new \Zend\Uri\Http('http://localhost')); + $recordRoute = new \Zend\Router\Http\Segment( + '/Record/[:id[/[:tab]]]', + [ + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + ], + [ + 'controller' => 'Record', + 'action' => 'Home', + ] + ); + + $router->addRoute('record', $recordRoute); + $url->setRouter($router); + + return $url; + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php index 2ee0ba6e3d8da4c9759420ebcf21893751950210..f9c6467564429c9f1161bc76703db57086814e2b 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/RecordTest.php @@ -164,7 +164,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ public function testGetListEntry() { - $driver = $this->createMock('VuFind\RecordDriver\AbstractBase'); + $driver = $this->createMock(\VuFind\RecordDriver\AbstractBase::class); $driver->expects($this->once())->method('getContainingLists') ->with($this->equalTo(42)) ->will($this->returnValue([1, 2, 3])); @@ -499,7 +499,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase if (null === $context) { $context = $this->getMockContext(); } - $view = $this->getMockBuilder('Zend\View\Renderer\PhpRenderer') + $view = $this->getMockBuilder(\Zend\View\Renderer\PhpRenderer::class) ->disableOriginalConstructor() ->setMethods(['render', 'plugin', 'resolver']) ->getMock(); @@ -536,7 +536,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ protected function getMockResolver() { - return $this->createMock('Zend\View\Resolver\ResolverInterface'); + return $this->createMock(\Zend\View\Resolver\ResolverInterface::class); } /** @@ -546,7 +546,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ protected function getMockContext() { - $context = $this->createMock('VuFind\View\Helper\Root\Context'); + $context = $this->createMock(\VuFind\View\Helper\Root\Context::class); $context->expects($this->any())->method('__invoke') ->will($this->returnValue($context)); return $context; @@ -561,7 +561,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ protected function getMockUrl($expectedRoute) { - $url = $this->createMock('Zend\View\Helper\Url'); + $url = $this->createMock(\Zend\View\Helper\Url::class); $url->expects($this->once())->method('__invoke') ->with($this->equalTo($expectedRoute)) ->will($this->returnValue('http://foo/bar')); @@ -577,7 +577,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ protected function getMockServerUrl() { - $url = $this->createMock('Zend\View\Helper\ServerUrl'); + $url = $this->createMock(\Zend\View\Helper\ServerUrl::class); $url->expects($this->once())->method('__invoke') ->will($this->returnValue('http://server-foo/baz')); return $url; @@ -590,7 +590,7 @@ class RecordTest extends \PHPUnit\Framework\TestCase */ protected function getMockSearchTabs() { - $searchTabs = $this->getMockBuilder('VuFind\View\Helper\Root\SearchTabs') + $searchTabs = $this->getMockBuilder(\VuFind\View\Helper\Root\SearchTabs::class) ->disableOriginalConstructor()->getMock(); $searchTabs->expects($this->any())->method('getCurrentHiddenFilterParams') ->will($this->returnValue('')); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php index 7a8ea1f96568639c255a74be9caca1c584d2304f..a879de3f5cd2dafc51abc56e2e8debf8eacb0e78 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/SafeMoneyFormatTest.php @@ -81,7 +81,7 @@ class SafeMoneyFormatTest extends \PHPUnit\Framework\TestCase public function testFormatting() { $escaper = new \Zend\View\Helper\EscapeHtml(); - $view = $this->createMock('Zend\View\Renderer\PhpRenderer'); + $view = $this->createMock(\Zend\View\Renderer\PhpRenderer::class); $view->expects($this->any())->method('plugin') ->with($this->equalTo('escapeHtml')) ->will($this->returnValue($escaper)); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0c0a053754dfaec4cf25989426c03c22b8afed75 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/ShortenUrlTest.php @@ -0,0 +1,62 @@ +<?php +/** + * ShortenUrl view helper Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\View\Helper\Root; + +use VuFind\UrlShortener\Database; +use VuFind\UrlShortener\UrlShortenerInterface; +use VuFind\View\Helper\Root\ShortenUrl; +use VuFind\View\Helper\Root\ShortenUrlFactory; + +/** + * ShortenUrl view helper Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class ShortenUrlTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that helper proxies to appropriate service. + * + * @return void + */ + public function testHelper() + { + $container = new \VuFindTest\Container\MockContainer($this); + $service = $container->createMock(Database::class, ['shorten']); + $service->expects($this->once())->method('shorten') + ->with($this->equalTo('foo'))->will($this->returnValue('bar')); + $container->set(UrlShortenerInterface::class, $service); + $factory = new ShortenUrlFactory(); + $helper = $factory($container, ShortenUrl::class); + $this->assertEquals('bar', $helper('foo')); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php index f058cb96a5cbe2931694c304ecace313b4a4bc19..db2c99a5879f00fc8c2bb8d511b447ed939e947e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/View/Helper/Root/TranslateTest.php @@ -220,7 +220,7 @@ class TranslateTest extends \PHPUnit\Framework\TestCase public function testLocaleWithTranslator() { $translate = new Translate(); - $translator = $this->createMock('Zend\I18n\Translator\Translator'); + $translator = $this->createMock(\Zend\I18n\Translator\Translator::class); $translator->expects($this->once())->method('getLocale') ->will($this->returnValue('foo')); $translate->setTranslator($translator); @@ -235,7 +235,7 @@ class TranslateTest extends \PHPUnit\Framework\TestCase public function testGetTranslator() { $translate = new Translate(); - $translator = $this->createMock('Zend\I18n\Translator\TranslatorInterface'); + $translator = $this->createMock(\Zend\I18n\Translator\TranslatorInterface::class); $translate->setTranslator($translator); $this->assertEquals($translator, $translate->getTranslator()); } @@ -252,7 +252,7 @@ class TranslateTest extends \PHPUnit\Framework\TestCase $callback = function ($str, $domain) use ($translations) { return $translations[$domain][$str] ?? $str; }; - $translator = $this->createMock('Zend\I18n\Translator\TranslatorInterface'); + $translator = $this->createMock(\Zend\I18n\Translator\TranslatorInterface::class); $translator->expects($this->any())->method('translate') ->will($this->returnCallback($callback)); return $translator; diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/XSLT/Import/VuFindTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/XSLT/Import/VuFindTest.php new file mode 100644 index 0000000000000000000000000000000000000000..2757350182fff677bd57ddcc0a77275e343f6f01 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/XSLT/Import/VuFindTest.php @@ -0,0 +1,161 @@ +<?php +/** + * XSLT helper tests. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\XSLT\Import; + +use VuFind\XSLT\Import\VuFind; + +/** + * XSLT helper tests. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class VuFindTest extends \VuFindTest\Unit\DbTestCase +{ + /** + * Test the getChangeTracker helper. + * + * @return void + */ + public function testGetChangeTracker() + { + VuFind::setServiceLocator($this->getServiceManager()); + $this->assertEquals( + \VuFind\Db\Table\ChangeTracker::class, + get_class(VuFind::getChangeTracker()) + ); + } + + /** + * Test the getConfig helper. + * + * @return void + */ + public function testGetConfig() + { + VuFind::setServiceLocator($this->getServiceManager()); + $this->assertEquals( + \Zend\Config\Config::class, get_class(VuFind::getConfig()) + ); + } + + /** + * Test the harvestTextFile helper. + * + * @return void + */ + public function testHarvestTextFile() + { + $this->assertEquals( + file_get_contents(__FILE__), + VuFind::harvestTextFile('file://' . __FILE__) + ); + } + + /** + * Test the stripBadChars helper. + * + * @return void + */ + public function testStripBadChars() + { + $this->assertEquals('f oo', VuFind::stripBadChars('f' . chr(8) . 'oo')); + } + + /** + * Test the mapString helper. + * + * @return void + */ + public function testMapString() + { + $this->assertEquals( + 'CD', VuFind::mapString('SoundDisc', 'format_map.properties') + ); + } + + /** + * Test the stripArticles helper. + * + * @return void + */ + public function testStripArticles() + { + $this->assertEquals('title', VuFind::stripArticles('The Title')); + $this->assertEquals('title', VuFind::stripArticles('A Title')); + $this->assertEquals('odd title', VuFind::stripArticles('An Odd Title')); + } + + /** + * Test the xmlAsText helper. + * + * @return void + */ + public function testXmlAsText() + { + $doc = new \DOMDocument('1.0'); + $node = new \DOMElement('bar', 'foo'); + $doc->appendChild($node); + $expected = '<?xml version="1.0"?>' . "\n<bar>foo</bar>\n"; + $this->assertEquals($expected, VuFind::xmlAsText([$node])); + } + + /** + * Test the removeTagAndReturnXMLasText helper. + * + * @return void + */ + public function testRemoveTagAndReturnXMLasText() + { + $doc = new \DOMDocument('1.0'); + $node = new \DOMElement('bar', 'foo'); + $doc->appendChild($node); + $node->appendChild(new \DOMElement('xyzzy', 'baz')); + $expected = '<?xml version="1.0"?>' . "\n<bar>foo</bar>\n"; + $this->assertEquals( + $expected, VuFind::removeTagAndReturnXMLasText([$node], 'xyzzy') + ); + } + + /** + * Test the explode helper. + * + * @return void + */ + public function testExplode() + { + $expected = '<?xml version="1.0" encoding="utf-8"?>' + . "\n<part>a</part>\n<part>b</part>\n"; + $this->assertEquals( + $expected, simplexml_import_dom(VuFind::explode(',', 'a,b'))->asXml() + ); + } +} diff --git a/module/VuFindAdmin/config/module.config.php b/module/VuFindAdmin/config/module.config.php index bf80ea81a8aded7eb5bb918becdb7ac77a69470f..e874986086b1dbe92aacf06cff588fe65f9d9107 100644 --- a/module/VuFindAdmin/config/module.config.php +++ b/module/VuFindAdmin/config/module.config.php @@ -9,6 +9,8 @@ $config = [ 'VuFindAdmin\Controller\MaintenanceController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFindAdmin\Controller\SocialstatsController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFindAdmin\Controller\TagsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindAdmin\Controller\OverdriveController' => + 'VuFind\Controller\AbstractBaseFactory', ], 'aliases' => [ 'Admin' => 'VuFindAdmin\Controller\AdminController', @@ -16,6 +18,7 @@ $config = [ 'AdminMaintenance' => 'VuFindAdmin\Controller\MaintenanceController', 'AdminSocial' => 'VuFindAdmin\Controller\SocialstatsController', 'AdminTags' => 'VuFindAdmin\Controller\TagsController', + 'AdminOverdrive' => 'VuFindAdmin\Controller\OverdriveController', ], ], 'router' => [ @@ -81,6 +84,16 @@ $config = [ ] ] ], + 'overdrive' => [ + 'type' => 'Zend\Router\Http\Segment', + 'options' => [ + 'route' => '/Overdrive', + 'defaults' => [ + 'controller' => 'AdminOverdrive', + 'action' => 'Home', + ] + ] + ], ], ], ], diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php index 6f8288f0b14384e9dbbab0a7bf1344f712e7c3f1..1e156a1864693e471214e9c520fe7242ccaf6bf5 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/AbstractAdmin.php @@ -75,7 +75,7 @@ class AbstractAdmin extends \VuFind\Controller\AbstractBase $config = $this->getConfig(); if (!isset($config->Site->admin_enabled) || !$config->Site->admin_enabled) { $pluginManager = $this->serviceLocator - ->get('Zend\Mvc\Controller\PluginManager'); + ->get(\Zend\Mvc\Controller\PluginManager::class); $redirectPlugin = $pluginManager->get('redirect'); return $redirectPlugin->toRoute('admin/disabled'); } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php index ef019c79411179757d060cb9453724d4528a24be..161f248055ae6547267d56f90cdcf9606dd17ebf 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/AdminController.php @@ -58,7 +58,7 @@ class AdminController extends AbstractAdmin $config = $this->getConfig(); $xml = false; if (isset($config->Index->url)) { - $response = $this->serviceLocator->get('VuFindHttp\HttpService') + $response = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->get($config->Index->url . '/admin/cores?wt=xml'); $xml = $response->isSuccess() ? $response->getBody() : false; } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php index 75a829e8d4647da8a352758c558b3c41801c3d6d..17a5f45a79e1339ba29bb991a27684acfd7d095f 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/ConfigController.php @@ -70,7 +70,7 @@ class ConfigController extends AbstractAdmin // Reload config now that it has been edited (otherwise, old setting // will persist in cache): - $this->serviceLocator->get('VuFind\Config\PluginManager') + $this->serviceLocator->get(\VuFind\Config\PluginManager::class) ->reload('config'); } else { $this->flashMessenger()->addMessage( diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php index bd5349a7223fe3a7d51079d3d6f9fe52d368aea6..178d316309781dcc546c3fd2abf28d595a07c958 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/MaintenanceController.php @@ -46,7 +46,7 @@ class MaintenanceController extends AbstractAdmin public function homeAction() { $view = $this->createViewModel(); - $view->caches = $this->serviceLocator->get('VuFind\Cache\Manager') + $view->caches = $this->serviceLocator->get(\VuFind\Cache\Manager::class) ->getCacheList(); $view->setTemplate('admin/maintenance/home'); return $view; @@ -59,7 +59,7 @@ class MaintenanceController extends AbstractAdmin */ public function clearcacheAction() { - $cacheManager = $this->serviceLocator->get('VuFind\Cache\Manager'); + $cacheManager = $this->serviceLocator->get(\VuFind\Cache\Manager::class); foreach ($this->params()->fromQuery('cache', []) as $cache) { $cacheManager->getCache($cache)->flush(); } diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/OverdriveController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/OverdriveController.php new file mode 100644 index 0000000000000000000000000000000000000000..ecc8292aefea8845b44a742840a22eae131fe7e2 --- /dev/null +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/OverdriveController.php @@ -0,0 +1,82 @@ +<?php +/** + * Admin Tag Controller + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindAdmin\Controller; + +/** + * Class controls distribution of tags and resource tags. + * + * @category VuFind + * @package Controller + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class OverdriveController extends AbstractAdmin +{ + /** + * Params + * + * @var array + */ + protected $params; + + /** + * Get the url parameters + * + * @param string $param A key to check the url params for + * + * @return string + */ + protected function getParam($param) + { + return (isset($this->params[$param])) + ? $this->params[$param] + : $this->params()->fromPost( + $param, + $this->params()->fromQuery($param, null) + ); + } + + /** + * Tag Details + * + * @return \Zend\View\Model\ViewModel + */ + public function homeAction() + { + $connector = $this->serviceLocator + ->get('VuFind\DigitalContent\OverdriveConnector'); + + $view = $this->createViewModel(); + $view->setTemplate('admin/overdrive/home'); + $view->productsKey = $connector->getCollectionToken(); + $view->overdriveConfig = $connector->getConfig(); + $view->hasAccess = $connector->getAccess(); + return $view; + } +} diff --git a/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php b/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php index 7a6cfb73c2c8c6d5996b4d1c4bd35413e23c487d..4cdb34b286a907b0e66c0a250f8492c17b7df0c5 100644 --- a/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php +++ b/module/VuFindAdmin/tests/unit-tests/src/VuFindTest/Controller/SocialstatsControllerTest.php @@ -47,17 +47,17 @@ class SocialstatsControllerTest extends \VuFindTest\Unit\TestCase public function testHome() { // Create mocks to simulate database lookups: - $c = $this->getMockBuilder('VuFindAdmin\Controller\SocialstatsController') + $c = $this->getMockBuilder(\VuFindAdmin\Controller\SocialstatsController::class) ->setMethods(['getTable'])->disableOriginalConstructor()->getMock(); - $comments = $this->getMockBuilder('VuFind\Db\Table\Comments') + $comments = $this->getMockBuilder(\VuFind\Db\Table\Comments::class) ->disableOriginalConstructor()->setMethods(['getStatistics'])->getMock(); $comments->expects($this->once())->method('getStatistics')->will($this->returnValue('comments-data')); $c->expects($this->at(0))->method('getTable')->with($this->equalTo('comments'))->will($this->returnValue($comments)); - $userresource = $this->getMockBuilder('VuFind\Db\Table\UserResource') + $userresource = $this->getMockBuilder(\VuFind\Db\Table\UserResource::class) ->setMethods(['getStatistics'])->disableOriginalConstructor()->getMock(); $userresource->expects($this->once())->method('getStatistics')->will($this->returnValue('userresource-data')); $c->expects($this->at(1))->method('getTable')->with($this->equalTo('userresource'))->will($this->returnValue($userresource)); - $resourcetags = $this->getMockBuilder('VuFind\Db\Table\ResourceTags') + $resourcetags = $this->getMockBuilder(\VuFind\Db\Table\ResourceTags::class) ->disableOriginalConstructor()->setMethods(['getStatistics']) ->getMock(); $resourcetags->expects($this->once())->method('getStatistics')->will($this->returnValue('resourcetags-data')); diff --git a/module/VuFindApi/config/module.config.php b/module/VuFindApi/config/module.config.php index 1cdbb9823153f4457c779fdd532639e6835546e1..d551909287fb5a3186b50c18d86f3afa30d7b638 100644 --- a/module/VuFindApi/config/module.config.php +++ b/module/VuFindApi/config/module.config.php @@ -4,8 +4,8 @@ namespace VuFindApi\Module\Configuration; $config = [ 'controllers' => [ 'factories' => [ - 'VuFindApi\Controller\ApiController' => 'VuFindApi\Controller\Factory::getApiController', - 'VuFindApi\Controller\SearchApiController' => 'VuFindApi\Controller\Factory::getSearchApiController', + 'VuFindApi\Controller\ApiController' => 'VuFindApi\Controller\ApiControllerFactory', + 'VuFindApi\Controller\SearchApiController' => 'VuFindApi\Controller\SearchApiControllerFactory', ], 'aliases' => [ 'Api' => 'VuFindApi\Controller\ApiController', diff --git a/module/VuFindApi/src/VuFindApi/Controller/Factory.php b/module/VuFindApi/src/VuFindApi/Controller/ApiControllerFactory.php similarity index 56% rename from module/VuFindApi/src/VuFindApi/Controller/Factory.php rename to module/VuFindApi/src/VuFindApi/Controller/ApiControllerFactory.php index 784099af9c82d6d1bb3e031c957569fa67e32c41..135616e255cd7ffd43c0532c87b4f3be130ba3cb 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/Factory.php +++ b/module/VuFindApi/src/VuFindApi/Controller/ApiControllerFactory.php @@ -1,6 +1,6 @@ <?php /** - * Factory for controllers. + * Factory for ApiController. * * PHP version 7 * @@ -27,48 +27,42 @@ */ namespace VuFindApi\Controller; -use Zend\ServiceManager\ServiceManager; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; /** - * Factory for controllers. + * Factory for ApiController. * * @category VuFind * @package Controller * @author Ere Maijala <ere.maijala@helsinki.fi> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:controllers Wiki - * - * @codeCoverageIgnore */ -class Factory +class ApiControllerFactory implements FactoryInterface { /** - * Construct the ApiController. - * - * @param ServiceManager $sm Service manager. + * Create an object * - * @return ApiController - */ - public static function getApiController(ServiceManager $sm) - { - $controller = new ApiController($sm); - $controller->addApi($sm->get('ControllerManager')->get('SearchApi')); - return $controller; - } - - /** - * Construct the SearchApiController. + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * - * @param ServiceManager $sm Service manager. + * @return object * - * @return SearchApiController + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public static function getSearchApiController(ServiceManager $sm) - { - return new SearchApiController( - $sm, - $sm->get('VuFindApi\Formatter\RecordFormatter'), - $sm->get('VuFindApi\Formatter\FacetFormatter') - ); + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $controller = new $requestedName($container); + $controller->addApi($container->get('ControllerManager')->get('SearchApi')); + return $controller; } } diff --git a/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php b/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php index fd056266039895a4530c7bd630c1aa1d1b9c9f7a..1776c7037e781eb6bbf3793ef6c7f03a418004cc 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php +++ b/module/VuFindApi/src/VuFindApi/Controller/ApiTrait.php @@ -115,7 +115,8 @@ trait ApiTrait */ protected function isAccessDenied($permission) { - $auth = $this->serviceLocator->get('ZfcRbac\Service\AuthorizationService'); + $auth = $this->serviceLocator + ->get(\ZfcRbac\Service\AuthorizationService::class); if (!$auth->isGranted($permission)) { return $this->output([], self::STATUS_ERROR, 403, 'Permission denied'); } diff --git a/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php b/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php index 1794a75f42790481a973aedafd06a31a94d1e719..579c65c1526aec679bf285c6de70fb612120f4f0 100644 --- a/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php +++ b/module/VuFindApi/src/VuFindApi/Controller/SearchApiController.php @@ -114,7 +114,6 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch $results = $this->getResultsManager()->get($this->searchClassId); $options = $results->getOptions(); $params = $results->getParams(); - $params->activateAllFacets(); $viewParams = [ 'config' => $config, @@ -189,7 +188,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch return $this->output([], self::STATUS_ERROR, 400, 'Missing id'); } - $loader = $this->serviceLocator->get('VuFind\Record\Loader'); + $loader = $this->serviceLocator->get(\VuFind\Record\Loader::class); try { if (is_array($request['id'])) { $results = $loader->loadBatchForSource($request['id']); @@ -253,7 +252,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch ? $facetConfig->SpecialFacets->hierarchical->toArray() : []; - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); + $runner = $this->serviceLocator->get(\VuFind\Search\SearchRunner::class); try { $results = $runner->run( $request, @@ -333,7 +332,7 @@ class SearchApiController extends \VuFind\Controller\AbstractSearch $facetResults = $results->getFullFieldFacets($facets, false, -1, 'count'); $facetHelper = $this->serviceLocator - ->get('VuFind\Search\Solr\HierarchicalFacetHelper'); + ->get(\VuFind\Search\Solr\HierarchicalFacetHelper::class); $facetList = []; foreach ($facets as $facet) { diff --git a/module/VuFindApi/src/VuFindApi/Controller/SearchApiControllerFactory.php b/module/VuFindApi/src/VuFindApi/Controller/SearchApiControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ea1fa137ff4f0ffd764952c39fbb576480e9e639 --- /dev/null +++ b/module/VuFindApi/src/VuFindApi/Controller/SearchApiControllerFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for SearchApiController. + * + * PHP version 7 + * + * Copyright (C) The National Library of Finland 2016. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +namespace VuFindApi\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for SearchApiController. + * + * @category VuFind + * @package Controller + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +class SearchApiControllerFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container, + $container->get(\VuFindApi\Formatter\RecordFormatter::class), + $container->get(\VuFindApi\Formatter\FacetFormatter::class) + ); + } +} diff --git a/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatterFactory.php b/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatterFactory.php index 84b1cc4c7d615a3522e7a2e8923517a0858ab30e..62f6f016cae6c1419a88fe4214aa5a4678a1d468 100644 --- a/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatterFactory.php +++ b/module/VuFindApi/src/VuFindApi/Formatter/RecordFormatterFactory.php @@ -62,7 +62,7 @@ class RecordFormatterFactory implements FactoryInterface throw new \Exception('Unexpected options sent to factory.'); } - $recordFields = $container->get('VuFind\Config\YamlReader') + $recordFields = $container->get(\VuFind\Config\YamlReader::class) ->get('SearchApiRecordFields.yaml'); $helperManager = $container->get('ViewHelperManager'); return new $requestedName($recordFields, $helperManager); diff --git a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php index 2f3c71b3e59fd070ef9dd38725dc2ae1a3a2e7b7..7cbba89e5e6a26a8fc7e0457b8ff7ead32b7bef2 100644 --- a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php +++ b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/FacetFormatterTest.php @@ -156,7 +156,7 @@ class FacetFormatterTest extends \VuFindTest\Unit\TestCase $results = []; $helper = new \VuFind\Search\Solr\HierarchicalFacetHelper(); - $configManager = $this->createMock('VuFind\Config\PluginManager'); + $configManager = $this->createMock(\VuFind\Config\PluginManager::class); $params = new Params(new Options($configManager), $configManager); $requestParams = new \Zend\StdLib\Parameters($request); $params->initFromRequest($requestParams); @@ -181,12 +181,12 @@ class FacetFormatterTest extends \VuFindTest\Unit\TestCase */ protected function getFakeResults($request, $facetData) { - $configManager = $this->createMock('VuFind\Config\PluginManager'); + $configManager = $this->createMock(\VuFind\Config\PluginManager::class); $params = new Params(new Options($configManager), $configManager); $params->initFromRequest(new \Zend\Stdlib\Parameters($request)); - $ss = $this->getMockBuilder('VuFindSearch\Service') + $ss = $this->getMockBuilder(\VuFindSearch\Service::class) ->disableOriginalConstructor()->getMock(); - $rl = $this->getMockBuilder('VuFind\Record\Loader') + $rl = $this->getMockBuilder(\VuFind\Record\Loader::class) ->disableOriginalConstructor()->getMock(); return new Results($params, $ss, $rl, 100, $facetData); } diff --git a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php index 169642533af2417f06ced1a9f9211a27f72b0609..09afece364288026b8bfba950a2ebc457677e890 100644 --- a/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php +++ b/module/VuFindApi/tests/unit-tests/src/VuFindTest/Formatter/RecordFormatterTest.php @@ -80,11 +80,11 @@ class RecordFormatterTest extends \VuFindTest\Unit\TestCase protected function getHelperPluginManager() { $hm = new \Zend\View\HelperPluginManager( - $this->createMock('Interop\Container\ContainerInterface') + $this->createMock(\Interop\Container\ContainerInterface::class) ); $hm->setService('translate', new \VuFind\View\Helper\Root\Translate()); - $mockRecordLink = $this->getMockBuilder('VuFind\View\Helper\Root\RecordLink') + $mockRecordLink = $this->getMockBuilder(\VuFind\View\Helper\Root\RecordLink::class) ->disableOriginalConstructor()->getMock(); $mockRecordLink->expects($this->any())->method('getUrl') ->will($this->returnValue('http://record')); diff --git a/module/VuFindConsole/Module.php b/module/VuFindConsole/Module.php index b0462b1990aad2d3610c1658f30929a5d936cd2e..8a509e31dea80326a825dc5f3dc88da1bece8315 100644 --- a/module/VuFindConsole/Module.php +++ b/module/VuFindConsole/Module.php @@ -117,16 +117,19 @@ class Module implements \Zend\ModuleManager\Feature\ConsoleUsageProviderInterfac 'language copystring' => 'Copy one language string to another', 'language delete' => 'Remove a language string from all files', 'language normalize' => 'Normalize a directory of language files', + 'scheduledsearch notify' => 'Send scheduled search email notifications', 'util cleanup_record_cache' => 'Remove unused records from the cache', 'util commit' => 'Solr commit tool', 'util createHierarchyTrees' => 'Cache populator for hierarchies', 'util cssBuilder' => 'LESS compiler', 'util deletes' => 'Tool for deleting Solr records', + 'util expire_auth_hashes' => 'Database auth_hash table cleanup', 'util expire_external_sessions' => 'Database external_session table cleanup', 'util expire_searches' => 'Database search table cleanup', 'util expire_sessions' => 'Database session table cleanup', 'util index_reserves' => 'Solr reserves indexer', + 'util lint_marc' => 'MARC validator', 'util optimize' => 'Solr optimize tool', 'util sitemap' => 'XML sitemap generator', 'util suppressed' => 'Remove ILS-suppressed records from Solr', diff --git a/module/VuFindConsole/config/module.config.php b/module/VuFindConsole/config/module.config.php index 584db8061e98068dd35211cf7f53e69b2a720159..b153af319f3e1d45c27f64e8ee18d372f26cff6d 100644 --- a/module/VuFindConsole/config/module.config.php +++ b/module/VuFindConsole/config/module.config.php @@ -10,6 +10,7 @@ $config = [ 'VuFindConsole\Controller\ImportController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFindConsole\Controller\LanguageController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFindConsole\Controller\RedirectController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFindConsole\Controller\ScheduledSearchController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFindConsole\Controller\UtilController' => 'VuFind\Controller\AbstractBaseFactory', ], 'aliases' => [ @@ -19,6 +20,7 @@ $config = [ 'import' => 'VuFindConsole\Controller\ImportController', 'language' => 'VuFindConsole\Controller\LanguageController', 'redirect' => 'VuFindConsole\Controller\RedirectController', + 'scheduledsearch' => 'VuFindConsole\Controller\ScheduledSearchController', 'util' => 'VuFindConsole\Controller\UtilController', ], ], @@ -68,17 +70,20 @@ $routes = [ 'language/copystring' => 'language copystring [<source>] [<target>]', 'language/delete' => 'language delete [<target>]', 'language/normalize' => 'language normalize [<target>]', + 'scheduledsearch/notify' => 'scheduledsearch notify', 'util/cleanup_record_cache' => 'util (cleanuprecordcache|cleanup_record_cache) [--help|-h]', 'util/commit' => 'util commit [<core>]', - 'util/createHierarchyTrees' => 'util createHierarchyTrees [--skip-xml|-sx] [--skip-json|-sj] [--help|-h]', + 'util/createHierarchyTrees' => 'util createHierarchyTrees [--skip-xml|-sx] [--skip-json|-sj] [<backend>] [--help|-h]', 'util/cssBuilder' => 'util cssBuilder [...themes]', 'util/deletes' => 'util deletes [--verbose] [<filename>] [<format>] [<index>]', + 'util/expire_auth_hashes' => 'util expire_auth_hashes [--help|-h] [--batch=] [--sleep=] [<daysOld>]', 'util/expire_external_sessions' => 'util expire_external_sessions [--help|-h] [--batch=] [--sleep=] [<daysOld>]', 'util/expire_searches' => 'util expire_searches [--help|-h] [--batch=] [--sleep=] [<daysOld>]', 'util/expire_sessions' => 'util expire_sessions [--help|-h] [--batch=] [--sleep=] [<daysOld>]', 'util/index_reserves' => 'util index_reserves [--help|-h] [-d=s] [-t=s] [-f=s]', + 'util/lint_marc' => 'util lint_marc [<filename>]', 'util/optimize' => 'util optimize [<core>]', - 'util/sitemap' => 'util sitemap [--verbose]', + 'util/sitemap' => 'util sitemap [--help|-h] [--verbose] [--baseurl=s] [--basesitemapurl=s]', 'util/suppressed' => 'util suppressed [--help|-h] [--authorities] [--outfile=s]', 'util/switch_db_hash' => 'util switch_db_hash [<newhash>] [<newkey>]', ]; diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php b/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php index 78322f74f8bc04ce00ec985a281f6b95bf80d4fb..9f2f31a1af718032814b86353a5e43c68e6641be 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php @@ -117,7 +117,8 @@ class AbstractBase extends AbstractActionController */ public function getConfig($id = 'config') { - return $this->serviceLocator->get('VuFind\Config\PluginManager')->get($id); + return $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class)->get($id); } /** @@ -127,7 +128,7 @@ class AbstractBase extends AbstractActionController */ public function getILS() { - return $this->serviceLocator->get('VuFind\ILS\Connection'); + return $this->serviceLocator->get(\VuFind\ILS\Connection::class); } /** @@ -139,7 +140,7 @@ class AbstractBase extends AbstractActionController */ public function getTable($table) { - return $this->serviceLocator->get('VuFind\Db\Table\PluginManager') + return $this->serviceLocator->get(\VuFind\Db\Table\PluginManager::class) ->get($table); } } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php b/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php index d2696c70ec9ea62c54780d9e25b628f4b2d2eec1..a03e1ec4dd0ddb338355f8697efee923181c9bd6 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php @@ -68,7 +68,7 @@ class CompileController extends AbstractBase if (empty($target)) { $target = "{$source}_compiled"; } - $compiler = $this->serviceLocator->get('VuFindTheme\ThemeCompiler'); + $compiler = $this->serviceLocator->get(\VuFindTheme\ThemeCompiler::class); if (!$compiler->compile($source, $target, $request->getParam('force'))) { Console::writeLine($compiler->getLastError()); return $this->getFailureResponse(); diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php b/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php index aa190de726d9ddc2b7ec136a8d31f7df1215b6bf..7d4d7b53b3a303f6ea6b3cfc7acea2667da2fb03 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php @@ -357,7 +357,7 @@ class GenerateController extends AbstractBase } // Use the theme generator to create and configure the theme: - $generator = $this->serviceLocator->get('VuFindTheme\ThemeGenerator'); + $generator = $this->serviceLocator->get(\VuFindTheme\ThemeGenerator::class); if (!$generator->generate($name) || !$generator->configure($this->getConfig(), $name) ) { @@ -384,7 +384,7 @@ class GenerateController extends AbstractBase } // Use the theme generator to create and configure the theme: - $generator = $this->serviceLocator->get('VuFindTheme\MixinGenerator'); + $generator = $this->serviceLocator->get(\VuFindTheme\MixinGenerator::class); if (!$generator->generate($name)) { Console::writeLine($generator->getLastError()); return $this->getFailureResponse(); @@ -402,6 +402,8 @@ class GenerateController extends AbstractBase */ protected function getGeneratorTools() { - return $this->serviceLocator->get('VuFindConsole\Generator\GeneratorTools'); + return $this->serviceLocator->get( + \VuFindConsole\Generator\GeneratorTools::class + ); } } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php b/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php index fdbb12bff3105c95bf906d751f2f4cbafc6995b0..8db422eb84b3ef026927af0e718c05765d0ee1bc 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php @@ -84,7 +84,7 @@ class HarvestController extends AbstractBase } // Get the default VuFind HTTP client: - $client = $this->serviceLocator->get('VuFindHttp\HttpService') + $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) ->createClient(); // Run the job! diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php b/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php index 8c1ff0c2c23a0296d44b9471515af4669f1db46d..6b5f64e0fcabca0dbf03ee355a5f7f077d3ef7ed 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php @@ -140,7 +140,8 @@ class ImportController extends AbstractBase $testMode = $request->getParam('test-only') ? true : false; $index = $request->getParam('index', 'SolrWeb'); - $configLoader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configLoader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $crawlConfig = $configLoader->get('webcrawl'); // Get the time we started indexing -- we'll delete records older than this @@ -159,7 +160,7 @@ class ImportController extends AbstractBase // Skip Solr operations if we're in test mode. if (!$testMode) { - $solr = $this->serviceLocator->get('VuFind\Solr\Writer'); + $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); if ($verbose) { Console::writeLine("Deleting old records (prior to $startTime)..."); } diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php b/module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php new file mode 100644 index 0000000000000000000000000000000000000000..9db7b83d5c2649030b5f5613fa3fea876396b100 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php @@ -0,0 +1,467 @@ +<?php +/** + * CLI Controller Module (scheduled search tools) + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Controller + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +namespace VuFindConsole\Controller; + +use Zend\Console\Console; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * CLI Controller Module (scheduled search tools) + * + * @category VuFind + * @package Controller + * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> + * @author Ere Maijala <ere.maijala@helsinki.fi> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:controllers Wiki + */ +class ScheduledSearchController extends AbstractBase + implements \VuFind\I18n\Translator\TranslatorAwareInterface +{ + use \VuFind\I18n\Translator\TranslatorAwareTrait; + use \VuFind\I18n\Translator\LanguageInitializerTrait; + + /** + * Useful date format value + * + * @var string + */ + protected $iso8601 = 'Y-m-d\TH:i:s\Z'; + + /** + * HMAC generator + * + * @var \VuFind\Crypt\HMAC + */ + protected $hmac; + + /** + * View renderer + * + * @var \Zend\View\Renderer\PhpRenderer + */ + protected $renderer; + + /** + * URL helper + * + * @var \Zend\View\Helper\Url + */ + protected $urlHelper; + + /** + * Search results plugin manager + * + * @var \VuFind\Search\Results\PluginManager + */ + protected $resultsManager; + + /** + * Configured schedule options + * + * @var array + */ + protected $scheduleOptions; + + /** + * Top-level VuFind configuration + * + * @var \Zend\Config\Config + */ + protected $mainConfig; + + /** + * Number of results to retrieve when performing searches + * + * @var int + */ + protected $limit = 50; + + /** + * Mail service + * + * @var \VuFind\Mailer\Mailer + */ + protected $mailer; + + /** + * Constructor + * + * @param ServiceLocatorInterface $sm Service locator + */ + public function __construct(ServiceLocatorInterface $sm) + { + parent::__construct($sm); + + $this->hmac = $sm->get(\VuFind\Crypt\HMAC::class); + $this->renderer = $sm->get('ViewRenderer'); + $this->urlHelper = $this->renderer->plugin('url'); + $this->resultsManager = $sm->get( + \VuFind\Search\Results\PluginManager::class + ); + $this->scheduleOptions = $sm + ->get(\VuFind\Search\History::class) + ->getScheduleOptions(); + $this->mainConfig = $sm->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $this->mailer = $sm->get(\VuFind\Mailer\Mailer::class); + } + + /** + * Send notifications. + * + * @return \Zend\Console\Response + */ + public function notifyAction() + { + $this->processViewAlerts(); + return $this->getSuccessResponse(); + } + + /** + * Display a message. + * + * @param string $msg Message to display + * + * @return void + */ + protected function msg($msg) + { + Console::writeLine($msg); + } + + /** + * Display a warning. + * + * @param string $msg Message to display + * + * @return void + */ + protected function warn($msg) + { + Console::writeLine('WARNING: ' . $msg); + } + + /** + * Display an error. + * + * @param string $msg Message to display + * + * @return void + */ + protected function err($msg) + { + Console::writeLine('ERROR: ' . $msg); + } + + /** + * Validate the schedule (return true if we should send a message). + * + * @param \DateTime $todayTime The time the notification job started. + * @param \DateTime $lastTime Last time notification was sent. + * @param \VuFind\Db\Row\Search $s Search row to validate. + * + * @return bool + */ + protected function validateSchedule($todayTime, $lastTime, $s) + { + $schedule = $s->notification_frequency; + if (!isset($this->scheduleOptions[$schedule])) { + $this->err('Search ' . $s->id . ": unknown schedule: $schedule"); + return false; + } + $diff = $todayTime->diff($lastTime); + if ($diff->days < $schedule) { + $this->msg( + ' Bypassing search ' . $s->id + . ': previous execution too recent (' + . $this->scheduleOptions[$schedule] . ', ' + . $lastTime->format($this->iso8601) . ')' + ); + return false; + } + return true; + } + + /** + * Load and validate a user object associated with the search; return false + * if there is a problem. + * + * @param \VuFind\Db\Row\Search $s Current search row. + * + * @return \VuFind\Db\Row\User|bool + */ + protected function getUserForSearch($s) + { + // Use a static variable to hold the last accessed user (to spare duplicate + // database lookups, since we're loading rows in user order). + static $user = false; + + if ($user === false || $s->user_id != $user->id) { + if (!$user = $this->getTable('user')->getById($s->user_id)) { + $this->warn( + 'Search ' . $s->id . ': user ' . $s->user_id + . ' does not exist ' + ); + return false; + } + } + if (!$user->email || trim($user->email) == '') { + $this->warn( + 'User ' . $user->username + . ' does not have an email address, bypassing alert ' . $s->id + ); + return false; + } + return $user; + } + + /** + * Set up the translator language. + * + * @param string $userLang User language preference from database (may be empty). + * + * @return void + */ + protected function setLanguage($userLang) + { + // Start with default language setting; override with user language + // preference if set and valid. + $language = $this->mainConfig->Site->language; + if ($userLang != '' + && in_array( + $userLang, + array_keys($this->mainConfig->Languages->toArray()) + ) + ) { + $language = $userLang; + } + $this->translator->setLocale($language); + $this->addLanguageToTranslator($this->translator, $language); + } + + /** + * Load and validate the results object associated with the search; return false + * if there is a problem. + * + * @param \VuFind\Db\Row\Search $s Current search row. + * + * @return \VuFind\Db\Row\User|bool + */ + protected function getObjectForSearch($s) + { + $minSO = $s->getSearchObject(); + $searchObject = $minSO->deminify($this->resultsManager); + if (!$searchObject->getOptions()->supportsScheduledSearch()) { + $this->err( + 'Unsupported search backend ' . $searchObject->getBackendId() + . ' for search ' . $searchObject->getSearchId() + ); + return false; + } + return $searchObject; + } + + /** + * Given a search results object, fetch records that have changed since the last + * search. Return false on error. + * + * @param \VuFind\Search\Base\Results $searchObject Search results object + * @param \DateTime $lastTime Last notification time + * + * @return array|bool + */ + protected function getNewRecords($searchObject, $lastTime) + { + // Prepare query + $params = $searchObject->getParams(); + $params->setLimit($this->limit); + $params->setSort('first_indexed desc', true); + $searchId = $searchObject->getSearchId(); + try { + $records = $searchObject->getResults(); + } catch (\Exception $e) { + $this->err("Error processing search $searchId: " . $e->getMessage()); + return false; + } + if (empty($records)) { + $this->msg( + " No results found for search $searchId" + ); + return false; + } + $newestRecordDate + = date($this->iso8601, strtotime($records[0]->getFirstIndexed())); + $lastExecutionDate = $lastTime->format($this->iso8601); + if ($newestRecordDate < $lastExecutionDate) { + $this->msg( + " No new results for search ($searchId): " + . "$newestRecordDate < $lastExecutionDate" + ); + return false; + } + $this->msg( + " New results for search ($searchId): " + . "$newestRecordDate >= $lastExecutionDate" + ); + // Collect records that have been indexed (for the first time) + // after previous scheduled alert run + $newRecords = []; + foreach ($records as $record) { + $recDate = date($this->iso8601, strtotime($record->getFirstIndexed())); + if ($recDate < $lastExecutionDate) { + break; + } + $newRecords[] = $record; + } + return $newRecords; + } + + /** + * Build the email message. + * + * @param \VuFind\Db\Row\Search $s Search table row + * @param \VuFind\Db\Row\User $user User owning search row + * @param \VuFind\Search\Base\Results $searchObject Search results object + * @param array $newRecords New results in search + * + * @return string + */ + protected function buildEmail($s, $user, $searchObject, $newRecords) + { + $viewBaseUrl = $searchUrl = $s->notification_base_url; + $searchUrl .= $this->urlHelper->__invoke( + $searchObject->getOptions()->getSearchAction() + ) . $searchObject->getUrlQuery()->getParams(false); + $secret = $s->getUnsubscribeSecret($this->hmac, $user); + $unsubscribeUrl = $s->notification_base_url + . $this->urlHelper->__invoke('myresearch-unsubscribe') + . "?id={$s->id}&key=$secret"; + $userInstitution = $this->mainConfig->Site->institution; + $params = $searchObject->getParams(); + // Filter function to only pass along selected checkboxes: + $selectedCheckboxes = function ($data) { + return $data['selected'] ?? false; + }; + $viewParams = [ + 'records' => $newRecords, + 'info' => [ + 'baseUrl' => $viewBaseUrl, + 'description' => $params->getDisplayQuery(), + 'recordCount' => count($newRecords), + 'url' => $searchUrl, + 'unsubscribeUrl' => $unsubscribeUrl, + 'checkboxFilters' => array_filter( + $params->getCheckboxFacets(), $selectedCheckboxes + ), + 'filters' => $params->getFilterList(true), + 'userInstitution' => $userInstitution + ] + ]; + return $this->renderer + ->render('Email/scheduled-alert.phtml', $viewParams); + } + + /** + * Try to send an email message to a user. Return true on success, false on + * error. + * + * @param \VuFind\Db\Row\User $user User to email + * @param string $message Email message body + * + * @return bool + */ + protected function sendEmail($user, $message) + { + $subject = $this->mainConfig->Site->title + . ': ' . $this->translate('Scheduled Alert Results'); + $from = $this->mainConfig->Site->email; + $to = $user->email; + try { + $this->mailer->send($to, $from, $subject, $message); + return true; + } catch (\Exception $e) { + $this->msg( + 'Initial email send failed; resetting connection and retrying...' + ); + } + // If we got this far, the first attempt threw an exception; let's reset + // the connection, then try again.... + $this->mailer->resetConnection(); + try { + $this->mailer->send($to, $from, $subject, $message); + } catch (\Exception $e) { + $this->err( + "Failed to send message to {$user->email}: " . $e->getMessage() + ); + return false; + } + // If we got here, the retry was a success! + return true; + } + + /** + * Send scheduled alerts for a view. + * + * @return void + */ + protected function processViewAlerts() + { + $todayTime = new \DateTime(); + $scheduled = $this->getTable('search')->getScheduledSearches(); + $this->msg(sprintf('Processing %d searches', count($scheduled))); + foreach ($scheduled as $s) { + $lastTime = new \DateTime($s->last_notification_sent); + if (!$this->validateSchedule($todayTime, $lastTime, $s) + || !($user = $this->getUserForSearch($s)) + || !($searchObject = $this->getObjectForSearch($s)) + || !($newRecords = $this->getNewRecords($searchObject, $lastTime)) + ) { + continue; + } + // Set email language + $this->setLanguage($user->last_language); + + // Prepare email content + $message = $this->buildEmail($s, $user, $searchObject, $newRecords); + if (!$this->sendEmail($user, $message)) { + // If email send failed, move on to the next user without updating + // the database table. + continue; + } + $searchTime = date('Y-m-d H:i:s'); + if ($s->setLastExecuted($searchTime) === 0) { + $this->err("Error updating last_executed date for search $searchId"); + } + } + $this->msg('Done processing searches'); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php index 5d29961a9c520f4d0bc53e1a570858c4e9c0875e..644aa0fee48fdc2dcc8938266cec8a62284d6ffa 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php +++ b/module/VuFindConsole/src/VuFindConsole/Controller/UtilController.php @@ -152,7 +152,7 @@ class UtilController extends AbstractBase && !empty($reserves) ) { // Setup Solr Connection - $solr = $this->serviceLocator->get('VuFind\Solr\Writer'); + $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); // Delete existing records $solr->deleteAll('SolrReserves'); @@ -262,7 +262,7 @@ class UtilController extends AbstractBase $core = $this->getRequest()->getParam('core', 'Solr'); // Commit and Optimize the Solr Index - $solr = $this->serviceLocator->get('VuFind\Solr\Writer'); + $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); $solr->commit($core); if ($optimize) { $solr->optimize($core); @@ -277,14 +277,43 @@ class UtilController extends AbstractBase */ public function sitemapAction() { + $request = $this->getRequest(); + if ($request->getParam('help') || $request->getParam('h')) { + Console::writeLine('Generate sitemap files.'); + Console::writeLine(''); + Console::writeLine( + 'Optional parameters: [--verbose] [--baseurl=url]' + . ' [--basesitemapurl=url]' + ); + Console::writeLine(''); + Console::writeLine(' verbose: turn on detailed feedback'); + Console::writeLine( + ' baseurl: define the base url (overrides the url setting in' + . ' Site section of config.ini)' + ); + Console::writeLine( + ' basesitemapurl: define the base sitemap url (overrides the url' + . ' setting in Site section of config.ini, or baseSitemapUrl in' + . ' sitemap.ini)' + ); + Console::writeLine(''); + return $this->getFailureResponse(); + } + // Build sitemap and display appropriate warnings if needed: - $configLoader = $this->serviceLocator->get('VuFind\Config\PluginManager'); + $configLoader = $this->serviceLocator + ->get(\VuFind\Config\PluginManager::class); $generator = new Sitemap( - $this->serviceLocator->get('VuFind\Search\BackendManager'), + $this->serviceLocator->get(\VuFind\Search\BackendManager::class), $configLoader->get('config')->Site->url, $configLoader->get('sitemap') ); - $request = $this->getRequest(); $generator->setVerbose($request->getParam('verbose', false)); + if ($url = $request->getParam('baseurl', false)) { + $generator->setBaseUrl($url); + } + if ($sitemapUrl = $request->getParam('basesitemapurl', false)) { + $generator->setBaseSitemapUrl($sitemapUrl); + } $generator->generate(); foreach ($generator->getWarnings() as $warning) { Console::writeLine("$warning"); @@ -398,7 +427,7 @@ class UtilController extends AbstractBase . implode(', ', $ids) ); } - $writer = $this->serviceLocator->get('VuFind\Solr\Writer'); + $writer = $this->serviceLocator->get(\VuFind\Solr\Writer::class); $writer->deleteRecords($index, $ids); if ($verbose) { Console::writeLine('Delete operation completed.'); @@ -424,7 +453,8 @@ class UtilController extends AbstractBase return $this->getFailureResponse(); } - $recordTable = $this->serviceLocator->get('VuFind\Db\Table\PluginManager') + $recordTable = $this->serviceLocator + ->get(\VuFind\Db\Table\PluginManager::class) ->get('Record'); $count = $recordTable->cleanup(); @@ -526,6 +556,26 @@ class UtilController extends AbstractBase ); } + /** + * Command-line tool to clear unwanted entries + * from auth_hash database table. + * + * @return \Zend\Console\Response + */ + public function expireauthhashesAction() + { + $request = $this->getRequest(); + if ($request->getParam('help') || $request->getParam('h')) { + return $this->expirationHelp('authentication hashes'); + } + + return $this->expire( + \VuFind\Db\Table\AuthHash::class, + '%%count%% expired authentication hashes deleted.', + 'No expired authentication hashes to delete.' + ); + } + /** * Command-line tool to delete suppressed records from the index. * @@ -579,7 +629,7 @@ class UtilController extends AbstractBase } } else { // Default behavior: Get Suppressed Records and Delete from index - $solr = $this->serviceLocator->get('VuFind\Solr\Writer'); + $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); $solr->deleteRecords($backend, $result); $solr->commit($backend); $solr->optimize($backend); @@ -742,17 +792,30 @@ class UtilController extends AbstractBase { $request = $this->getRequest(); if ($request->getParam('help') || $request->getParam('h')) { - Console::writeLine('Available switches:'); - Console::writeLine('--skip-xml or -sx => Skip the XML cache'); - Console::writeLine('--skip-json or -sj => Skip the JSON cache'); - Console::writeLine('--help or -h => Show this message'); + $scriptName = $this->getRequest()->getScriptName(); + if (substr($scriptName, -9) === 'index.php') { + $scriptName .= ' util createHierarchyTrees'; + } + Console::writeLine( + 'Usage: ' . $scriptName + . ' [<backend>] [--skip-xml or -sx] [--skip-json or -sj]' + . ' [--help or -h]' + ); + Console::writeLine( + "\t<backend> => Search backend, e.g. " . DEFAULT_SEARCH_BACKEND + . " (default) or Search2" + ); + Console::writeLine("\t--skip-xml or -sx => Skip the XML cache"); + Console::writeLine("\t--skip-json or -sj => Skip the JSON cache"); + Console::writeLine("\t--help or -h => Show this message"); return $this->getFailureResponse(); } $skipJson = $request->getParam('skip-json') || $request->getParam('sj'); $skipXml = $request->getParam('skip-xml') || $request->getParam('sx'); - $recordLoader = $this->serviceLocator->get('VuFind\Record\Loader'); + $backendId = $request->getParam('backend') ?? DEFAULT_SEARCH_BACKEND; + $recordLoader = $this->serviceLocator->get(\VuFind\Record\Loader::class); $hierarchies = $this->serviceLocator - ->get('VuFind\Search\Results\PluginManager')->get('Solr') + ->get(\VuFind\Search\Results\PluginManager::class)->get($backendId) ->getFullFieldFacets(['hierarchy_top_id']); if (!isset($hierarchies['hierarchy_top_id']['data']['list'])) { $hierarchies['hierarchy_top_id']['data']['list'] = []; @@ -768,7 +831,7 @@ class UtilController extends AbstractBase . number_format($count) . ' records' ); try { - $driver = $recordLoader->load($recordid); + $driver = $recordLoader->load($recordid, $backendId); // Only do this if the record is actually a hierarchy type record if ($driver->getHierarchyType()) { // JSON @@ -811,7 +874,7 @@ class UtilController extends AbstractBase public function cssbuilderAction() { $compiler = new \VuFindTheme\LessCompiler(true); - $cacheManager = $this->serviceLocator->get('VuFind\Cache\Manager'); + $cacheManager = $this->serviceLocator->get(\VuFind\Cache\Manager::class); $cacheDir = $cacheManager->getCacheDir() . 'less/'; $compiler->setTempPath($cacheDir); $compiler->compile(array_unique($this->getRequest()->getParam('themes'))); @@ -970,7 +1033,8 @@ class UtilController extends AbstractBase } // Now do the database rewrite: - $userTable = $this->serviceLocator->get('VuFind\Db\Table\PluginManager') + $userTable = $this->serviceLocator + ->get(\VuFind\Db\Table\PluginManager::class) ->get('User'); $users = $userTable->select( function ($select) { @@ -998,4 +1062,30 @@ class UtilController extends AbstractBase Console::writeLine("\tFinished."); return $this->getSuccessResponse(); } + + /** + * Lint a file of MARC records. + * + * @return \Zend\Console\Response + */ + public function lintmarcAction() + { + $request = $this->getRequest(); + $filename = $request->getParam('filename'); + $marc = substr($filename, -3) !== 'xml' + ? new File_MARC($filename) : new File_MARCXML($filename); + $linter = new \File_MARC_Lint(); + $i = 0; + while ($record = $marc->next()) { + $i++; + $field001 = $record->getField('001'); + $field001 = $field001 ? (string)$field001->getData() : 'undefined'; + Console::writeLine("Checking record $i (001 = $field001)..."); + $warnings = $linter->checkRecord($record); + if (count($warnings) > 0) { + Console::writeLine('Warnings: ' . implode("\n", $warnings)); + } + } + return $this->getSuccessResponse(); + } } diff --git a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php index 204527b24ee67c6f5500597ae0beb5d26fed4182..6996491370f4469147c717a08c371227b2db6ce9 100644 --- a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php +++ b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php @@ -29,6 +29,7 @@ namespace VuFindDevTools\Controller; use VuFind\I18n\Translator\Loader\ExtendedIni; +use VuFind\Search\Results\PluginManager as ResultsManager; use VuFindDevTools\LanguageHelper; /** @@ -55,7 +56,7 @@ class DevtoolsController extends \VuFind\Controller\AbstractBase { try { $backend = $this->serviceLocator - ->get('VuFind\Search\BackendManager') + ->get(\VuFind\Search\BackendManager::class) ->get($id); } catch (\Exception $e) { return null; @@ -78,7 +79,7 @@ class DevtoolsController extends \VuFind\Controller\AbstractBase } if (isset($view->min) && $view->min) { $view->results = $view->min->deminify( - $this->serviceLocator->get('VuFind\Search\Results\PluginManager') + $this->serviceLocator->get(ResultsManager::class) ); } if (isset($view->results) && $view->results) { diff --git a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php index 9ff1aeb5fcbabf032cf849bcb3c8d7f71a091fa6..8037c4bbac83556d96ebcd66945b426896d71f1e 100644 --- a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php +++ b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/Controller/DevtoolsControllerTest.php @@ -84,7 +84,7 @@ class DevtoolsControllerTest extends \VuFindTest\Unit\TestCase protected function getMockController() { $config = new Config(['Languages' => ['en' => 'English']]); - $c = $this->getMockBuilder('VuFindDevTools\Controller\DevtoolsController') + $c = $this->getMockBuilder(\VuFindDevTools\Controller\DevtoolsController::class) ->setMethods(['getConfig'])->disableOriginalConstructor()->getMock(); $c->expects($this->any())->method('getConfig')->will($this->returnValue($config)); return $c; diff --git a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php index 02a09bd4bdc67a7b87b24e9a3a9394ebd05c6507..77ec90569df9da9c08f9caadefffabf0bbbd3d32 100644 --- a/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php +++ b/module/VuFindDevTools/tests/unit-tests/src/VuFindTest/LanguageHelperTest.php @@ -90,7 +90,7 @@ class LanguageHelperTest extends \VuFindTest\Unit\TestCase { $config = new Config(['Languages' => ['en' => 'English']]); return new LanguageHelper( - $this->createMock('VuFind\I18n\Translator\Loader\ExtendedIni'), + $this->createMock(\VuFind\I18n\Translator\Loader\ExtendedIni::class), $config ); } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php index dff9533c693b39fb267c011a44cc1b6cbd5f5bda..758ac6db9e88a2d8a44d661af225956a33973fa6 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/AbstractBackend.php @@ -110,14 +110,11 @@ abstract class AbstractBackend implements BackendInterface, LoggerAwareInterface * * @param ResponseInterface $response Response * - * @return void + * @return ResponseInterface */ protected function injectSourceIdentifier(RecordCollectionInterface $response) { $response->setSourceIdentifier($this->identifier); - foreach ($response as $record) { - $record->setSourceIdentifier($this->identifier); - } return $response; } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php index d9a55f933b4f3dbb28d2618ffdef1f92fbdc86ee..6de40da175cd3d62bd2717215503393dfbbdd3d3 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/BrowZine/Response/RecordCollectionFactory.php @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['data'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php index cad62f92f8e54827dab2c7a16978061467d1d532..12e2cd3f5c74292153ba1ce138d41eaed34627f9 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Backend.php @@ -421,7 +421,7 @@ class Backend extends AbstractBackend } $authTokenData = $this->cache->getItem('edsAuthenticationToken'); if (isset($authTokenData)) { - $currentToken = $authTokenData['token'] ?? ''; + $currentToken = $authTokenData['token'] ?? ''; $expirationTime = $authTokenData['expiration'] ?? 0; $this->debugPrint( 'Cached Authentication data: ' @@ -473,7 +473,7 @@ class Backend extends AbstractBackend } $autocompleteData = $this->cache->getItem('edsAutocomplete'); if (!empty($autocompleteData)) { - $currentToken = $autocompleteData['token'] ?? ''; + $currentToken = $autocompleteData['token'] ?? ''; $expirationTime = $autocompleteData['expiration'] ?? 0; // Check to see if the token expiration time is greater than the current diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php index 7c5e702ab5dfaccc98038e9c0a09cc58b895a98f..79fefed753ef358425861709d79f2865087e29fb 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/QueryBuilder.php @@ -118,7 +118,7 @@ class QueryBuilder */ protected function queryGroupToArray(QueryGroup $query) { - $groups = []; + $groups = []; foreach ($query->getQueries() as $params) { // Advanced Search if ($params instanceof QueryGroup) { diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php index 577e8558e53988b8a1be6773987af5a1ccaf9fa7..6fab79eebced0d4f291da1fff669eeaa4872131c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Response/RecordCollectionFactory.php @@ -98,7 +98,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface ?? $response['Records'] ?? []; foreach ($records as $record) { - $collection->add(call_user_func($this->recordFactory, $record)); + $collection->add(call_user_func($this->recordFactory, $record), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php index 9d115379964b62b139c7dd887c1f92926892ff02..6b5bc3d5fce5ac48813fc0955fdc8a5eaae85deb 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EDS/Zend2.php @@ -77,18 +77,8 @@ class Zend2 extends Base implements LoggerAwareInterface * conjunction with the EDS API * <ul> * <li>debug - boolean to control debug mode</li> - * <li>authtoken - Authentication to use for calls to the API. If using IP - * Authentication, this is not needed.</li> - * <li>username - EBSCO username for account setup for usage with the EDS - * API. This is only required for institutions using UID Authentication </li> - * <li>password - EBSCO password for account setup for usage with the EDS - * API. This is only required for institutions using UID Authentication </li> - * <li>orgid - Organization making calls to the EDS API </li> - * <li>sessiontoken - SessionToken this call is associated with, is one - * exists. If not, the a profile value must be present </li> - * <li>profile - EBSCO profile to use for calls to the API. </li> - * <li>isguest - is the user a guest. This needs to be present if there - * is no session token present</li> + * <li>orgid - Organization making calls to the EDS API</li> + * <li>timeout - HTTP timeout value (default = 120)</li> * </ul> * @param Zend2HttpClient $client Zend2 HTTP client object (optional) */ @@ -96,7 +86,7 @@ class Zend2 extends Base implements LoggerAwareInterface { parent::__construct($settings); $this->client = is_object($client) ? $client : new Zend2HttpClient(); - $this->client->setOptions(['timeout' => 120]); + $this->client->setOptions(['timeout' => $settings['timeout'] ?? 120]); $adapter = new CurlAdapter(); $adapter->setOptions( [ diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php index 341d74e183a453bec19068f2eb0416ea583f95f4..c2c9543322fdf50f38a1ea23a1df0c611e6b5fa0 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Backend.php @@ -138,8 +138,6 @@ class Backend extends AbstractBackend * @param QueryBuilder $queryBuilder Query builder * * @return void - * - * @todo Typehint QueryBuilderInterface */ public function setQueryBuilder(QueryBuilder $queryBuilder) { diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php index 9c237a60adb076b7c418bc7d2266b66e03a2d535..170774149e2961a80fed4f9cf5a7e4ce0a706477 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/EIT/Response/XML/RecordCollectionFactory.php @@ -96,7 +96,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['docs'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php index ede139d3023dd96bdb063e9c21bbbd768c82cbf5..fada0c1063468f1a763dd280de0278793adada7c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/LibGuides/Response/RecordCollectionFactory.php @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['documents'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php index c8acc2dac29c2ceef86315815042106bd4e4651e..e624040d54e61a64662fcd16f7ec0e4424abaad1 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Pazpar2/Response/RecordCollectionFactory.php @@ -93,7 +93,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $response['total'], $response['offset'] ); foreach ($response['records'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php index 56d22647e0c0c7639610e84faf834f4cd3758d7d..dacafdfa4a3e8ab0b1d436f7f20233c0e3c09146 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php @@ -83,7 +83,7 @@ class Connector implements \Zend\Log\LoggerAwareInterface 'recordCount' => 0, 'documents' => [], 'facets' => [], - 'error' => 'Primo does not accept an empty query' + 'error' => 'empty_search_disallowed' ]; /** @@ -284,10 +284,27 @@ class Connector implements \Zend\Log\LoggerAwareInterface // range facet control in the interface. look for injectPubDate if (!empty($args["filterList"])) { foreach ($args["filterList"] as $facet => $values) { - foreach ($values as $value) { - $thisValue = preg_replace('/,/', '+', $value); - $qs[] = "query=facet_" . $facet . ",exact," - . urlencode($thisValue); + $facetOp = 'AND'; + if (isset($values['values'])) { + $facetOp = $values['facetOp']; + $values = $values['values']; + } + array_map( + function ($value) { + return urlencode(preg_replace('/,/', '+', $value)); + }, + $values + ); + if ('OR' === $facetOp) { + $qs[] = "query_inc=facet_$facet,exact," . + implode(',', $values); + } elseif ('NOT' === $facetOp) { + $qs[] = "query_exc=facet_$facet,exact," . + implode(',', $values); + } else { + foreach ($values as $value) { + $qs[] = "query_inc=facet_$facet,exact,$value"; + } } } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php index 58ca36264968ecd2fcefc71f508d49c86143fbc9..f5392e4627783619c0716d38026cc0927576767c 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollection.php @@ -93,4 +93,14 @@ class RecordCollection extends AbstractRecordCollection $size = $this->response['query']['pageSize'] ?? 0; return $page * $size; } + + /** + * Return any errors. + * + * @return array + */ + public function getErrors() + { + return (array)($this->response['error'] ?? []); + } } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php index 210ca51b7b82e2b5f2b2cd3793d81a9b6bacb8d1..7837856a72526aec571b862d19b3ca4644e4b665 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Response/RecordCollectionFactory.php @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['documents'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php index 5b175d86f62b281922ba269c1e897aa145b03666..2075717d888456c2417a0ed60655cdcce98a5cc2 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Connector.php @@ -66,9 +66,9 @@ class Connector implements \Zend\Log\LoggerAwareInterface /** * Maximum length of a GET url. * - * Switches to POST if the SOLR target URL exeeds this length. + * Switches to POST if the SOLR target URL exceeds this length. * - * @see self::query() + * @see \VuFindSearch\Backend\Solr\Connector::query() * * @var int */ diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php index 39c4ecbf140350c0054297eb103895067bcb9546..89dd7ef53b5e50a547b7358c6639f96e0cffc6e6 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/LuceneSyntaxHelper.php @@ -485,21 +485,21 @@ class LuceneSyntaxHelper */ protected function normalizeUnquotedText($input) { - // Freestanding hyphens and slashes can cause problems: + // Freestanding hyphens, pluses and slashes can cause problems: $lookahead = self::$insideQuotes; - // remove freestanding hyphens + // remove freestanding hyphens and pluses $input = preg_replace( - '/(\s+[-]$|\s+[-]\s+|^[-]\s+)' . $lookahead . '/', + '/(\s+[+-]+$|\s+[+-]+\s+|^[+-]+\s+)' . $lookahead . '/', ' ', $input ); // wrap quotes on standalone slashes $input = preg_replace( - '/(\s+[\/]\s+)' . $lookahead . '/', + '/(\s+[\/]+\s+)' . $lookahead . '/', ' "/" ', $input ); // remove trailing and leading slashes $input = preg_replace( - '/(\s+[\/]$|^[\/]\s+)' . $lookahead . '/', + '/(\s+[\/]+$|^[\/]+\s+)' . $lookahead . '/', ' ', $input ); // A proximity of 1 is illegal and meaningless -- remove it: @@ -626,7 +626,7 @@ class LuceneSyntaxHelper * * @return string * - * @see self::capitalizeRanges + * @see \VuFindSearch\Backend\Solr\LuceneSyntaxHelper::capitalizeRanges() * * @todo Check possible problem with umlauts/non-ASCII word characters */ diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php index d5c42e0690a03ae505aa67fd09e9b34296cd8df1..d846dd55fc2a7820a11260a4a5c510afa90e776a 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilder.php @@ -144,6 +144,7 @@ class QueryBuilder implements QueryBuilderInterface $highlight = !empty($this->fieldsToHighlight); if ($handler = $this->getSearchHandler($finalQuery->getHandler(), $string)) { + $string = $handler->preprocessQueryString($string); if (!$handler->hasExtendedDismax() && $this->getLuceneHelper()->containsAdvancedLuceneSyntax($string) ) { @@ -181,24 +182,6 @@ class QueryBuilder implements QueryBuilderInterface return $params; } - /** - * Control whether or not the QueryBuilder should create an hl.q parameter - * when the main query includes clauses that should not be factored into - * highlighting. (Turned off by default). - * - * @param bool $enable Should highlighting query generation be enabled? - * - * @return void - * - * @deprecated - */ - public function setCreateHighlightingQuery($enable) - { - // This is deprecated, but use it to manipulate the highlighted field - // list for backward compatibility. - $this->fieldsToHighlight = $enable ? '*' : ''; - } - /** * Get list of fields to highlight, filtered by array. * @@ -349,7 +332,7 @@ class QueryBuilder implements QueryBuilderInterface * * @return string * - * @see self::reduceQueryGroup() + * @see \VuFindSearch\Backend\Solr\QueryBuilder::reduceQueryGroup() */ protected function reduceQueryGroupComponents(AbstractQuery $component) { diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php index e34db5487b3ed44c55c17a1054d5f216628df40f..d5336648fb7110fffd07d53a3c7ff051558a6e32 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/QueryBuilderInterface.php @@ -55,17 +55,6 @@ interface QueryBuilderInterface */ public function build(AbstractQuery $query); - /** - * Control whether or not the QueryBuilder should create an hl.q parameter - * when the main query includes clauses that should not be factored into - * highlighting. (Turned off by default). - * - * @param bool $enable Should highlighting query generation be enabled? - * - * @return void - */ - public function setCreateHighlightingQuery($enable); - /** * Control whether or not the QueryBuilder should create a spellcheck.q * parameter. (Turned off by default). diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php index 7ab8c7ae4e7e542553983b929ab6efdf71c5d450..fbfbafac924872b3fad04fae25483f3886e76535 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollection.php @@ -44,7 +44,7 @@ class RecordCollection extends AbstractRecordCollection /** * Template of deserialized SOLR response. * - * @see self::__construct() + * @see \VuFindSearch\Backend\Solr\Response\Json\RecordCollection::__construct() * * @var array */ @@ -148,6 +148,16 @@ class RecordCollection extends AbstractRecordCollection return $this->response['highlighting'] ?? []; } + /** + * Get cursorMark. + * + * @return string + */ + public function getCursorMark() + { + return $this->response['nextCursorMark'] ?? ''; + } + /** * Get raw Solr input parameters from the response. * diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php index 6ffd4eff6c4cf0642d7be5bd6febdf5757871927..3618d941906b97158f4d79fdc08a7cec6805e254 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/RecordCollectionFactory.php @@ -97,7 +97,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface $collection = new $this->collectionClass($response); if (isset($response['response']['docs'])) { foreach ($response['response']['docs'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } } return $collection; diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php index 12d1a70b4495f95f8b1bc5e3de78e5c91a59bbe1..d11dd237146a035216b267a541135ac08bff3223 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Response/Json/Spellcheck.php @@ -165,7 +165,7 @@ class Spellcheck implements IteratorAggregate, Countable * * @return bool */ - protected function contains($term) + protected function contains(string $term) { if ($this->terms->offsetExists($term)) { return true; diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php index 0083c7573d6315365af84c4176a4b62b209e7a83..8355981304783e8770ffb9127b28525555dc4e02 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/SearchHandler.php @@ -53,7 +53,7 @@ class SearchHandler */ protected static $configKeys = [ 'CustomMunge', 'DismaxFields', 'DismaxHandler', 'QueryFields', - 'DismaxParams', 'FilterQuery' + 'DismaxParams', 'FilterQuery', 'DismaxMunge' ]; /** @@ -125,6 +125,22 @@ class SearchHandler return $this->createQueryString($search, false); } + /** + * Apply standard pre-processing to the query string. + * + * @param string $search Search string + * + * @return string + */ + public function preprocessQueryString($search) + { + // Apply Dismax munging, if required: + if ($this->hasDismax()) { + return $this->dismaxMunge($search); + } + return $search; + } + /** * Return an advanced query string for specified search string. * @@ -381,31 +397,64 @@ class SearchHandler foreach ($this->specs['CustomMunge'] as $mungeName => $mungeOps) { $mungeValues[$mungeName] = $search; foreach ($mungeOps as $operation) { - switch ($operation[0]) { - case 'append': - $mungeValues[$mungeName] .= $operation[1]; - break; - case 'lowercase': - $mungeValues[$mungeName] = strtolower($mungeValues[$mungeName]); - break; - case 'preg_replace': - $mungeValues[$mungeName] = preg_replace( - $operation[1], $operation[2], $mungeValues[$mungeName] - ); - break; - case 'uppercase': - $mungeValues[$mungeName] = strtoupper($mungeValues[$mungeName]); - break; - default: - throw new \InvalidArgumentException( - sprintf('Unknown munge operation: %s', $operation[0]) - ); - } + $mungeValues[$mungeName] + = $this->customMunge($mungeValues[$mungeName], $operation); } } return $mungeValues; } + /** + * Apply custom search string munging to a Dismax query. + * + * @param string $search searchstring + * + * @return string + */ + protected function dismaxMunge($search) + { + foreach ($this->specs['DismaxMunge'] as $operation) { + $search = $this->customMunge($search, $operation); + } + return $search; + } + + /** + * Apply a munge operation to a search string. + * + * @param string $string string to munge + * @param array $operation munge operation + * + * @return string + */ + protected function customMunge($string, $operation) + { + switch ($operation[0]) { + case 'append': + $string .= $operation[1]; + break; + case 'lowercase': + $string = strtolower($string); + break; + case 'preg_replace': + $string = preg_replace( + $operation[1], $operation[2], $string + ); + break; + case 'ucfirst': + $string = ucfirst($string); + break; + case 'uppercase': + $string = strtoupper($string); + break; + default: + throw new \InvalidArgumentException( + sprintf('Unknown munge operation: %s', $operation[0]) + ); + } + return $string; + } + /** * Return query string for specified search string. * @@ -423,7 +472,9 @@ class SearchHandler // Extended Dismax available), let's build a Dismax subquery to avoid // some of the ugly side effects of our Lucene query generation logic. if (($this->hasExtendedDismax() || !$advanced) && $this->hasDismax()) { - $query = $this->dismaxSubquery($search); + $query = $this->dismaxSubquery( + $this->dismaxMunge($search) + ); } else { $mungeRules = $this->mungeRules(); // Do not munge w/o rules diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php index fbfb17f124b3ac8d33be1d525b2973027ae6dadc..aa319b2d92f932414694d2ed8320f2ebf227346a 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/Summon/Response/RecordCollectionFactory.php @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['documents'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php index 38a35c4f428f5bd9f72c9f59b21875f07a7d814e..29c727c73c120f839570aed6ba5ddc7591fab996 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php @@ -100,7 +100,7 @@ class RecordCollectionFactory implements RecordCollectionFactoryInterface } $collection = new $this->collectionClass($response); foreach ($response['docs'] as $doc) { - $collection->add(call_user_func($this->recordFactory, $doc)); + $collection->add(call_user_func($this->recordFactory, $doc), false); } return $collection; } diff --git a/module/VuFindSearch/src/VuFindSearch/Query/Query.php b/module/VuFindSearch/src/VuFindSearch/Query/Query.php index 2e3d3731461945ec08cc730c89230ca643ac5551..f5e3d889631e446f8f6c867486306219f30fd895 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/Query.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/Query.php @@ -84,6 +84,28 @@ class Query extends AbstractQuery return $this->queryString; } + /** + * Apply normalization to a string. + * + * @param string $text String to normalize. + * + * @return string + */ + protected function normalizeText($text) + { + return strtolower($this->stripDiacritics($text)); + } + + /** + * Return search string in a normalized format. + * + * @return string + */ + public function getNormalizedString() + { + return $this->normalizeText($this->queryString); + } + /** * Set the search string. * @@ -156,6 +178,24 @@ class Query extends AbstractQuery return (bool)preg_match("/\b$needle\b/u", $this->getString()); } + /** + * Does the query contain the specified term when comparing normalized strings? + * + * @param string $needle Term to check + * + * @return bool + */ + public function containsNormalizedTerm($needle) + { + // Escape characters with special meaning in regular expressions to avoid + // errors: + $needle = preg_quote($this->normalizeText($needle), '/'); + + return (bool)preg_match( + "/\b$needle\b/u", $this->getNormalizedString() + ); + } + /** * Get a concatenated list of all query strings within the object. * @@ -169,16 +209,19 @@ class Query extends AbstractQuery /** * Replace a term. * - * @param string $from Search term to find - * @param string $to Search term to insert + * @param string $from Search term to find + * @param string $to Search term to insert + * @param boolean $normalize If we should apply text normalization when replacing * * @return void */ - public function replaceTerm($from, $to) + public function replaceTerm($from, $to, $normalize = false) { // Escape $from so it is regular expression safe (just in case it // includes any weird punctuation -- unlikely but possible): - $from = preg_quote($from, '/'); + $from = preg_quote($normalize ? $this->normalizeText($from) : $from, '/'); + $queryString = $normalize + ? $this->getNormalizedString() : $this->queryString; // If our "from" pattern contains non-word characters, we can't use word // boundaries for matching. We want to try to use word boundaries when @@ -191,6 +234,24 @@ class Query extends AbstractQuery } // Perform the replacement: - $this->queryString = preg_replace($pattern, $to, $this->queryString); + $this->queryString = preg_replace($pattern, $to, $queryString); + } + + /** + * Remove diacritics (accents, umlauts, etc.) from a string + * + * @param string $string The text where we would like to remove diacritics + * + * @return string The input text with diacritics removed + */ + protected function stripDiacritics($string) + { + // See http://userguide.icu-project.org/transforms/general for + // an explanation of this. + $transliterator = \Transliterator::createFromRules( + ':: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', + \Transliterator::FORWARD + ); + return $transliterator->transliterate($string); } } diff --git a/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php b/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php index 3a03e2af1c8c5da1170eaada30679cadd980cbe9..0182d1b2f73a5fdc54bec48061029806542fcbbe 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/QueryGroup.php @@ -51,7 +51,7 @@ class QueryGroup extends AbstractQuery /** * Name of the handler to be used if the query group is reduced. * - * @see VuFindSearch\Backend\Solr\QueryBuilder::reduceQueryGroup() + * @see \VuFindSearch\Backend\Solr\QueryBuilder::reduceQueryGroup() * * @var string * @@ -253,6 +253,23 @@ class QueryGroup extends AbstractQuery return false; } + /** + * Does the query contain the specified term when comparing normalized strings? + * + * @param string $needle Term to check + * + * @return bool + */ + public function containsNormalizedTerm($needle) + { + foreach ($this->getQueries() as $q) { + if ($q->containsNormalizedTerm($needle)) { + return true; + } + } + return false; + } + /** * Get a concatenated list of all query strings within the object. * @@ -270,15 +287,16 @@ class QueryGroup extends AbstractQuery /** * Replace a term. * - * @param string $from Search term to find - * @param string $to Search term to insert + * @param string $from Search term to find + * @param string $to Search term to insert + * @param boolean $normalize If we should apply text normalization when replacing * * @return void */ - public function replaceTerm($from, $to) + public function replaceTerm($from, $to, $normalize = false) { foreach ($this->getQueries() as $q) { - $q->replaceTerm($from, $to); + $q->replaceTerm($from, $to, $normalize); } } } diff --git a/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php b/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php index f2458f7bc651636c51ebcd9c7c1e33bce0a8dd44..b375a839f77faae216c1f6fb2cc7acb3d2cf6868 100644 --- a/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php +++ b/module/VuFindSearch/src/VuFindSearch/Query/QueryInterface.php @@ -39,6 +39,15 @@ namespace VuFindSearch\Query; */ interface QueryInterface { + /** + * Does the query contain the specified term when comparing normalized strings? + * + * @param string $needle Term to check + * + * @return bool + */ + public function containsNormalizedTerm($needle); + /** * Does the query contain the specified term? * @@ -58,10 +67,11 @@ interface QueryInterface /** * Replace a term. * - * @param string $from Search term to find - * @param string $to Search term to insert + * @param string $from Search term to find + * @param string $to Search term to insert + * @param boolean $normalize If we should apply text normalization when replacing * * @return void */ - public function replaceTerm($from, $to); + public function replaceTerm($from, $to, $normalize = false); } diff --git a/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php index 45b973533820c51772758bcbb4dea66dbffacbd8..196744af486e26b4e4c7d15c560d9ecf90685dae 100644 --- a/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php +++ b/module/VuFindSearch/src/VuFindSearch/Response/AbstractRecordCollection.php @@ -77,6 +77,16 @@ abstract class AbstractRecordCollection implements RecordCollectionInterface return $this->records; } + /** + * Return any errors. + * + * @return array + */ + public function getErrors() + { + return []; + } + /** * Shuffles records. * @@ -117,6 +127,9 @@ abstract class AbstractRecordCollection implements RecordCollectionInterface public function setSourceIdentifier($identifier) { $this->source = $identifier; + foreach ($this->records as $record) { + $record->setSourceIdentifier($identifier); + } } /** @@ -132,18 +145,32 @@ abstract class AbstractRecordCollection implements RecordCollectionInterface /** * Add a record to the collection. * - * @param RecordInterface $record Record to add + * @param RecordInterface $record Record to add + * @param bool $checkExisting Whether to check for existing record in + * the collection (slower, but makes sure there are no duplicates) * * @return void */ - public function add(RecordInterface $record) + public function add(RecordInterface $record, $checkExisting = true) { - if (!in_array($record, $this->records, true)) { + if (!$checkExisting || !$this->has($record)) { $this->records[$this->pointer] = $record; $this->next(); } } + /** + * Check if the collection contains the given record + * + * @param RecordInterface $record Record to check + * + * @return bool + */ + public function has(RecordInterface $record) + { + return in_array($record, $this->records, true); + } + /** * Replace a record in the collection. * diff --git a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/morelikethis b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/morelikethis index 493e80ab0e7e292a0cc4d8360c52faa927c27639..a7d1e30c65a83d1ae02c6f848a4e4e5cbf5798a8 100644 --- a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/morelikethis +++ b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/morelikethis @@ -3,5 +3,5 @@ Date: Thu, 11 Oct 2012 07:56:30 GMT Last-Modified: Thu, 11 Oct 2012 07:05:29 GMT Server: Jetty(6.1.11) -{"responseHeader":{"status":0,"QTime":22},"match":{"numFound":1,"start":0,"maxScore":14.28401,"docs":[{"genre":["Domestic fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["170 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"2001033643","callnumber-a":"PS3552.R878","publisher":["Feminist Press at the City University of New York,"],"id":"704640","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","The test /","Adult children of aging parents Fiction","Automobile drivers' tests Fiction","Fathers and daughters Fiction","Middle aged women Fiction","Older men Fiction"],"title":"The test /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","The test / Dorothy Bryant ; afterword by Barbara Horn.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 2001.","170 p. ; 22 cm.","Originally published: Ata Books, Berkeley, Calif., 1991.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","Includes bibliographical references (p. 169-170).","May06eng","Adult children of aging parents Fiction.","Automobile drivers' tests Fiction.","Fathers and daughters Fiction.","Middle aged women Fiction.","Older men Fiction.","Domestic fiction. lcsh","MAIN PS3552.R878 T47 2001"],"last_indexed":"2012-09-27T21:19:18Z","isbn":["1558612742 (pbk. : alk. paper)"],"callnumber-label":"PS3552","publishDate":["2001"],"institution":["Villanova University"],"recordtype":"marc","oclc_num":["47013195"],"topic":["Adult children of aging parents Fiction","Automobile drivers' tests Fiction","Fathers and daughters Fiction","Middle aged women Fiction","Older men Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"The test /","callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878T472001","topic_facet":["Adult children of aging parents","Automobile drivers' tests","Fathers and daughters","Middle aged women","Older men"],"format":["Book"],"title_sort":"test","title_short":"The test /","ctrlnum":["(OCoLC)ocm47013195"],"fullrecord":"01252cam a2200337 a 4500001000700000005001700007008004100024010001700065035002300082040003700105020003500142050002600177049000900203100002800212245006000240250002700300260007400327300002100401500006100422500011400483504005400597590001300651650004600664650004000710650003600750650003200786650002400818655002800842994001200870852003200882#30;704640#30;20060525093449.0#30;060508r20011991nyu b 000 1 eng #30; #31;a 2001033643#30; #31;a(OCoLC)ocm47013195#30; #31;aDLC#31;cDLC#31;dYDX#31;dOCLCQ#31;dBAKER#31;dPVU#30; #31;a1558612742 (pbk. : alk. paper)#30;00#31;aPS3552.R878#31;bT47 2001#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;14#31;aThe test /#31;cDorothy Bryant ; afterword by Barbara Horn.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c2001.#30; #31;a170 p. ;#31;c22 cm.#30; #31;aOriginally published: Ata Books, Berkeley, Calif., 1991.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aIncludes bibliographical references (p. 169-170).#30; #31;aMay06eng#30; 0#31;aAdult children of aging parents#31;vFiction.#30; 0#31;aAutomobile drivers' tests#31;vFiction.#30; 0#31;aFathers and daughters#31;vFiction.#30; 0#31;aMiddle aged women#31;vFiction.#30; 0#31;aOlder men#31;vFiction.#30; 7#31;aDomestic fiction.#31;2lcsh#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iT47 2001#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"The test / Dorothy Bryant ; afterword by Barbara Horn.","title_fullStr":"The test / Dorothy Bryant ; afterword by Barbara Horn.","title_full_unstemmed":"The test / Dorothy Bryant ; afterword by Barbara Horn.","genre_facet":["Fiction.","Domestic fiction."],"_version_":1446541939723730944,"score":14.28401}]},"response":{"numFound":1079840,"start":0,"maxScore":2.3629262,"docs":[{"genre":["Psychological fiction. lcsh","Feminist fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["186 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"97016088","callnumber-a":"PS3552.R878","publisher":["Feminist Press at the City University of New York,"],"id":"704635","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","Miss Giardino /","Italian American women Fiction","High school teachers Fiction","Retired teachers Fiction","Women teachers Fiction","Older women Fiction"],"title":"Miss Giardino /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 1997.","186 p. ; 22 cm.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","Includes bibliographical references (p. 181-185).","May06eng","Italian American women Fiction.","High school teachers Fiction.","Retired teachers Fiction.","Women teachers Fiction.","San Francisco (Calif.) Fiction.","Older women Fiction.","Psychological fiction. lcsh","Feminist fiction. lcsh","MAIN PS3552.R878 M5 1997"],"last_indexed":"2012-09-27T21:19:18Z","isbn":["1558611746 (pbk. : alk. paper)"],"callnumber-label":"PS3552","publishDate":["1997"],"institution":["Villanova University"],"recordtype":"marc","oclc_num":["36776451"],"topic":["Italian American women Fiction","High school teachers Fiction","Retired teachers Fiction","Women teachers Fiction","Older women Fiction"],"building":["Falvey Library"],"geographic":["San Francisco (Calif.) Fiction"],"callnumber-first":"P - Language and Literature","title_auth":"Miss Giardino /","geographic_facet":["San Francisco (Calif.)"],"callnumber":"PS3552.R878M51997","callnumber-subject":"PS - American Literature","topic_facet":["Italian American women","High school teachers","Retired teachers","Women teachers","Older women"],"format":["Book"],"title_sort":"miss giardino","title_short":"Miss Giardino /","ctrlnum":["(OCoLC)ocm36776451"],"fullrecord":"01267cam a2200361 a 4500001000700000005001700007008004100024010001700065035002300082040002500105020003500130043001200165050002500177049000900202100002800211245006400239250002700303260007400330300002100404500011400425504005400539590001300593650003700606650003500643650003100678650002900709651003700738650002600775655003300801655002800834994001200862852003100874#30;704635#30;20060525110908.0#30;060508r19971978nyu b 000 1 eng #30; #31;a 97016088 #30; #31;a(OCoLC)ocm36776451#30; #31;aDLC#31;cDLC#31;dBAKER#31;dPVU#30; #31;a1558611746 (pbk. : alk. paper)#30; #31;an-us-ca#30;00#31;aPS3552.R878#31;bM5 1997#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aMiss Giardino /#31;cDorothy Bryant ; afterword by Janet Zandy.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c1997.#30; #31;a186 p. ;#31;c22 cm.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aIncludes bibliographical references (p. 181-185).#30; #31;aMay06eng#30; 0#31;aItalian American women#31;vFiction.#30; 0#31;aHigh school teachers#31;vFiction.#30; 0#31;aRetired teachers#31;vFiction.#30; 0#31;aWomen teachers#31;vFiction.#30; 0#31;aSan Francisco (Calif.)#31;vFiction.#30; 0#31;aOlder women#31;vFiction.#30; 7#31;aPsychological fiction.#31;2lcsh#30; 7#31;aFeminist fiction.#31;2lcsh#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iM5 1997#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","title_fullStr":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","title_full_unstemmed":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","genre_facet":["Fiction.","Psychological fiction.","Feminist fiction."],"_version_":1446541939715342336,"score":2.3629262},{"genre":["Psychological fiction. lcsh","Diary fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["265 p. ; 22 cm."],"dewey-tens":["810 - American literature in English"],"callnumber-a":"PS3552.R878","lccn":"97014405","callnumber-subject-code":"PS","dewey-hundreds":["800 - Literature"],"title_sub":"a novel /","publishDate":["1997"],"recordtype":"marc","topic":["Middle aged women California Education Fiction","Married women California Psychology Fiction"],"spellingShingle":["Middle aged women California Education Fiction","Married women California Psychology Fiction","Bryant, Dorothy, 1930-","Ella Price's journal : a novel /"],"geographic_facet":["California"],"callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878E431997","dewey-sort-browse":["813.00000000"],"format":["Book"],"title_short":"Ella Price's journal :","title_sort":"ella price's journal :a novel","fullrecord":"01121cam a2200313 a 4500001000700000005001700007008004100024010001700065035002000082040002300102020003600125020003500161043001200196050002600208082001600234049000900250100002800259245008600287250002700373260007400400300002100474500006500495504005000560650005500610650005200665655003300717655002500750852003200775#30;476297#30;19980910105451.0#30;980910r19971972nyu b 000 1 eng #30; #31;a 97014405 #30; #31;a(OCoLC)36767574#30; #31;aDLC#31;cDLC#31;dC#P#31;dPVU#30; #31;a1558611819 (cloth : alk. paper)#30; #31;a1558611754 (pbk. : alk. paper)#30; #31;an-us-ca#30;00#31;aPS3552.R878#31;bE43 1997#30;00#31;a813/.54#31;221#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aElla Price's journal :#31;ba novel /#31;cby Dorothy Bryant ; afterword by Barbara Horn.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c1997.#30; #31;a265 p. ;#31;c22 cm.#30; #31;a\"Originally published by J.B. Lippincott, 1972\"--T.p. verso.#30; #31;aIncludes bibliographical references (p. 264).#30; 0#31;aMiddle aged women#31;zCalifornia#31;xEducation#31;xFiction.#30; 0#31;aMarried women#31;zCalifornia#31;xPsychology#31;xFiction.#30; 7#31;aPsychological fiction.#31;2lcsh#30; 7#31;aDiary fiction.#31;2lcsh#30;0 #31;bMAIN#31;hPS3552.R878#31;iE43 1997#30;#29;","dewey-ones":["813 - American fiction in English"],"ctrlnum":["(OCoLC)36767574"],"marc_error":["Typo : Erroneous character found at end of leader [ 45e0 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"dewey-raw":["813/.54"],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"publisher":["Feminist Press at the City University of New York,"],"id":"476297","author":"Bryant, Dorothy, 1930-","title":"Ella Price's journal : a novel /","spelling":["Bryant, Dorothy, 1930-","Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 1997.","265 p. ; 22 cm.","\"Originally published by J.B. Lippincott, 1972\"--T.p. verso.","Includes bibliographical references (p. 264).","Middle aged women California Education Fiction.","Married women California Psychology Fiction.","Psychological fiction. lcsh","Diary fiction. lcsh","MAIN PS3552.R878 E43 1997"],"last_indexed":"2012-09-27T19:12:22Z","isbn":["1558611819 (cloth : alk. paper)","1558611754 (pbk. : alk. paper)"],"callnumber-label":"PS3552","institution":["Villanova University"],"oclc_num":["36767574"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Ella Price's journal : a novel /","dewey-sort":"813.00000000","topic_facet":["Middle aged women","Married women","Education","Fiction.","Psychology"],"dewey-full":["813/.54"],"first_indexed":"2012-09-27T19:12:22Z","genre_facet":["Psychological fiction.","Diary fiction."],"title_full":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","title_fullStr":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","title_full_unstemmed":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","_version_":1446540985586679809,"score":2.1145177},{"edition":"1st ed.","physical":["303 p. : 1 map ; 23 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"93072365","callnumber-a":"PS3552.R878","publisher":["Ata Books,"],"id":"704643","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","Anita, Anita : Garibaldi of the New World : a novel /","Garibaldi, Anita, 1821?-1849 Fiction","Garibaldi, Giuseppe, 1807-1882 Fiction"],"title":"Anita, Anita : Garibaldi of the New World : a novel /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","1st ed.","Berkeley, Calif. : Ata Books, c1993.","303 p. : 1 map ; 23 cm.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","May06eng","Garibaldi, Anita, 1821?-1849 Fiction.","Garibaldi, Giuseppe, 1807-1882 Fiction.","MAIN PS3552.R878 A83 1993"],"title_sub":"Garibaldi of the New World : a novel /","last_indexed":"2012-09-27T21:19:18Z","isbn":["0931688175 (cloth) :","0931688183 (paper)"],"callnumber-label":"PS3552","era_facet":["1821?-1849","1807-1882"],"publishDate":["1993"],"institution":["Villanova University"],"recordtype":"marc","oclc_num":["29789196"],"topic":["Garibaldi, Anita, 1821?-1849 Fiction","Garibaldi, Giuseppe, 1807-1882 Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Anita, Anita : Garibaldi of the New World : a novel /","callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878A831993","format":["Book"],"title_sort":"anita, anita :garibaldi of the new world : a novel","title_short":"Anita, Anita :","ctrlnum":["(OCoLC)ocm29789196"],"fullrecord":"00957cam a2200277 a 4500001000700000005001700007008004100024010001700065035002300082040003000105020003300135020002300168050002600191049000900217100002800226245007900254250001200333260004300345300003000388500011400418590001300532600004400545600004600589994001200635852003200647#30;704643#30;20060525092525.0#30;060508s1993 caub 000 1 eng #30; #31;a 93072365 #30; #31;a(OCoLC)ocm29789196#30; #31;aDLC#31;cDLC#31;dOCL#31;dBAKER#31;dPVU#30; #31;a0931688175 (cloth) :#31;c$20.00#30; #31;a0931688183 (paper)#30;00#31;aPS3552.R878#31;bA83 1993#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aAnita, Anita :#31;bGaribaldi of the New World : a novel /#31;cby Dorothy Bryant.#30; #31;a1st ed.#30; #31;aBerkeley, Calif. :#31;bAta Books,#31;cc1993.#30; #31;a303 p. :#31;b1 map ;#31;c23 cm.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aMay06eng#30;10#31;aGaribaldi, Anita,#31;d1821?-1849#31;vFiction.#30;10#31;aGaribaldi, Giuseppe,#31;d1807-1882#31;vFiction.#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iA83 1993#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","title_fullStr":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","title_full_unstemmed":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","genre_facet":["Fiction."],"_version_":1446541939727925248,"score":1.544408},{"edition":"1st ed.","physical":["144 p. : ill. ; 24 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"82073209","callnumber-a":"PS3552.R878","publisher":["Ata Books,"],"id":"704636","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","A day in San Francisco : a novel /","Parents of gays California San Francisco Fiction","Gay men California San Francisco Fiction","Parent and child Fiction"],"title":"A day in San Francisco : a novel /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","A day in San Francisco : a novel / by Dorothy Bryant.","1st ed.","Berkeley, Calif. : Ata Books, c1982.","144 p. : ill. ; 24 cm.","The Alfred S. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","May06eng","Parents of gays California San Francisco Fiction.","Gay men California San Francisco Fiction.","Parent and child Fiction.","San Francisco (Calif.) Fiction.","MAIN PS3552.R878 D3 1982"],"title_sub":"a novel /","last_indexed":"2012-09-27T21:19:18Z","isbn":["0931688094 :","0931688108 (pbk.) :"],"callnumber-label":"PS3552","publishDate":["1982"],"institution":["Villanova University"],"recordtype":"marc","oclc_num":["9094826"],"topic":["Parents of gays California San Francisco Fiction","Gay men California San Francisco Fiction","Parent and child Fiction"],"building":["Falvey Library"],"geographic":["San Francisco (Calif.) Fiction"],"callnumber-first":"P - Language and Literature","title_auth":"A day in San Francisco : a novel /","geographic_facet":["California","San Francisco","San Francisco (Calif.)"],"callnumber":"PS3552.R878D31982","callnumber-subject":"PS - American Literature","topic_facet":["Parents of gays","Gay men","Parent and child"],"format":["Book"],"title_sort":"day in san francisco :a novel","title_short":"A day in San Francisco :","ctrlnum":["(OCoLC)ocm09094826"],"fullrecord":"01072cam a2200313 a 4500001000700000005001700007008004100024010001700065035002300082040003500105020002500140020003100165043001200196050002500208049000900233100002800242245006000270250001200330260004300342300002900385500011400414590001300528650005700541650004900598650003100647651003700678994001200715852003100727#30;704636#30;20060525094305.0#30;060508s1982 caua 000 1 eng #30; #31;a 82073209 #30; #31;a(OCoLC)ocm09094826#30; #31;aDLC#31;cDLC#31;dGFK#31;dOCL#31;dOCLCQ#31;dPVU#30; #31;a0931688094 :#31;c$12.00#30; #31;a0931688108 (pbk.) :#31;c$6.00#30; #31;an-us-ca#30;0 #31;aPS3552.R878#31;bD3 1982#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;12#31;aA day in San Francisco :#31;ba novel /#31;cby Dorothy Bryant.#30; #31;a1st ed.#30; #31;aBerkeley, Calif. :#31;bAta Books,#31;cc1982.#30; #31;a144 p. :#31;bill. ;#31;c24 cm.#30; #31;aThe Alfred S. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aMay06eng#30; 0#31;aParents of gays#31;zCalifornia#31;zSan Francisco#31;vFiction.#30; 0#31;aGay men#31;zCalifornia#31;zSan Francisco#31;vFiction.#30; 0#31;aParent and child#31;vFiction.#30; 0#31;aSan Francisco (Calif.)#31;vFiction.#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iD3 1982#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"A day in San Francisco : a novel / by Dorothy Bryant.","title_fullStr":"A day in San Francisco : a novel / by Dorothy Bryant.","title_full_unstemmed":"A day in San Francisco : a novel / by Dorothy Bryant.","genre_facet":["Fiction."],"_version_":1446541939719536640,"score":1.5362269},{"genre":["Domestic fiction. lcsh"],"edition":"1st ed.","physical":["367 p. ; 22 cm."],"dewey-tens":["810 - American literature in English"],"callnumber-a":"PS3552.I74","lccn":"2001089763","callnumber-subject-code":"PS","dewey-hundreds":["800 - Literature"],"title_sub":"a novel /","publishDate":["2001"],"recordtype":"marc","topic":["Women college students Fiction","Children of military personnel Fiction","Americans Japan Okinawa-shi Fiction"],"spellingShingle":["Women college students Fiction","Children of military personnel Fiction","Americans Japan Okinawa-shi Fiction","Bird, Sarah.","The Yokota Officers Club : a novel /"],"geographic_facet":["Japan","Okinawa-shi","Okinawa-shi (Japan)"],"callnumber-subject":"PS - American Literature","callnumber":"PS3552.I74Y652001","dewey-sort-browse":["813.00000000"],"format":["Book"],"title_short":"The Yokota Officers Club :","title_sort":"yokota officers club :a novel","fullrecord":"00932cam a2200313 a 4500001000700000005001700007008004100024010001700065035002300082040001300105020001500118043002100133050002500154082001600179049000900195100001700204245005800221250001200279260006100291300002100352590001400373650003700387650004500424650004400469651003400513655002800547994001200575852003100587#30;576965#30;20020619143311.0#30;010406s2001 nyu 000 1 eng #30; #31;a 2001089763#30; #31;a(OCoLC)ocm47125699#30; #31;aDLC#31;cDLC#30; #31;a037541214X#30; #31;aa-ja---#31;an-us---#30;00#31;aPS3552.I74#31;bY65 2001#30;00#31;a813/.54#31;221#30; #31;aPVUM#30;1 #31;aBird, Sarah.#30;14#31;aThe Yokota Officers Club :#31;ba novel /#31;cby Sarah Bird.#30; #31;a1st ed.#30; #31;aNew York :#31;bKnopf :#31;bDistributed by Random House,#31;c2001.#30; #31;a367 p. ;#31;c22 cm.#30; #31;aJune02eng#30; 0#31;aWomen college students#31;vFiction.#30; 0#31;aChildren of military personnel#31;vFiction.#30; 0#31;aAmericans#31;zJapan#31;zOkinawa-shi#31;vFiction.#30; 0#31;aOkinawa-shi (Japan)#31;vFiction.#30; 7#31;aDomestic fiction.#31;2lcsh#30; #31;aE0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.I74#31;iY65 2001#30;#29;","dewey-ones":["813 - American fiction in English"],"ctrlnum":["(OCoLC)ocm47125699"],"marc_error":["Typo : Erroneous character found at end of leader [ 45e0 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"dewey-raw":["813/.54"],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"publisher":["Knopf :"],"id":"576965","author":"Bird, Sarah.","title":"The Yokota Officers Club : a novel /","spelling":["Bird, Sarah.","The Yokota Officers Club : a novel / by Sarah Bird.","1st ed.","New York : Knopf : Distributed by Random House, 2001.","367 p. ; 22 cm.","June02eng","Women college students Fiction.","Children of military personnel Fiction.","Americans Japan Okinawa-shi Fiction.","Okinawa-shi (Japan) Fiction.","Domestic fiction. lcsh","MAIN PS3552.I74 Y65 2001"],"last_indexed":"2012-09-27T20:15:03Z","isbn":["037541214X"],"callnumber-label":"PS3552","institution":["Villanova University"],"oclc_num":["47125699"],"geographic":["Okinawa-shi (Japan) Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"The Yokota Officers Club : a novel /","dewey-sort":"813.00000000","topic_facet":["Women college students","Children of military personnel","Americans"],"dewey-full":["813/.54"],"first_indexed":"2012-09-27T20:15:03Z","genre_facet":["Fiction.","Domestic fiction."],"title_full":"The Yokota Officers Club : a novel / by Sarah Bird.","title_fullStr":"The Yokota Officers Club : a novel / by Sarah Bird.","title_full_unstemmed":"The Yokota Officers Club : a novel / by Sarah Bird.","_version_":1446541460167983104,"score":1.3638511}]}} +{"responseHeader":{"status":0,"QTime":22},"match":{"numFound":1,"start":0,"maxScore":14.28401,"docs":[{"genre":["Domestic fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["170 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"2001033643","callnumber-a":"PS3552.R878","publisher":["Feminist Press at the City University of New York,"],"id":"704640","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","The test /","Adult children of aging parents Fiction","Automobile drivers' tests Fiction","Fathers and daughters Fiction","Middle aged women Fiction","Older men Fiction"],"title":"The test /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","The test / Dorothy Bryant ; afterword by Barbara Horn.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 2001.","170 p. ; 22 cm.","Originally published: Ata Books, Berkeley, Calif., 1991.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","Includes bibliographical references (p. 169-170).","May06eng","Adult children of aging parents Fiction.","Automobile drivers' tests Fiction.","Fathers and daughters Fiction.","Middle aged women Fiction.","Older men Fiction.","Domestic fiction. lcsh","MAIN PS3552.R878 T47 2001"],"last_indexed":"2012-09-27T21:19:18Z","isbn":["1558612742 (pbk. : alk. paper)"],"callnumber-label":"PS3552","publishDate":["2001"],"institution":["Villanova University"],"record_format":"marc","oclc_num":["47013195"],"topic":["Adult children of aging parents Fiction","Automobile drivers' tests Fiction","Fathers and daughters Fiction","Middle aged women Fiction","Older men Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"The test /","callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878T472001","topic_facet":["Adult children of aging parents","Automobile drivers' tests","Fathers and daughters","Middle aged women","Older men"],"format":["Book"],"title_sort":"test","title_short":"The test /","ctrlnum":["(OCoLC)ocm47013195"],"fullrecord":"01252cam a2200337 a 4500001000700000005001700007008004100024010001700065035002300082040003700105020003500142050002600177049000900203100002800212245006000240250002700300260007400327300002100401500006100422500011400483504005400597590001300651650004600664650004000710650003600750650003200786650002400818655002800842994001200870852003200882#30;704640#30;20060525093449.0#30;060508r20011991nyu b 000 1 eng #30; #31;a 2001033643#30; #31;a(OCoLC)ocm47013195#30; #31;aDLC#31;cDLC#31;dYDX#31;dOCLCQ#31;dBAKER#31;dPVU#30; #31;a1558612742 (pbk. : alk. paper)#30;00#31;aPS3552.R878#31;bT47 2001#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;14#31;aThe test /#31;cDorothy Bryant ; afterword by Barbara Horn.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c2001.#30; #31;a170 p. ;#31;c22 cm.#30; #31;aOriginally published: Ata Books, Berkeley, Calif., 1991.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aIncludes bibliographical references (p. 169-170).#30; #31;aMay06eng#30; 0#31;aAdult children of aging parents#31;vFiction.#30; 0#31;aAutomobile drivers' tests#31;vFiction.#30; 0#31;aFathers and daughters#31;vFiction.#30; 0#31;aMiddle aged women#31;vFiction.#30; 0#31;aOlder men#31;vFiction.#30; 7#31;aDomestic fiction.#31;2lcsh#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iT47 2001#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"The test / Dorothy Bryant ; afterword by Barbara Horn.","title_fullStr":"The test / Dorothy Bryant ; afterword by Barbara Horn.","title_full_unstemmed":"The test / Dorothy Bryant ; afterword by Barbara Horn.","genre_facet":["Fiction.","Domestic fiction."],"_version_":1446541939723730944,"score":14.28401}]},"response":{"numFound":1079840,"start":0,"maxScore":2.3629262,"docs":[{"genre":["Psychological fiction. lcsh","Feminist fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["186 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"97016088","callnumber-a":"PS3552.R878","publisher":["Feminist Press at the City University of New York,"],"id":"704635","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","Miss Giardino /","Italian American women Fiction","High school teachers Fiction","Retired teachers Fiction","Women teachers Fiction","Older women Fiction"],"title":"Miss Giardino /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 1997.","186 p. ; 22 cm.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","Includes bibliographical references (p. 181-185).","May06eng","Italian American women Fiction.","High school teachers Fiction.","Retired teachers Fiction.","Women teachers Fiction.","San Francisco (Calif.) Fiction.","Older women Fiction.","Psychological fiction. lcsh","Feminist fiction. lcsh","MAIN PS3552.R878 M5 1997"],"last_indexed":"2012-09-27T21:19:18Z","isbn":["1558611746 (pbk. : alk. paper)"],"callnumber-label":"PS3552","publishDate":["1997"],"institution":["Villanova University"],"record_format":"marc","oclc_num":["36776451"],"topic":["Italian American women Fiction","High school teachers Fiction","Retired teachers Fiction","Women teachers Fiction","Older women Fiction"],"building":["Falvey Library"],"geographic":["San Francisco (Calif.) Fiction"],"callnumber-first":"P - Language and Literature","title_auth":"Miss Giardino /","geographic_facet":["San Francisco (Calif.)"],"callnumber":"PS3552.R878M51997","callnumber-subject":"PS - American Literature","topic_facet":["Italian American women","High school teachers","Retired teachers","Women teachers","Older women"],"format":["Book"],"title_sort":"miss giardino","title_short":"Miss Giardino /","ctrlnum":["(OCoLC)ocm36776451"],"fullrecord":"01267cam a2200361 a 4500001000700000005001700007008004100024010001700065035002300082040002500105020003500130043001200165050002500177049000900202100002800211245006400239250002700303260007400330300002100404500011400425504005400539590001300593650003700606650003500643650003100678650002900709651003700738650002600775655003300801655002800834994001200862852003100874#30;704635#30;20060525110908.0#30;060508r19971978nyu b 000 1 eng #30; #31;a 97016088 #30; #31;a(OCoLC)ocm36776451#30; #31;aDLC#31;cDLC#31;dBAKER#31;dPVU#30; #31;a1558611746 (pbk. : alk. paper)#30; #31;an-us-ca#30;00#31;aPS3552.R878#31;bM5 1997#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aMiss Giardino /#31;cDorothy Bryant ; afterword by Janet Zandy.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c1997.#30; #31;a186 p. ;#31;c22 cm.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aIncludes bibliographical references (p. 181-185).#30; #31;aMay06eng#30; 0#31;aItalian American women#31;vFiction.#30; 0#31;aHigh school teachers#31;vFiction.#30; 0#31;aRetired teachers#31;vFiction.#30; 0#31;aWomen teachers#31;vFiction.#30; 0#31;aSan Francisco (Calif.)#31;vFiction.#30; 0#31;aOlder women#31;vFiction.#30; 7#31;aPsychological fiction.#31;2lcsh#30; 7#31;aFeminist fiction.#31;2lcsh#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iM5 1997#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","title_fullStr":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","title_full_unstemmed":"Miss Giardino / Dorothy Bryant ; afterword by Janet Zandy.","genre_facet":["Fiction.","Psychological fiction.","Feminist fiction."],"_version_":1446541939715342336,"score":2.3629262},{"genre":["Psychological fiction. lcsh","Diary fiction. lcsh"],"edition":"1st Feminist Press ed.","physical":["265 p. ; 22 cm."],"dewey-tens":["810 - American literature in English"],"callnumber-a":"PS3552.R878","lccn":"97014405","callnumber-subject-code":"PS","dewey-hundreds":["800 - Literature"],"title_sub":"a novel /","publishDate":["1997"],"record_format":"marc","topic":["Middle aged women California Education Fiction","Married women California Psychology Fiction"],"spellingShingle":["Middle aged women California Education Fiction","Married women California Psychology Fiction","Bryant, Dorothy, 1930-","Ella Price's journal : a novel /"],"geographic_facet":["California"],"callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878E431997","dewey-sort-browse":["813.00000000"],"format":["Book"],"title_short":"Ella Price's journal :","title_sort":"ella price's journal :a novel","fullrecord":"01121cam a2200313 a 4500001000700000005001700007008004100024010001700065035002000082040002300102020003600125020003500161043001200196050002600208082001600234049000900250100002800259245008600287250002700373260007400400300002100474500006500495504005000560650005500610650005200665655003300717655002500750852003200775#30;476297#30;19980910105451.0#30;980910r19971972nyu b 000 1 eng #30; #31;a 97014405 #30; #31;a(OCoLC)36767574#30; #31;aDLC#31;cDLC#31;dC#P#31;dPVU#30; #31;a1558611819 (cloth : alk. paper)#30; #31;a1558611754 (pbk. : alk. paper)#30; #31;an-us-ca#30;00#31;aPS3552.R878#31;bE43 1997#30;00#31;a813/.54#31;221#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aElla Price's journal :#31;ba novel /#31;cby Dorothy Bryant ; afterword by Barbara Horn.#30; #31;a1st Feminist Press ed.#30; #31;aNew York :#31;bFeminist Press at the City University of New York,#31;c1997.#30; #31;a265 p. ;#31;c22 cm.#30; #31;a\"Originally published by J.B. Lippincott, 1972\"--T.p. verso.#30; #31;aIncludes bibliographical references (p. 264).#30; 0#31;aMiddle aged women#31;zCalifornia#31;xEducation#31;xFiction.#30; 0#31;aMarried women#31;zCalifornia#31;xPsychology#31;xFiction.#30; 7#31;aPsychological fiction.#31;2lcsh#30; 7#31;aDiary fiction.#31;2lcsh#30;0 #31;bMAIN#31;hPS3552.R878#31;iE43 1997#30;#29;","dewey-ones":["813 - American fiction in English"],"ctrlnum":["(OCoLC)36767574"],"marc_error":["Typo : Erroneous character found at end of leader [ 45e0 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"dewey-raw":["813/.54"],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"publisher":["Feminist Press at the City University of New York,"],"id":"476297","author":"Bryant, Dorothy, 1930-","title":"Ella Price's journal : a novel /","spelling":["Bryant, Dorothy, 1930-","Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","1st Feminist Press ed.","New York : Feminist Press at the City University of New York, 1997.","265 p. ; 22 cm.","\"Originally published by J.B. Lippincott, 1972\"--T.p. verso.","Includes bibliographical references (p. 264).","Middle aged women California Education Fiction.","Married women California Psychology Fiction.","Psychological fiction. lcsh","Diary fiction. lcsh","MAIN PS3552.R878 E43 1997"],"last_indexed":"2012-09-27T19:12:22Z","isbn":["1558611819 (cloth : alk. paper)","1558611754 (pbk. : alk. paper)"],"callnumber-label":"PS3552","institution":["Villanova University"],"oclc_num":["36767574"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Ella Price's journal : a novel /","dewey-sort":"813.00000000","topic_facet":["Middle aged women","Married women","Education","Fiction.","Psychology"],"dewey-full":["813/.54"],"first_indexed":"2012-09-27T19:12:22Z","genre_facet":["Psychological fiction.","Diary fiction."],"title_full":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","title_fullStr":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","title_full_unstemmed":"Ella Price's journal : a novel / by Dorothy Bryant ; afterword by Barbara Horn.","_version_":1446540985586679809,"score":2.1145177},{"edition":"1st ed.","physical":["303 p. : 1 map ; 23 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"93072365","callnumber-a":"PS3552.R878","publisher":["Ata Books,"],"id":"704643","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","Anita, Anita : Garibaldi of the New World : a novel /","Garibaldi, Anita, 1821?-1849 Fiction","Garibaldi, Giuseppe, 1807-1882 Fiction"],"title":"Anita, Anita : Garibaldi of the New World : a novel /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","1st ed.","Berkeley, Calif. : Ata Books, c1993.","303 p. : 1 map ; 23 cm.","The Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","May06eng","Garibaldi, Anita, 1821?-1849 Fiction.","Garibaldi, Giuseppe, 1807-1882 Fiction.","MAIN PS3552.R878 A83 1993"],"title_sub":"Garibaldi of the New World : a novel /","last_indexed":"2012-09-27T21:19:18Z","isbn":["0931688175 (cloth) :","0931688183 (paper)"],"callnumber-label":"PS3552","era_facet":["1821?-1849","1807-1882"],"publishDate":["1993"],"institution":["Villanova University"],"record_format":"marc","oclc_num":["29789196"],"topic":["Garibaldi, Anita, 1821?-1849 Fiction","Garibaldi, Giuseppe, 1807-1882 Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Anita, Anita : Garibaldi of the New World : a novel /","callnumber-subject":"PS - American Literature","callnumber":"PS3552.R878A831993","format":["Book"],"title_sort":"anita, anita :garibaldi of the new world : a novel","title_short":"Anita, Anita :","ctrlnum":["(OCoLC)ocm29789196"],"fullrecord":"00957cam a2200277 a 4500001000700000005001700007008004100024010001700065035002300082040003000105020003300135020002300168050002600191049000900217100002800226245007900254250001200333260004300345300003000388500011400418590001300532600004400545600004600589994001200635852003200647#30;704643#30;20060525092525.0#30;060508s1993 caub 000 1 eng #30; #31;a 93072365 #30; #31;a(OCoLC)ocm29789196#30; #31;aDLC#31;cDLC#31;dOCL#31;dBAKER#31;dPVU#30; #31;a0931688175 (cloth) :#31;c$20.00#30; #31;a0931688183 (paper)#30;00#31;aPS3552.R878#31;bA83 1993#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;10#31;aAnita, Anita :#31;bGaribaldi of the New World : a novel /#31;cby Dorothy Bryant.#30; #31;a1st ed.#30; #31;aBerkeley, Calif. :#31;bAta Books,#31;cc1993.#30; #31;a303 p. :#31;b1 map ;#31;c23 cm.#30; #31;aThe Alfred F. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aMay06eng#30;10#31;aGaribaldi, Anita,#31;d1821?-1849#31;vFiction.#30;10#31;aGaribaldi, Giuseppe,#31;d1807-1882#31;vFiction.#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iA83 1993#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","title_fullStr":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","title_full_unstemmed":"Anita, Anita : Garibaldi of the New World : a novel / by Dorothy Bryant.","genre_facet":["Fiction."],"_version_":1446541939727925248,"score":1.544408},{"edition":"1st ed.","physical":["144 p. : ill. ; 24 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"82073209","callnumber-a":"PS3552.R878","publisher":["Ata Books,"],"id":"704636","author":"Bryant, Dorothy, 1930-","spellingShingle":["Bryant, Dorothy, 1930-","A day in San Francisco : a novel /","Parents of gays California San Francisco Fiction","Gay men California San Francisco Fiction","Parent and child Fiction"],"title":"A day in San Francisco : a novel /","callnumber-subject-code":"PS","spelling":["Bryant, Dorothy, 1930-","A day in San Francisco : a novel / by Dorothy Bryant.","1st ed.","Berkeley, Calif. : Ata Books, c1982.","144 p. : ill. ; 24 cm.","The Alfred S. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.","May06eng","Parents of gays California San Francisco Fiction.","Gay men California San Francisco Fiction.","Parent and child Fiction.","San Francisco (Calif.) Fiction.","MAIN PS3552.R878 D3 1982"],"title_sub":"a novel /","last_indexed":"2012-09-27T21:19:18Z","isbn":["0931688094 :","0931688108 (pbk.) :"],"callnumber-label":"PS3552","publishDate":["1982"],"institution":["Villanova University"],"record_format":"marc","oclc_num":["9094826"],"topic":["Parents of gays California San Francisco Fiction","Gay men California San Francisco Fiction","Parent and child Fiction"],"building":["Falvey Library"],"geographic":["San Francisco (Calif.) Fiction"],"callnumber-first":"P - Language and Literature","title_auth":"A day in San Francisco : a novel /","geographic_facet":["California","San Francisco","San Francisco (Calif.)"],"callnumber":"PS3552.R878D31982","callnumber-subject":"PS - American Literature","topic_facet":["Parents of gays","Gay men","Parent and child"],"format":["Book"],"title_sort":"day in san francisco :a novel","title_short":"A day in San Francisco :","ctrlnum":["(OCoLC)ocm09094826"],"fullrecord":"01072cam a2200313 a 4500001000700000005001700007008004100024010001700065035002300082040003500105020002500140020003100165043001200196050002500208049000900233100002800242245006000270250001200330260004300342300002900385500011400414590001300528650005700541650004900598650003100647651003700678994001200715852003100727#30;704636#30;20060525094305.0#30;060508s1982 caua 000 1 eng #30; #31;a 82073209 #30; #31;a(OCoLC)ocm09094826#30; #31;aDLC#31;cDLC#31;dGFK#31;dOCL#31;dOCLCQ#31;dPVU#30; #31;a0931688094 :#31;c$12.00#30; #31;a0931688108 (pbk.) :#31;c$6.00#30; #31;an-us-ca#30;0 #31;aPS3552.R878#31;bD3 1982#30; #31;aPVUM#30;1 #31;aBryant, Dorothy,#31;d1930-#30;12#31;aA day in San Francisco :#31;ba novel /#31;cby Dorothy Bryant.#30; #31;a1st ed.#30; #31;aBerkeley, Calif. :#31;bAta Books,#31;cc1982.#30; #31;a144 p. :#31;bill. ;#31;c24 cm.#30; #31;aThe Alfred S. Mannella and Rose T. (Lauria) Mannella Endowed Library Fund donated by Alfred S. Mannella, '58.#30; #31;aMay06eng#30; 0#31;aParents of gays#31;zCalifornia#31;zSan Francisco#31;vFiction.#30; 0#31;aGay men#31;zCalifornia#31;zSan Francisco#31;vFiction.#30; 0#31;aParent and child#31;vFiction.#30; 0#31;aSan Francisco (Calif.)#31;vFiction.#30; #31;aC0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.R878#31;iD3 1982#30;#29;","first_indexed":"2012-09-27T21:19:18Z","language":["English"],"title_full":"A day in San Francisco : a novel / by Dorothy Bryant.","title_fullStr":"A day in San Francisco : a novel / by Dorothy Bryant.","title_full_unstemmed":"A day in San Francisco : a novel / by Dorothy Bryant.","genre_facet":["Fiction."],"_version_":1446541939719536640,"score":1.5362269},{"genre":["Domestic fiction. lcsh"],"edition":"1st ed.","physical":["367 p. ; 22 cm."],"dewey-tens":["810 - American literature in English"],"callnumber-a":"PS3552.I74","lccn":"2001089763","callnumber-subject-code":"PS","dewey-hundreds":["800 - Literature"],"title_sub":"a novel /","publishDate":["2001"],"record_format":"marc","topic":["Women college students Fiction","Children of military personnel Fiction","Americans Japan Okinawa-shi Fiction"],"spellingShingle":["Women college students Fiction","Children of military personnel Fiction","Americans Japan Okinawa-shi Fiction","Bird, Sarah.","The Yokota Officers Club : a novel /"],"geographic_facet":["Japan","Okinawa-shi","Okinawa-shi (Japan)"],"callnumber-subject":"PS - American Literature","callnumber":"PS3552.I74Y652001","dewey-sort-browse":["813.00000000"],"format":["Book"],"title_short":"The Yokota Officers Club :","title_sort":"yokota officers club :a novel","fullrecord":"00932cam a2200313 a 4500001000700000005001700007008004100024010001700065035002300082040001300105020001500118043002100133050002500154082001600179049000900195100001700204245005800221250001200279260006100291300002100352590001400373650003700387650004500424650004400469651003400513655002800547994001200575852003100587#30;576965#30;20020619143311.0#30;010406s2001 nyu 000 1 eng #30; #31;a 2001089763#30; #31;a(OCoLC)ocm47125699#30; #31;aDLC#31;cDLC#30; #31;a037541214X#30; #31;aa-ja---#31;an-us---#30;00#31;aPS3552.I74#31;bY65 2001#30;00#31;a813/.54#31;221#30; #31;aPVUM#30;1 #31;aBird, Sarah.#30;14#31;aThe Yokota Officers Club :#31;ba novel /#31;cby Sarah Bird.#30; #31;a1st ed.#30; #31;aNew York :#31;bKnopf :#31;bDistributed by Random House,#31;c2001.#30; #31;a367 p. ;#31;c22 cm.#30; #31;aJune02eng#30; 0#31;aWomen college students#31;vFiction.#30; 0#31;aChildren of military personnel#31;vFiction.#30; 0#31;aAmericans#31;zJapan#31;zOkinawa-shi#31;vFiction.#30; 0#31;aOkinawa-shi (Japan)#31;vFiction.#30; 7#31;aDomestic fiction.#31;2lcsh#30; #31;aE0#31;bPVU#30;0 #31;bMAIN#31;hPS3552.I74#31;iY65 2001#30;#29;","dewey-ones":["813 - American fiction in English"],"ctrlnum":["(OCoLC)ocm47125699"],"marc_error":["Typo : Erroneous character found at end of leader [ 45e0 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"dewey-raw":["813/.54"],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"publisher":["Knopf :"],"id":"576965","author":"Bird, Sarah.","title":"The Yokota Officers Club : a novel /","spelling":["Bird, Sarah.","The Yokota Officers Club : a novel / by Sarah Bird.","1st ed.","New York : Knopf : Distributed by Random House, 2001.","367 p. ; 22 cm.","June02eng","Women college students Fiction.","Children of military personnel Fiction.","Americans Japan Okinawa-shi Fiction.","Okinawa-shi (Japan) Fiction.","Domestic fiction. lcsh","MAIN PS3552.I74 Y65 2001"],"last_indexed":"2012-09-27T20:15:03Z","isbn":["037541214X"],"callnumber-label":"PS3552","institution":["Villanova University"],"oclc_num":["47125699"],"geographic":["Okinawa-shi (Japan) Fiction"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"The Yokota Officers Club : a novel /","dewey-sort":"813.00000000","topic_facet":["Women college students","Children of military personnel","Americans"],"dewey-full":["813/.54"],"first_indexed":"2012-09-27T20:15:03Z","genre_facet":["Fiction.","Domestic fiction."],"title_full":"The Yokota Officers Club : a novel / by Sarah Bird.","title_fullStr":"The Yokota Officers Club : a novel / by Sarah Bird.","title_full_unstemmed":"The Yokota Officers Club : a novel / by Sarah Bird.","_version_":1446541460167983104,"score":1.3638511}]}} diff --git a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/multi-record b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/multi-record index d521e23bb62a391737d15121fe69e7f9f031c159..aef97d28b81608dba175484bd28962cda8586c9a 100644 --- a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/multi-record +++ b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/multi-record @@ -3,4 +3,4 @@ Date: Thu, 11 Oct 2012 07:56:30 GMT Last-Modified: Thu, 11 Oct 2012 07:05:29 GMT Server: Jetty(6.1.11) -{"responseHeader":{"status":0,"QTime":1,"params":{"q":"id:(12345 OR 125456 OR 234547)","json.nl":"arrarr","wt":"json"}},"response":{"numFound":3,"start":0,"docs":[{"physical":["viii, 163 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"F","dewey-tens":["010 - Bibliographies"],"collection":["Main Stacks"],"lccn":"79028077","callnumber-a":"F1411.A2N48","publisher":["Greenwood Press,"],"id":"12345","author":"Nawaz, Tawfique.","spellingShingle":["Nawaz, Tawfique.","The new international economic order : a bibliography /","International economic relations Bibliography"],"title":"The new international economic order : a bibliography /","callnumber-subject-code":"F","spelling":["Nawaz, Tawfique.","The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","Westport, Conn. : Greenwood Press, 1980.","viii, 163 p. ; 22 cm.","International economic relations Bibliography.","MAIN F1411.A2N48"],"title_sub":"a bibliography /","dewey-hundreds":["000 - Computer science, information & general works"],"last_indexed":"2012-09-27T14:51:17Z","isbn":["0313221111"],"callnumber-label":"F1411","publishDate":["1980"],"institution":["Villanova University"],"recordtype":"marc","oclc_num":["5831268"],"topic":["International economic relations Bibliography"],"building":["Falvey Library"],"callnumber-first":"F - General American History","title_auth":"The new international economic order : a bibliography /","dewey-sort":"016.33700000","dewey-sort-browse":["016.33700000"],"callnumber":"F1411.A2N48","callnumber-subject":"F - General American History","topic_facet":["International economic relations","Bibliography."],"format":["Book"],"title_sort":"new international economic order :a bibliography","title_short":"The new international economic order :","dewey-full":["016.337"],"ctrlnum":["ocm05831268 820113","(CaOTULAS)157328431"],"dewey-ones":["016 - Bibliographies of works on specific subjects"],"fullrecord":"00732cam a22002411a 4500001000600000008004100006010001700047020001500064035002300079035002400102035001300126040002400139049002000163050001800183082001200201099001600213100002100229245009200250260004700342300002700389650005200416852002200468#30;12345#30;791205s1980 ctu b 000 0 eng #30; #31;a 79028077 #30; #31;a0313221111#30; #31;aocm05831268 820113#30; #31;a(CaOTULAS)157328431#30; #31;900013296#30; #31;aDLC#31;cDLC#31;dm.c.#31;dPVU#30; #31;aPVUM#31;c[0480355]#30;0 #31;aF1411.A2#31;bN48#30; #31;a016.337#30;1 #31;aF1411.A2N48#30;1 #31;aNawaz, Tawfique.#30;14#31;aThe new international economic order :#31;ba bibliography /#31;c[compiled by] Tawfique Nawaz.#30; #31;aWestport, Conn. :#31;bGreenwood Press,#31;c1980.#30; #31;aviii, 163 p. ;#31;c22 cm.#30; 0#31;aInternational economic relations#31;xBibliography.#30;0 #31;bMAIN#31;hF1411.A2N48#30;#29;","first_indexed":"2012-09-27T14:51:17Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","title_fullStr":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","title_full_unstemmed":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","dewey-raw":["016.337"],"_version_":1446539186124357632},{"physical":["xv, 298 p. : ports. ; 23 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"57000890","callnumber-a":"PR4382.K54","publisher":["Routledge and K. Paul,"],"id":"125456","author":"Knight, George Wilson, 1897-","spellingShingle":["Knight, George Wilson, 1897-","Lord Byron's marriage ; the evidence of asterisks /","Byron, George Gordon Byron, Baron, 1788-1824 Marriage"],"title":"Lord Byron's marriage ; the evidence of asterisks /","callnumber-subject-code":"PR","spelling":["Knight, George Wilson, 1897-","Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","London : Routledge and K. Paul, [1957]","xv, 298 p. : ports. ; 23 cm.","Includes index.","Bibliography: p. xiii-xv.","Byron, George Gordon Byron, Baron, 1788-1824 Marriage.","MAIN PR4382.K54"],"title_sub":"the evidence of asterisks /","last_indexed":"2012-09-27T16:01:13Z","callnumber-label":"PR4382","era_facet":["1788-1824"],"publishDate":["1957"],"institution":["Villanova University"],"recordtype":"marc","topic":["Byron, George Gordon Byron, Baron, 1788-1824 Marriage"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Lord Byron's marriage ; the evidence of asterisks /","callnumber-subject":"PR - English Literature","callnumber":"PR4382.K54","topic_facet":["Marriage."],"format":["Book"],"title_sort":"lord byron's marriage ;the evidence of asterisks","title_short":"Lord Byron's marriage ;","ctrlnum":["(CaOTULAS)203368380"],"fullrecord":"00735cam a22002411a 4500001000700000008004100007010001700048035002400065035001300089040000700102043001200109049000900121050002200130099001500152100003400167245007900201260004500280300003500325500002000360504003000380600006200410852002100472#30;125456#30;900523s1957 enkc b 001 0beng d#30; #31;a 57000890 #30; #31;a(CaOTULAS)203368380#30; #31;900133275#30; #31;aLC#30; #31;ae-uk---#30; #31;aPVUM#30; 0#31;aPR4382#31;b.K54 1957#30;1 #31;aPR4382.K54#30;1 #31;aKnight, George Wilson,#31;d1897-#30;10#31;aLord Byron's marriage ;#31;bthe evidence of asterisks /#31;cby G. Wilson Knight.#30; #31;aLondon :#31;bRoutledge and K. Paul,#31;c[1957]#30; #31;axv, 298 p. :#31;bports. ;#31;c23 cm.#30; #31;aIncludes index.#30; #31;aBibliography: p. xiii-xv.#30;10#31;aByron, George Gordon Byron,#31;cBaron,#31;d1788-1824#31;xMarriage.#30;0 #31;bMAIN#31;hPR4382.K54#30;#29;","first_indexed":"2012-09-27T16:01:13Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","title_fullStr":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","title_full_unstemmed":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","_version_":1446539687921451008},{"edition":"[1st ed.]. -","physical":["xvii, 284 p.; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"79165402","callnumber-a":"PS3563.I423R4","publisher":["Farrar, Straus and Giroux,"],"id":"234547","author":"Mills, James, 1932-","spellingShingle":["Mills, James, 1932-","Report to the commissioner. -"],"title":"Report to the commissioner. -","callnumber-subject-code":"PZ","spelling":["Mills, James, 1932-","Report to the commissioner. -","[1st ed.]. -","New York: Farrar, Straus and Giroux, [1972]","xvii, 284 p.; 22 cm.","A novel.","MAIN PS3563.I423R4"],"last_indexed":"2012-09-27T17:00:08Z","isbn":["0374249407:"],"callnumber-label":"PZ4","publishDate":["1972"],"institution":["Villanova University"],"recordtype":"marc","building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Report to the commissioner. -","callnumber-subject":"PZ - Fiction","callnumber":"PS3563.I423R4","format":["Book"],"title_sort":"report to the commissioner. -","title_short":"Report to the commissioner. -","ctrlnum":["(CaOTULAS)203453340"],"fullrecord":"00608cam a22002291a 4500001000700000008004100007010001700048020002200065035002400087035001300111040000700124049000900131050003100140099001800171100002500189245003400214250001700248260005000265300002600315500001300341852002400354#30;234547#30;900804s1972 nyu 000 1 eng u#30; #31;a 79165402 #30; #31;a0374249407:#31;c6.95#30; #31;a(CaOTULAS)203453340#30; #31;900252111#30; #31;aLC#30; #31;aPVUM#30; 0#31;aPZ4.M6557#31;bRe#31;aPS3563.I423#30;1 #31;aPS3563.I423R4#30;1 #31;aMills, James,#31;d1932-#30;10#31;aReport to the commissioner. -#30; #31;a[1st ed.]. -#30; #31;aNew York:#31;bFarrar, Straus and Giroux,#31;c[1972]#30; #31;axvii, 284 p.;#31;c22 cm.#30; #31;aA novel.#30;0 #31;bMAIN#31;hPS3563.I423R4#30;#29;","first_indexed":"2012-09-27T17:00:08Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"Report to the commissioner. -","title_fullStr":"Report to the commissioner. -","title_full_unstemmed":"Report to the commissioner. -","_version_":1446540071117258752}]}} +{"responseHeader":{"status":0,"QTime":1,"params":{"q":"id:(12345 OR 125456 OR 234547)","json.nl":"arrarr","wt":"json"}},"response":{"numFound":3,"start":0,"docs":[{"physical":["viii, 163 p. ; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"F","dewey-tens":["010 - Bibliographies"],"collection":["Main Stacks"],"lccn":"79028077","callnumber-a":"F1411.A2N48","publisher":["Greenwood Press,"],"id":"12345","author":"Nawaz, Tawfique.","spellingShingle":["Nawaz, Tawfique.","The new international economic order : a bibliography /","International economic relations Bibliography"],"title":"The new international economic order : a bibliography /","callnumber-subject-code":"F","spelling":["Nawaz, Tawfique.","The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","Westport, Conn. : Greenwood Press, 1980.","viii, 163 p. ; 22 cm.","International economic relations Bibliography.","MAIN F1411.A2N48"],"title_sub":"a bibliography /","dewey-hundreds":["000 - Computer science, information & general works"],"last_indexed":"2012-09-27T14:51:17Z","isbn":["0313221111"],"callnumber-label":"F1411","publishDate":["1980"],"institution":["Villanova University"],"record_format":"marc","oclc_num":["5831268"],"topic":["International economic relations Bibliography"],"building":["Falvey Library"],"callnumber-first":"F - General American History","title_auth":"The new international economic order : a bibliography /","dewey-sort":"016.33700000","dewey-sort-browse":["016.33700000"],"callnumber":"F1411.A2N48","callnumber-subject":"F - General American History","topic_facet":["International economic relations","Bibliography."],"format":["Book"],"title_sort":"new international economic order :a bibliography","title_short":"The new international economic order :","dewey-full":["016.337"],"ctrlnum":["ocm05831268 820113","(CaOTULAS)157328431"],"dewey-ones":["016 - Bibliographies of works on specific subjects"],"fullrecord":"00732cam a22002411a 4500001000600000008004100006010001700047020001500064035002300079035002400102035001300126040002400139049002000163050001800183082001200201099001600213100002100229245009200250260004700342300002700389650005200416852002200468#30;12345#30;791205s1980 ctu b 000 0 eng #30; #31;a 79028077 #30; #31;a0313221111#30; #31;aocm05831268 820113#30; #31;a(CaOTULAS)157328431#30; #31;900013296#30; #31;aDLC#31;cDLC#31;dm.c.#31;dPVU#30; #31;aPVUM#31;c[0480355]#30;0 #31;aF1411.A2#31;bN48#30; #31;a016.337#30;1 #31;aF1411.A2N48#30;1 #31;aNawaz, Tawfique.#30;14#31;aThe new international economic order :#31;ba bibliography /#31;c[compiled by] Tawfique Nawaz.#30; #31;aWestport, Conn. :#31;bGreenwood Press,#31;c1980.#30; #31;aviii, 163 p. ;#31;c22 cm.#30; 0#31;aInternational economic relations#31;xBibliography.#30;0 #31;bMAIN#31;hF1411.A2N48#30;#29;","first_indexed":"2012-09-27T14:51:17Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","title_fullStr":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","title_full_unstemmed":"The new international economic order : a bibliography / [compiled by] Tawfique Nawaz.","dewey-raw":["016.337"],"_version_":1446539186124357632},{"physical":["xv, 298 p. : ports. ; 23 cm."],"illustrated":"Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"57000890","callnumber-a":"PR4382.K54","publisher":["Routledge and K. Paul,"],"id":"125456","author":"Knight, George Wilson, 1897-","spellingShingle":["Knight, George Wilson, 1897-","Lord Byron's marriage ; the evidence of asterisks /","Byron, George Gordon Byron, Baron, 1788-1824 Marriage"],"title":"Lord Byron's marriage ; the evidence of asterisks /","callnumber-subject-code":"PR","spelling":["Knight, George Wilson, 1897-","Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","London : Routledge and K. Paul, [1957]","xv, 298 p. : ports. ; 23 cm.","Includes index.","Bibliography: p. xiii-xv.","Byron, George Gordon Byron, Baron, 1788-1824 Marriage.","MAIN PR4382.K54"],"title_sub":"the evidence of asterisks /","last_indexed":"2012-09-27T16:01:13Z","callnumber-label":"PR4382","era_facet":["1788-1824"],"publishDate":["1957"],"institution":["Villanova University"],"record_format":"marc","topic":["Byron, George Gordon Byron, Baron, 1788-1824 Marriage"],"building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Lord Byron's marriage ; the evidence of asterisks /","callnumber-subject":"PR - English Literature","callnumber":"PR4382.K54","topic_facet":["Marriage."],"format":["Book"],"title_sort":"lord byron's marriage ;the evidence of asterisks","title_short":"Lord Byron's marriage ;","ctrlnum":["(CaOTULAS)203368380"],"fullrecord":"00735cam a22002411a 4500001000700000008004100007010001700048035002400065035001300089040000700102043001200109049000900121050002200130099001500152100003400167245007900201260004500280300003500325500002000360504003000380600006200410852002100472#30;125456#30;900523s1957 enkc b 001 0beng d#30; #31;a 57000890 #30; #31;a(CaOTULAS)203368380#30; #31;900133275#30; #31;aLC#30; #31;ae-uk---#30; #31;aPVUM#30; 0#31;aPR4382#31;b.K54 1957#30;1 #31;aPR4382.K54#30;1 #31;aKnight, George Wilson,#31;d1897-#30;10#31;aLord Byron's marriage ;#31;bthe evidence of asterisks /#31;cby G. Wilson Knight.#30; #31;aLondon :#31;bRoutledge and K. Paul,#31;c[1957]#30; #31;axv, 298 p. :#31;bports. ;#31;c23 cm.#30; #31;aIncludes index.#30; #31;aBibliography: p. xiii-xv.#30;10#31;aByron, George Gordon Byron,#31;cBaron,#31;d1788-1824#31;xMarriage.#30;0 #31;bMAIN#31;hPR4382.K54#30;#29;","first_indexed":"2012-09-27T16:01:13Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","title_fullStr":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","title_full_unstemmed":"Lord Byron's marriage ; the evidence of asterisks / by G. Wilson Knight.","_version_":1446539687921451008},{"edition":"[1st ed.]. -","physical":["xvii, 284 p.; 22 cm."],"illustrated":"Not Illustrated","callnumber-first-code":"P","collection":["Main Stacks"],"lccn":"79165402","callnumber-a":"PS3563.I423R4","publisher":["Farrar, Straus and Giroux,"],"id":"234547","author":"Mills, James, 1932-","spellingShingle":["Mills, James, 1932-","Report to the commissioner. -"],"title":"Report to the commissioner. -","callnumber-subject-code":"PZ","spelling":["Mills, James, 1932-","Report to the commissioner. -","[1st ed.]. -","New York: Farrar, Straus and Giroux, [1972]","xvii, 284 p.; 22 cm.","A novel.","MAIN PS3563.I423R4"],"last_indexed":"2012-09-27T17:00:08Z","isbn":["0374249407:"],"callnumber-label":"PZ4","publishDate":["1972"],"institution":["Villanova University"],"record_format":"marc","building":["Falvey Library"],"callnumber-first":"P - Language and Literature","title_auth":"Report to the commissioner. -","callnumber-subject":"PZ - Fiction","callnumber":"PS3563.I423R4","format":["Book"],"title_sort":"report to the commissioner. -","title_short":"Report to the commissioner. -","ctrlnum":["(CaOTULAS)203453340"],"fullrecord":"00608cam a22002291a 4500001000700000008004100007010001700048020002200065035002400087035001300111040000700124049000900131050003100140099001800171100002500189245003400214250001700248260005000265300002600315500001300341852002400354#30;234547#30;900804s1972 nyu 000 1 eng u#30; #31;a 79165402 #30; #31;a0374249407:#31;c6.95#30; #31;a(CaOTULAS)203453340#30; #31;900252111#30; #31;aLC#30; #31;aPVUM#30; 0#31;aPZ4.M6557#31;bRe#31;aPS3563.I423#30;1 #31;aPS3563.I423R4#30;1 #31;aMills, James,#31;d1932-#30;10#31;aReport to the commissioner. -#30; #31;a[1st ed.]. -#30; #31;aNew York:#31;bFarrar, Straus and Giroux,#31;c[1972]#30; #31;axvii, 284 p.;#31;c22 cm.#30; #31;aA novel.#30;0 #31;bMAIN#31;hPS3563.I423R4#30;#29;","first_indexed":"2012-09-27T17:00:08Z","marc_error":["Typo : Erroneous character found at end of leader [ 45 ]; changing them to the standard \"4500\" --- [ n/a : n/a ]"],"language":["English"],"title_full":"Report to the commissioner. -","title_fullStr":"Report to the commissioner. -","title_full_unstemmed":"Report to the commissioner. -","_version_":1446540071117258752}]}} diff --git a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/single-record b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/single-record index c447874295562c2ade8b66f493a782c41cc81dea..ff5f44f912548053d3eec5d011f10b868a7618f2 100644 --- a/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/single-record +++ b/module/VuFindSearch/tests/unit-tests/fixtures/solr/response/single-record @@ -3,4 +3,4 @@ Date: Thu, 11 Oct 2012 07:56:30 GMT Last-Modified: Thu, 11 Oct 2012 07:05:29 GMT Server: Jetty(6.1.11) -{"responseHeader":{"status":0,"QTime":0,"params":{"json.nl":"arrarr","wt":"json","q":"id:single-record"}},"response":{"numFound":1,"start":0,"docs":[{"illustrated":"Not Illustrated","id":"690250223","title":"Politics otherwise : Shakespeare as social and political critique /","title_sub":"Shakespeare as social and political critique /","spelling":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ... Amsterdam [u.a.] Rodopi 2012 XIV, 180 S. 23 cm Value inquiry book series 242 Philosophy. literature and politics Donskis, Leonidas (DE-601)391861484 (DE-601)357871030 (DE-600)14823147 Value inquiry book series 242","recordtype":"marc","title_auth":"Politics otherwise : Shakespeare as social and political critique /","title_short":"Politics otherwise :","title_sort":"politics otherwise :shakespeare as social and political critique","fullrecord":"00846nam a2200229 c 4500001001000000003000700010005001700017008004100034020002200075035002500097040002200122041000800144044001200152245010200164260003500266300002300301490007200324700004100396830007200437900005700509954005000566\u001e690250223\u001eDE-601\u001e20120419125548.0\u001e120419s2012 ne 000 0 eng d\u001e \u001fa978-90-420-3464-8\u001e \u001fa(DE-599)GBV690250223\u001e \u001faGBVCP\u001fbger\u001ferakwb\u001e0 \u001faeng\u001e \u001fane\u001faxxu\u001e10\u001faPolitics otherwise :\u001fbShakespeare as social and political critique /\u001fced. by Leonidas Donskis ...\u001e3 \u001faAmsterdam [u.a.]\u001fbRodopi\u001fc2012\u001e \u001faXIV, 180 S.\u001fc23 cm\u001e0 \u001faValue inquiry book series\u001fv242\u001faPhilosophy. literature and politics\u001e1 \u001faDonskis, Leonidas\u001f0(DE-601)391861484\u001e \u001fw(DE-601)357871030\u001fw(DE-600)14823147\u001faValue inquiry book series\u001fv242\u001e \u001faGBV\u001fbHAB Wolfenbüttel <23>\u001fd62.2354\u001fxN\u001fzN\u001fd62.2354\u001e \u001fa50\u001fb1305716574\u001fc01\u001fd62.2354\u001fea\u001fd62.2354\u001fx0023\u001e\u001d","title_full":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","title_fullStr":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","title_full_unstemmed":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","series":["Value inquiry book series"],"physical":["XIV, 180 S. 23 cm"],"building":["Library A"],"collection":["Catalog"],"format":["Book"],"series2":["Value inquiry book series","Philosophy. literature and politics"],"publisher":["Rodopi"],"ctrlnum":["(DE-599)GBV690250223"],"spellingShingle":["Value inquiry book series","Politics otherwise : Shakespeare as social and political critique /"],"isbn":["978-90-420-3464-8"],"author2Str":["Donskis, Leonidas"],"author2":["Donskis, Leonidas"],"language":["English"],"publishDate":["2012"],"institution":["MyInstitution"]}]}} \ No newline at end of file +{"responseHeader":{"status":0,"QTime":0,"params":{"json.nl":"arrarr","wt":"json","q":"id:single-record"}},"response":{"numFound":1,"start":0,"docs":[{"illustrated":"Not Illustrated","id":"690250223","title":"Politics otherwise : Shakespeare as social and political critique /","title_sub":"Shakespeare as social and political critique /","spelling":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ... Amsterdam [u.a.] Rodopi 2012 XIV, 180 S. 23 cm Value inquiry book series 242 Philosophy. literature and politics Donskis, Leonidas (DE-601)391861484 (DE-601)357871030 (DE-600)14823147 Value inquiry book series 242","record_format":"marc","title_auth":"Politics otherwise : Shakespeare as social and political critique /","title_short":"Politics otherwise :","title_sort":"politics otherwise :shakespeare as social and political critique","fullrecord":"00846nam a2200229 c 4500001001000000003000700010005001700017008004100034020002200075035002500097040002200122041000800144044001200152245010200164260003500266300002300301490007200324700004100396830007200437900005700509954005000566\u001e690250223\u001eDE-601\u001e20120419125548.0\u001e120419s2012 ne 000 0 eng d\u001e \u001fa978-90-420-3464-8\u001e \u001fa(DE-599)GBV690250223\u001e \u001faGBVCP\u001fbger\u001ferakwb\u001e0 \u001faeng\u001e \u001fane\u001faxxu\u001e10\u001faPolitics otherwise :\u001fbShakespeare as social and political critique /\u001fced. by Leonidas Donskis ...\u001e3 \u001faAmsterdam [u.a.]\u001fbRodopi\u001fc2012\u001e \u001faXIV, 180 S.\u001fc23 cm\u001e0 \u001faValue inquiry book series\u001fv242\u001faPhilosophy. literature and politics\u001e1 \u001faDonskis, Leonidas\u001f0(DE-601)391861484\u001e \u001fw(DE-601)357871030\u001fw(DE-600)14823147\u001faValue inquiry book series\u001fv242\u001e \u001faGBV\u001fbHAB Wolfenbüttel <23>\u001fd62.2354\u001fxN\u001fzN\u001fd62.2354\u001e \u001fa50\u001fb1305716574\u001fc01\u001fd62.2354\u001fea\u001fd62.2354\u001fx0023\u001e\u001d","title_full":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","title_fullStr":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","title_full_unstemmed":"Politics otherwise : Shakespeare as social and political critique / ed. by Leonidas Donskis ...","series":["Value inquiry book series"],"physical":["XIV, 180 S. 23 cm"],"building":["Library A"],"collection":["Catalog"],"format":["Book"],"series2":["Value inquiry book series","Philosophy. literature and politics"],"publisher":["Rodopi"],"ctrlnum":["(DE-599)GBV690250223"],"spellingShingle":["Value inquiry book series","Politics otherwise : Shakespeare as social and political critique /"],"isbn":["978-90-420-3464-8"],"author2Str":["Donskis, Leonidas"],"author2":["Donskis, Leonidas"],"language":["English"],"publishDate":["2012"],"institution":["MyInstitution"]}]}} \ No newline at end of file diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php index 3101bdbc0a840dbcc96701e4a9aac2c960df2e96..30925a9b5595d94d95fb0f8ab182ece7ff88992d 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/BrowZine/BackendTest.php @@ -120,7 +120,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnector(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -185,8 +185,8 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\BrowZine\Connector') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\BrowZine\Connector::class) ->setMethods($mock) ->setConstructorArgs([$client, 'faketoken', 'fakeid']) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php index bb9a13e053c3939696ca4cee0ee8c9eefbb1f1c9..b79f38501c7db69924034a6dd2d0cd66c1dac37a 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EDS/BackendTest.php @@ -177,7 +177,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(); $config = [ 'EBSCO_Account' => [ @@ -224,8 +224,8 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\EDS\Zend2') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\EDS\Zend2::class) ->setMethods($mock) ->setConstructorArgs([[], $client]) ->getMock(); @@ -244,13 +244,13 @@ class BackendTest extends \VuFindTest\Unit\TestCase protected function getBackend($connector, $factory = null, $cache = null, $container = null, $settings = [], $mock = null) { if (null === $factory) { - $factory = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $factory = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); } if (null === $cache) { - $cache = $this->createMock('Zend\Cache\Storage\Adapter\Filesystem'); + $cache = $this->createMock(\Zend\Cache\Storage\Adapter\Filesystem::class); } if (null === $container) { - $container = $this->getMockBuilder('Zend\Session\Container') + $container = $this->getMockBuilder(\Zend\Session\Container::class) ->disableOriginalConstructor()->getMock(); } if (null === $mock) { diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php index 58781cf8de6f49e5400e2be1baee234877d27db2..159743c8b9f8a8f2c0425a10bff0baf74d810285 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/EIT/BackendTest.php @@ -128,7 +128,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -164,7 +164,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); return $this->getMockBuilder(__NAMESPACE__ . '\ConnectorMock') ->setMethods($mock) ->setConstructorArgs(['http://fake', $client, 'profile', 'pwd', 'dbs']) diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php index eba17deea2c77c4a7d955eb4c7a0630c2515b028..ba33c125ce9c59073a5ead26cca35451cb4c3248 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/LibGuides/BackendTest.php @@ -121,7 +121,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnector(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -243,8 +243,8 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\LibGuides\Connector') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\LibGuides\Connector::class) ->setMethods($mock) ->setConstructorArgs(['fakeid', $client]) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php index 493e22d6e1afb0ff10843d4d18918fc23098c7b7..d240d5b779457edd6a840fd03cae5dc3f830c52b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Pazpar2/BackendTest.php @@ -51,12 +51,12 @@ class BackendTest extends TestCase */ public function testGetConnector() { - $connector = $this->getMockBuilder('VuFindSearch\Backend\Pazpar2\Connector') - ->setConstructorArgs(['http://fake', $this->createMock('Zend\Http\Client')]) + $connector = $this->getMockBuilder(\VuFindSearch\Backend\Pazpar2\Connector::class) + ->setConstructorArgs(['http://fake', $this->createMock(\Zend\Http\Client::class)]) ->getMock(); $back = new Backend( $connector, - $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface') + $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class) ); $this->assertEquals($connector, $back->getConnector()); } @@ -149,8 +149,8 @@ class BackendTest extends TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\Pazpar2\Connector') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\Pazpar2\Connector::class) ->setMethods($mock) ->setConstructorArgs(['fake', $client]) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php index 1b585f160f4ef0e0f75a927e9879c769a5e5c5e5..205c71762c0bb540ca0a62b153ad5d7f88647f7e 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/BackendTest.php @@ -121,7 +121,7 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -209,8 +209,8 @@ class BackendTest extends \VuFindTest\Unit\TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\Primo\Connector') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\Primo\Connector::class) ->setMethods($mock) ->setConstructorArgs(['http://fakeaddress.none', 'inst-id', $client]) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php index 01af661861a7416a86074ac570ffcfb083391b0f..c2cd04ca7a369e6712befac595e3447d528da45b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Primo/ConnectorTest.php @@ -81,7 +81,7 @@ class ConnectorTest extends TestCase $terms = []; $result = $conn->query('dummyinst', $terms); $this->assertEquals(0, $result['recordCount']); - $this->assertEquals('Primo does not accept an empty query', $result['error']); + $this->assertEquals('empty_search_disallowed', $result['error']); } /** diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php index 4f08bd25501ac3aaed7ff0684ef98007b1c60dea..e3b3ffd8c4820e7840b42157dca7d7c0a99c5caf 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/BackendTest.php @@ -286,7 +286,7 @@ class BackendTest extends TestCase public function testRandom() { // Test that random sort parameter is added: - $params = $this->getMockBuilder('VuFindSearch\ParamBag') + $params = $this->getMockBuilder(\VuFindSearch\ParamBag::class) ->setMethods(['set'])->getMock(); $params->expects($this->once())->method('set') ->with($this->equalTo('sort'), $this->matchesRegularExpression('/[0-9]+_random asc/')); @@ -351,7 +351,7 @@ class BackendTest extends TestCase protected function getConnectorMock(array $mock = []) { $map = new HandlerMap(['select' => ['fallback' => true]]); - return $this->getMockBuilder('VuFindSearch\Backend\Solr\Connector') + return $this->getMockBuilder(\VuFindSearch\Backend\Solr\Connector::class) ->setMethods($mock) ->setConstructorArgs(['http://example.org/', $map]) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php index c62ba7436c40fc0c79058f5eec633a17f7589ce3..915cb709a05816361dfd8a1de332732ea7dfea67 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/ConnectorTest.php @@ -134,7 +134,7 @@ class ConnectorTest extends TestCase public function testSaveThrowsUnknownFormat() { $conn = $this->createConnector(); - $document = $this->createMock('VuFindSearch\Backend\Solr\Document\UpdateDocument'); + $document = $this->createMock(\VuFindSearch\Backend\Solr\Document\UpdateDocument::class); $conn->write($document, 'unknown', 'update'); } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php index 2880025416c5f89e6e64ce3b904855894b0dc0af..716afca3a3689b4da9db4398cc207f7975e66d7b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/LuceneSyntaxHelperTest.php @@ -378,4 +378,45 @@ class LuceneSyntaxHelperTest extends \VuFindTest\Unit\TestCase ); } } + + /** + * Test normalization of unquoted special characters + * + * @return void + */ + public function testUnquotedNormalization() + { + $lh = new LuceneSyntaxHelper(false, false); + $tests = [ + 'this - that' => 'this that', + 'this -- that' => 'this that', + '- this that' => 'this that', + 'this that -' => 'this that', + '-- this -- that --' => 'this that', + 'this -that' => 'this -that', + 'this + that' => 'this that', + '+ this ++ that +' => 'this that', + 'this +that' => 'this +that', + 'this / that' => 'this "/" that', + 'this/that' => 'this/that', + '/this' => 'this', + '/this that' => 'this that', + 'this/' => 'this', + 'this that/' => 'this that', + '/this that/' => 'this that', + + // Quoted ones must not be affected + '"this - that"' => '"this - that"', + '"- this that"' => '"- this that"', + '"this that -"' => '"this that -"', + '"this + that"' => '"this + that"', + '"+ this ++ that +"' => '"+ this ++ that +"', + '"this / that"' => '"this / that"', + ]; + foreach ($tests as $input => $expected) { + $this->assertEquals( + $expected, $lh->normalizeSearchString($input) + ); + } + } } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php index 30ea55f23d75edf133dc8ac0508224143e0b857b..fc3c7e458267d4e3ea4728e96cfbc1e9a437e742 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/QueryBuilderTest.php @@ -344,38 +344,6 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase $this->assertEquals('a:filter', $q[0]); } - /** - * Test generation with highlighting, using the setCreateHighlightingQuery() - * method. - * - * @return void - */ - public function testSetCreateHighlightingQuery() - { - $qb = new QueryBuilder( - [ - 'test' => [ - 'DismaxFields' => ['test1'], - 'DismaxParams' => [['bq', 'boost']], - ] - ] - ); - - $q = new Query('*:*', 'test'); - - // No hl.q if highlighting query disabled: - $qb->setCreateHighlightingQuery(false); - $response1 = $qb->build($q); - $hlQ1 = $response1->get('hl.q'); - $this->assertEquals(null, $hlQ1[0]); - - // hl.q if highlighting query enabled: - $qb->setCreateHighlightingQuery(true); - $response2 = $qb->build($q); - $hlQ2 = $response2->get('hl.q'); - $this->assertEquals('*:*', $hlQ2[0]); - } - /** * Test hl.q edge case: when we are in dismax (not edismax) mode, and a boost * is set, and a query contains advanced syntax, VuFind manipulates the query @@ -443,7 +411,7 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase $qb->setFieldsToHighlight($input); $response = $qb->build($q); $hlfl = $response->get('hl.fl'); - $this->assertEquals($output, $hlfl[0]); + $this->assertEquals($output, $hlfl[0] ?? null); } } @@ -469,7 +437,7 @@ class QueryBuilderTest extends \VuFindTest\Unit\TestCase $qb->setCreateSpellingQuery(false); $response1 = $qb->build($q); $spQ1 = $response1->get('spellcheck.q'); - $this->assertEquals(null, $spQ1[0]); + $this->assertFalse(isset($spQ1[0])); // spellcheck.q if spellcheck query enabled: $qb->setCreateSpellingQuery(true); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php index 5bc0eeacd0299a3690ae1e519bd08fac4008e7f2..3c4357babe17b7fe5567c3fe9c11d6856487ee4b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/RecordCollectionTest.php @@ -78,7 +78,7 @@ class RecordCollectionTest extends TestCase ] ); for ($i = 0; $i < 5; $i++) { - $coll->add($this->createMock('VuFindSearch\Response\RecordInterface')); + $coll->add($this->createMock(\VuFindSearch\Response\RecordInterface::class)); } $coll->rewind(); $this->assertEquals(5, $coll->key()); @@ -192,4 +192,28 @@ class RecordCollectionTest extends TestCase $this->assertTrue(in_array($r2, $final)); $this->assertTrue(in_array($r3, $final)); } + + /** + * Test that the object handles offsets properly. + * + * @return void + */ + public function testAdd() + { + $coll = new RecordCollection( + [ + 'response' => ['numFound' => 10, 'start' => 5] + ] + ); + $record = $this->createMock(\VuFindSearch\Response\RecordInterface::class); + $coll->add($record); + for ($i = 0; $i < 4; $i++) { + $coll->add($this->createMock(\VuFindSearch\Response\RecordInterface::class)); + } + $this->assertEquals(5, $coll->count()); + $coll->add($record); + $this->assertEquals(5, $coll->count()); + $coll->add($record, false); + $this->assertEquals(6, $coll->count()); + } } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php index ffc63f597bcfdffbec237cea4b81edab6a6142a1..4e7dcd3b3f8b56162039a0df84d74edd7e12bf5a 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/Response/Json/SpellcheckTest.php @@ -53,7 +53,8 @@ class SpellcheckTest extends TestCase [ ['this is a phrase', []], ['foo', []], - ['foobar', []] + ['foobar', []], + ['1842', []], // test numeric handling (can cause problems) ], 'fake query' ); @@ -61,13 +62,27 @@ class SpellcheckTest extends TestCase [ ['is a', []], ['bar', []], - ['foo bar', []] + ['foo bar', []], + ['1842', []], + ['1843', []] ], 'fake query' ); $s1->mergeWith($s2); - $this->assertCount(5, $s1); + $this->assertCount(7, $s1); $this->assertEquals($s2, $s1->getSecondary()); + $this->assertEquals( + [ + 'this is a phrase' => [], + 'foobar' => [], + 'foo' => [], + 'bar' => [], + 'foo bar' => [], + '1842' => [], + '1843' => [], + ], + iterator_to_array($s1->getIterator()) + ); } /** diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php index 206ffc7e6cc41c948be5e987f49eed97d842e76f..143d5eb82a565749dc50008f1082893bd9e5bdf3 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Solr/SearchHandlerTest.php @@ -75,7 +75,7 @@ class SearchHandlerTest extends TestCase { $spec = ['DismaxParams' => [['foo', 'bar'], ['mm', '100%']], 'DismaxFields' => ['field1', 'field2']]; $hndl = new SearchHandler($spec); - $defaults = ['CustomMunge' => [], 'DismaxHandler' => 'dismax', 'QueryFields' => [], 'FilterQuery' => []]; + $defaults = ['CustomMunge' => [], 'DismaxHandler' => 'dismax', 'QueryFields' => [], 'FilterQuery' => [], 'DismaxMunge' => []]; $this->assertEquals($spec + $defaults, $hndl->toArray()); } @@ -131,4 +131,29 @@ class SearchHandlerTest extends TestCase $hndl->createSimpleQueryString('abc"123*') ); } + + /** + * Test dismax munge rules. + * + * @return void + */ + public function testPreprocessQueryString() + { + // fake munge rules based on a simplified version of default searchspecs.yaml + $spec = [ + 'DismaxMunge' => [ + ['uppercase'], + ['preg_replace', '/[ "]/', ""], + ['preg_replace', '/\*+$/', ""] + ], + 'DismaxFields' => ['callnumber'], + 'DismaxHandler' => 'dismax' + ]; + + $hndl = new SearchHandler($spec); + $this->assertEquals( + 'ABC123', + $hndl->preprocessQueryString('abc"123*') + ); + } } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php index d1d25f0135fec6bab3911c7ab18d07d9e32878c3..f4aaf0fa213a39e237e159c2c3a17ce03fd3df03 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/Summon/BackendTest.php @@ -125,7 +125,7 @@ class BackendTest extends TestCase */ public function testRetrieveWrapsSummonException() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(['getRecord']); $conn->expects($this->once()) ->method('getRecord') @@ -177,7 +177,7 @@ class BackendTest extends TestCase */ public function testSearchWrapsSummonException() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(['query']); $conn->expects($this->once()) ->method('query') @@ -211,7 +211,7 @@ class BackendTest extends TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -260,7 +260,7 @@ class BackendTest extends TestCase */ protected function getConnectorMock(array $mock = []) { - return $this->getMockBuilder('SerialsSolutions\Summon\Zend2') + return $this->getMockBuilder(\SerialsSolutions\Summon\Zend2::class) ->setMethods($mock) ->setConstructorArgs(['id', 'key']) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php index 305ec9ba18cebca2dc762433af00d5cbf38533a0..505450072cbeeaf8ebe3bfa7c11418f81d8e1a8e 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/BackendTest.php @@ -113,7 +113,7 @@ class BackendTest extends TestCase */ public function testConstructorSetters() { - $fact = $this->createMock('VuFindSearch\Response\RecordCollectionFactoryInterface'); + $fact = $this->createMock(\VuFindSearch\Response\RecordCollectionFactoryInterface::class); $conn = $this->getConnectorMock(); $back = new Backend($conn, $fact); $this->assertEquals($fact, $back->getRecordCollectionFactory()); @@ -149,8 +149,8 @@ class BackendTest extends TestCase */ protected function getConnectorMock(array $mock = []) { - $client = $this->createMock('Zend\Http\Client'); - return $this->getMockBuilder('VuFindSearch\Backend\WorldCat\Connector') + $client = $this->createMock(\Zend\Http\Client::class); + return $this->getMockBuilder(\VuFindSearch\Backend\WorldCat\Connector::class) ->setMethods($mock) ->setConstructorArgs(['fake', $client]) ->getMock(); diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php index 276c42c256d7932c339d90396c7eb31c930ba677..6c0ead00634393a858baf0fc1019456c8f3ce400 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Backend/WorldCat/ConnectorTest.php @@ -49,7 +49,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase */ public function testGetHoldings() { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); $connector = new Connector('key', $client); $client->expects($this->once())->method('setMethod') ->with($this->equalTo('POST')) @@ -57,7 +57,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase $client->expects($this->once())->method('setUri') ->with($this->equalTo('http://www.worldcat.org/webservices/catalog/content/libraries/baz?wskey=key&servicelevel=full&frbrGrouping=on')); $body = '<foo>bar</foo>'; - $response = $this->createMock('Zend\Http\Response'); + $response = $this->createMock(\Zend\Http\Response::class); $response->expects($this->once())->method('getBody') ->will($this->returnValue($body)); $response->expects($this->any())->method('isSuccess') @@ -77,12 +77,12 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase */ public function testGetHoldingsHttpFailure() { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); $connector = new Connector('key', $client); $client->expects($this->once())->method('setMethod') ->with($this->equalTo('POST')) ->will($this->returnValue($client)); - $response = $this->createMock('Zend\Http\Response'); + $response = $this->createMock(\Zend\Http\Response::class); $response->expects($this->any())->method('isSuccess') ->will($this->returnValue(false)); $client->expects($this->once())->method('send') @@ -97,7 +97,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase */ public function testGetRecord() { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); $connector = new Connector('key', $client); $client->expects($this->once())->method('setMethod') ->with($this->equalTo('POST')) @@ -105,7 +105,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase $client->expects($this->once())->method('setUri') ->with($this->equalTo('http://www.worldcat.org/webservices/catalog/content/baz?servicelevel=full&wskey=key')); $body = '<foo>bar</foo>'; - $response = $this->createMock('Zend\Http\Response'); + $response = $this->createMock(\Zend\Http\Response::class); $response->expects($this->once())->method('getBody') ->will($this->returnValue($body)); $response->expects($this->any())->method('isSuccess') @@ -123,7 +123,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase */ public function testGetRecordWithError() { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); $connector = new Connector('key', $client); $client->expects($this->once())->method('setMethod') ->with($this->equalTo('POST')) @@ -131,7 +131,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase $client->expects($this->once())->method('setUri') ->with($this->equalTo('http://www.worldcat.org/webservices/catalog/content/baz?servicelevel=full&wskey=key')); $body = '<foo><diagnostic>bad</diagnostic></foo>'; - $response = $this->createMock('Zend\Http\Response'); + $response = $this->createMock(\Zend\Http\Response::class); $response->expects($this->once())->method('getBody') ->will($this->returnValue($body)); $response->expects($this->any())->method('isSuccess') @@ -149,7 +149,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase */ public function testSearch() { - $client = $this->createMock('Zend\Http\Client'); + $client = $this->createMock(\Zend\Http\Client::class); $connector = new Connector('key', $client); $client->expects($this->once())->method('setMethod') ->with($this->equalTo('POST')) @@ -157,7 +157,7 @@ class ConnectorTest extends \PHPUnit\Framework\TestCase $client->expects($this->once())->method('setUri') ->with($this->equalTo('http://www.worldcat.org/webservices/catalog/search/sru?version=1.1&x=y&startRecord=0&maximumRecords=20&servicelevel=full&wskey=key')); $body = '<foo>,<numberOfRecords>1</numberOfRecords><records><record><recordData>bar</recordData></record></records></foo>'; - $response = $this->createMock('Zend\Http\Response'); + $response = $this->createMock(\Zend\Http\Response::class); $response->expects($this->once())->method('getBody') ->will($this->returnValue($body)); $response->expects($this->any())->method('isSuccess') diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php index 93062e6b4ee2411001e265182d8ec0ce84846e90..5a01351f7dc95dd3eea0f50328abb1ece8c2df0a 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryGroupTest.php @@ -87,6 +87,23 @@ class QueryGroupTest extends TestCase $this->assertEquals('test question multi word question', $q->getAllTerms()); } + /** + * Test replaceTerm() method with and without normalization using complex input + * + * @return void + */ + public function testReplaceTermWithNormalization() + { + // Without normalization we only replace the accented instance of "query": + $q = $this->getSampleQueryGroupWithWeirdCharacters(); + $q->replaceTerm('quéry', 'quéstion', false); + $this->assertEquals('tést quéstion multi WORD query', $q->getAllTerms()); + // With normalization, we replace both instances of "query": + $q = $this->getSampleQueryGroupWithWeirdCharacters(); + $q->replaceTerm('quéry', 'quéstion', true); + $this->assertEquals('test quéstion multi word quéstion', $q->getAllTerms()); + } + /** * Test QueryGroup cloning. * @@ -132,6 +149,20 @@ class QueryGroupTest extends TestCase $q->setOperator('fizz'); } + /** + * Test detection of normalized terms. + * + * @return void + */ + public function testContainsNormalizedTerm() + { + $q = $this->getSampleQueryGroupWithWeirdCharacters(); + // regular contains will fail because of the accent: + $this->assertFalse($q->containsTerm('test')); + // normalized contains will succeed: + $this->assertTrue($q->containsNormalizedTerm('test')); + } + /** * Get a test object. * @@ -144,4 +175,17 @@ class QueryGroupTest extends TestCase $q3 = new Query('multi word query'); return new QueryGroup('OR', [$q1, $q2, $q3]); } + + /** + * Get a test object with uppercase and accents. + * + * @return QueryGroup + */ + protected function getSampleQueryGroupWithWeirdCharacters() + { + $q1 = new Query('tést'); + $q2 = new Query('Quéry'); + $q3 = new Query('multi WORD query'); + return new QueryGroup('OR', [$q1, $q2, $q3]); + } } diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php index 2d8400af355dac0dba9b70ff7248c9a0ed2f5880..fb309de15c38824abacce086dbe1bbcb8426f49b 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/Query/QueryTest.php @@ -84,6 +84,21 @@ class QueryTest extends TestCase $this->assertEquals('test query we<(ird not/or', $q->getString()); } + /** + * Test normalization-related logic + * + * @return void + */ + public function testNormalization() + { + $q = new Query('this is a tést OF THINGS'); + $this->assertFalse($q->containsTerm('test')); + $this->assertTrue($q->containsNormalizedTerm('test')); + $this->assertEquals('this is a test of things', $q->getNormalizedString()); + $q->replaceTerm('test', 'mess', true); + $this->assertEquals('this is a mess of things', $q->getString()); + } + /** * Test setHandler() method * diff --git a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php index 283e3de01ae858fcaf118ec8f5687b7f80a1f525..956bd0377b2e747e6b7869c25fe52ffc8a8d9724 100644 --- a/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php +++ b/module/VuFindSearch/tests/unit-tests/src/VuFindTest/SearchServiceTest.php @@ -136,7 +136,7 @@ class SearchServiceTest extends TestCase public function testRetrieveBatchInterface() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestClassForRetrieveBatchInterface'); + $this->backend = $this->createMock(\VuFindTest\TestClassForRetrieveBatchInterface::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -165,7 +165,7 @@ class SearchServiceTest extends TestCase { $service = $this->getService(); $backend = $this->getBackend(); - $mockRecord = $this->createMock('VuFindSearch\Response\RecordInterface'); + $mockRecord = $this->createMock(\VuFindSearch\Response\RecordInterface::class); $response1 = $this->getRecordCollection(); $response1->expects($this->once())->method('add') ->with($this->equalTo($mockRecord)); @@ -198,7 +198,7 @@ class SearchServiceTest extends TestCase public function testRetrieveBatchInterfaceException() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestClassForRetrieveBatchInterface'); + $this->backend = $this->createMock(\VuFindTest\TestClassForRetrieveBatchInterface::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -253,7 +253,7 @@ class SearchServiceTest extends TestCase public function testRandomInterface() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestClassForRandomInterface'); + $this->backend = $this->createMock(\VuFindTest\TestClassForRandomInterface::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -291,7 +291,7 @@ class SearchServiceTest extends TestCase public function testRandomInterfaceWithException() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestClassForRandomInterface'); + $this->backend = $this->createMock(\VuFindTest\TestClassForRandomInterface::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -348,7 +348,7 @@ class SearchServiceTest extends TestCase for ($i = 1; $i < $limit + 1; $i++) { $response = $this->getRecordCollection(); $response->expects($this->any())->method('first') - ->will($this->returnValue($this->createMock('VuFindSearch\Response\RecordInterface'))); + ->will($this->returnValue($this->createMock(\VuFindSearch\Response\RecordInterface::class))); $backend->expects($this->at($i))->method('search') ->with( $this->equalTo($query), @@ -575,7 +575,7 @@ class SearchServiceTest extends TestCase public function testSimilar() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestBackendClassForSimilar'); + $this->backend = $this->createMock(\VuFindTest\TestBackendClassForSimilar::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -621,7 +621,7 @@ class SearchServiceTest extends TestCase public function testSimilarException() { // Use a special backend for this test... - $this->backend = $this->createMock('VuFindTest\TestBackendClassForSimilar'); + $this->backend = $this->createMock(\VuFindTest\TestBackendClassForSimilar::class); $service = $this->getService(); $backend = $this->getBackend(); @@ -651,9 +651,9 @@ class SearchServiceTest extends TestCase */ public function testFailedResolve() { - $mockResponse = $this->createMock('Zend\EventManager\ResponseCollection'); + $mockResponse = $this->createMock(\Zend\EventManager\ResponseCollection::class); $mockResponse->expects($this->any())->method('stopped')->will($this->returnValue(false)); - $em = $this->createMock('Zend\EventManager\EventManagerInterface'); + $em = $this->createMock(\Zend\EventManager\EventManagerInterface::class); $service = new Service(); $em->expects($this->any())->method('triggerUntil') ->with( @@ -674,7 +674,7 @@ class SearchServiceTest extends TestCase protected function getBackend() { if (!$this->backend) { - $this->backend = $this->createMock('VuFindSearch\Backend\BackendInterface'); + $this->backend = $this->createMock(\VuFindSearch\Backend\BackendInterface::class); } return $this->backend; } @@ -686,7 +686,7 @@ class SearchServiceTest extends TestCase */ protected function getService() { - $em = $this->createMock('Zend\EventManager\EventManagerInterface'); + $em = $this->createMock(\Zend\EventManager\EventManagerInterface::class); $service = new SearchServiceMock($this->getBackend()); $service->setEventManager($em); return $service; @@ -701,7 +701,7 @@ class SearchServiceTest extends TestCase */ protected function getRecordCollection() { - return $this->createMock('VuFindSearch\Response\AbstractRecordCollection'); + return $this->createMock(\VuFindSearch\Response\AbstractRecordCollection::class); } } diff --git a/module/VuFindTheme/Module.php b/module/VuFindTheme/Module.php index d353e85c645ef0f98225616f4140a6d69789a5f6..9fe837e5a6772f0b262319ca6043baa574f8dd2d 100644 --- a/module/VuFindTheme/Module.php +++ b/module/VuFindTheme/Module.php @@ -1,6 +1,6 @@ <?php /** - * ZF2 module definition for the VuFind theme system. + * Module definition for the VuFind theme system. * * PHP version 7 * @@ -27,10 +27,11 @@ */ namespace VuFindTheme; -use Zend\ServiceManager\ServiceManager; +use Zend\Mvc\View\Http\InjectTemplateListener as ZendInjectTemplateListener; +use Zend\ServiceManager\Factory\InvokableFactory; /** - * ZF2 module definition for the VuFind theme system. + * Module definition for the VuFind theme system. * * @category VuFind * @package Theme @@ -64,18 +65,17 @@ class Module public function getServiceConfig() { return [ + 'aliases' => [ + ZendInjectTemplateListener::class => InjectTemplateListener::class, + ], 'factories' => [ - 'VuFindTheme\MixinGenerator' => - 'VuFindTheme\Module::getMixinGenerator', - 'VuFindTheme\Mobile' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFindTheme\ResourceContainer' => - 'Zend\ServiceManager\Factory\InvokableFactory', - 'VuFindTheme\ThemeCompiler' => - 'VuFindTheme\Module::getThemeCompiler', - 'VuFindTheme\ThemeGenerator' => - 'VuFindTheme\Module::getThemeGenerator', - 'VuFindTheme\ThemeInfo' => 'VuFindTheme\Module::getThemeInfo', + InjectTemplateListener::class => InvokableFactory::class, + MixinGenerator::class => ThemeInfoInjectorFactory::class, + Mobile::class => InvokableFactory::class, + ResourceContainer::class => InvokableFactory::class, + ThemeCompiler::class => ThemeInfoInjectorFactory::class, + ThemeGenerator::class => ThemeInfoInjectorFactory::class, + ThemeInfo::class => ThemeInfoFactory::class, ], ]; } @@ -89,67 +89,24 @@ class Module { return [ 'factories' => [ - 'VuFindTheme\View\Helper\HeadThemeResources' => - 'VuFindTheme\View\Helper\Factory::getHeadThemeResources', - 'VuFindTheme\View\Helper\ImageLink' => - 'VuFindTheme\View\Helper\Factory::getImageLink', - 'Zend\View\Helper\HeadLink' => - 'VuFindTheme\View\Helper\Factory::getHeadLink', - 'Zend\View\Helper\HeadScript' => - 'VuFindTheme\View\Helper\Factory::getHeadScript', - 'Zend\View\Helper\InlineScript' => - 'VuFindTheme\View\Helper\Factory::getInlineScript', + View\Helper\HeadThemeResources::class => + View\Helper\HeadThemeResourcesFactory::class, + View\Helper\ImageLink::class => View\Helper\ImageLinkFactory::class, + View\Helper\HeadLink::class => + View\Helper\PipelineInjectorFactory::class, + View\Helper\HeadScript::class => + View\Helper\PipelineInjectorFactory::class, + View\Helper\InlineScript::class => + View\Helper\PipelineInjectorFactory::class, ], 'aliases' => [ - 'headThemeResources' => 'VuFindTheme\View\Helper\HeadThemeResources', - 'imageLink' => 'VuFindTheme\View\Helper\ImageLink', + 'headThemeResources' => View\Helper\HeadThemeResources::class, + 'imageLink' => View\Helper\ImageLink::class, + \Zend\View\Helper\HeadLink::class => View\Helper\HeadLink::class, + \Zend\View\Helper\HeadScript::class => View\Helper\HeadScript::class, + \Zend\View\Helper\InlineScript::class => + View\Helper\InlineScript::class, ], ]; } - - /** - * Factory function for MixinGenerator object. - * - * @param ServiceManager $sm Service manager - * - * @return MixinGenerator - */ - public static function getMixinGenerator(ServiceManager $sm) - { - return new MixinGenerator($sm->get('VuFindTheme\ThemeInfo')); - } - - /** - * Factory function for ThemeCompiler object. - * - * @param ServiceManager $sm Service manager - * - * @return ThemeCompiler - */ - public static function getThemeCompiler(ServiceManager $sm) - { - return new ThemeCompiler($sm->get('VuFindTheme\ThemeInfo')); - } - - /** - * Factory function for ThemeGenerator object. - * - * @param ServiceManager $sm Service manager - * - * @return ThemeGenerator - */ - public static function getThemeGenerator(ServiceManager $sm) - { - return new ThemeGenerator($sm->get('VuFindTheme\ThemeInfo')); - } - - /** - * Factory function for ThemeInfo object. - * - * @return ThemeInfo - */ - public static function getThemeInfo() - { - return new ThemeInfo(realpath(APPLICATION_PATH . '/themes'), 'bootprint3'); - } } diff --git a/module/VuFindTheme/src/VuFindTheme/Initializer.php b/module/VuFindTheme/src/VuFindTheme/Initializer.php index bbfba623472055f27fc66730c9174604001855e1..70d2441a39704a3acb314a9516c9fc875a4b0994 100644 --- a/module/VuFindTheme/src/VuFindTheme/Initializer.php +++ b/module/VuFindTheme/src/VuFindTheme/Initializer.php @@ -30,8 +30,8 @@ namespace VuFindTheme; use Zend\Config\Config; use Zend\Console\Console; use Zend\Mvc\MvcEvent; -use Zend\Mvc\View\Http\InjectTemplateListener as BaseInjectTemplateListener; use Zend\Stdlib\RequestInterface as Request; +use Zend\View\Resolver\TemplatePathStack; /** * VuFind Theme Initializer @@ -123,61 +123,17 @@ class Initializer $this->serviceManager = $this->event->getApplication()->getServiceManager(); // Get the cookie manager from the service manager: - $this->cookieManager = $this->serviceManager->get('VuFind\CookieManager'); + $this->cookieManager = $this->serviceManager + ->get(\VuFind\Cookie\CookieManager::class); // Get base directory from tools object: - $this->tools = $this->serviceManager->get('VuFindTheme\ThemeInfo'); + $this->tools = $this->serviceManager->get(\VuFindTheme\ThemeInfo::class); // Set up mobile device detector: - $this->mobile = $this->serviceManager->get('VuFindTheme\Mobile'); + $this->mobile = $this->serviceManager->get(\VuFindTheme\Mobile::class); $this->mobile->enable(isset($this->config->mobile_theme)); } - /** - * Adjust template injection to a strategy that works better with our themes. - * This needs to be called prior to the dispatch event, which is why it is a - * separate static method rather than part of the init() method below. - * - * @param MvcEvent $event Dispatch event object - * - * @return void - */ - public static function configureTemplateInjection(MvcEvent $event) - { - // Get access to the shared event manager: - $sharedEvents - = $event->getApplication()->getEventManager()->getSharedManager(); - - // Detach the default listener: - $listeners = $sharedEvents->getListeners( - ['Zend\Stdlib\DispatchableInterface'], MvcEvent::EVENT_DISPATCH - ); - foreach ($listeners as $priority => $priorityGroup) { - foreach ($priorityGroup as $callback) { - if ($callback[0] instanceof BaseInjectTemplateListener) { - $injectTemplatePriority = $priority; - $sharedEvents->detach( - $callback, 'Zend\Stdlib\DispatchableInterface' - ); - break 2; - } - } - } - - // If we didn't successfully detach a listener above, priority will not be - // set. This is an unexpected situation, so we should throw an exception. - if (!isset($injectTemplatePriority)) { - throw new \Exception('Unable to detach InjectTemplateListener'); - } - - // Attach our own listener in place of the one we removed: - $injectTemplateListener = new InjectTemplateListener(); - $sharedEvents->attach( - 'Zend\Stdlib\DispatchableInterface', MvcEvent::EVENT_DISPATCH, - [$injectTemplateListener, 'injectTemplate'], $injectTemplatePriority - ); - } - /** * Initialize the theme. This needs to be triggered as part of the dispatch * event. @@ -350,7 +306,8 @@ class Initializer $templatePathStack = []; // Grab the resource manager for tracking CSS, JS, etc.: - $resources = $this->serviceManager->get('VuFindTheme\ResourceContainer'); + $resources = $this->serviceManager + ->get(\VuFindTheme\ResourceContainer::class); // Set generator if necessary: if (isset($this->config->generator)) { @@ -396,16 +353,9 @@ class Initializer } } - // Inject the path stack generated above into the view resolver: - $resolver = $this->serviceManager->get('ViewResolver'); - if (!is_a($resolver, 'Zend\View\Resolver\AggregateResolver')) { - throw new \Exception('Unexpected resolver: ' . get_class($resolver)); - } - foreach ($resolver as $current) { - if (is_a($current, 'Zend\View\Resolver\TemplatePathStack')) { - $current->setPaths($templatePathStack); - } - } + // Inject the path stack generated above into the resolver: + $resolver = $this->serviceManager->get(TemplatePathStack::class); + $resolver->setPaths($templatePathStack); // Add theme specific language files for translation $this->updateTranslator($themes); @@ -431,8 +381,8 @@ class Initializer if (!empty($pathStack)) { try { - $translator = $this->serviceManager->get('Zend\Mvc\I18n\Translator'); - + $translator = $this->serviceManager + ->get(\Zend\Mvc\I18n\Translator::class); $pm = $translator->getPluginManager(); $pm->get('ExtendedIni')->addToPathStack($pathStack); } catch (\Zend\Mvc\I18n\Exception\BadMethodCallException $e) { @@ -444,13 +394,14 @@ class Initializer // Override the default cache with a theme-specific cache to avoid // key collisions in a multi-theme environment. try { - $cacheManager = $this->serviceManager->get('VuFind\Cache\Manager'); + $cacheManager = $this->serviceManager + ->get(\VuFind\Cache\Manager::class); $cacheName = $cacheManager->addLanguageCacheForTheme($theme); $translator->setCache($cacheManager->getCache($cacheName)); } catch (\Exception $e) { // Don't let a cache failure kill the whole application, but make // note of it: - $logger = $this->serviceManager->get('VuFind\Log\Logger'); + $logger = $this->serviceManager->get(\VuFind\Log\Logger::class); $logger->debug( 'Problem loading cache: ' . get_class($e) . ' exception: ' . $e->getMessage() diff --git a/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php b/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php index 55070c5982df1eb70a31243b0f5c165d3eb54a2d..7b4a0619ff1899ff919fdd92a74520ce17b662e0 100644 --- a/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php +++ b/module/VuFindTheme/src/VuFindTheme/Minify/PathConverter.php @@ -53,4 +53,38 @@ class PathConverter extends \MatthiasMullie\PathConverter\Converter return $path; } + + /** + * Convert paths relative to the themes directory. + * + * Takes advantage of the fact that we know the themes directory will be + * '../themes' relative to the cache directory. This allows path resolution to + * work regardless of whether there are symlinked directories or other + * differences between the actual file system path and the path used to access + * the theme files. + * + * @param string $path The relative path that needs to be converted + * + * @return string The new relative path + */ + public function convert($path) + { + $path = $this->from . '/' . $path; + $path = preg_replace('/.*?\/themes\//', '../themes/', $path); + + // Remove .. parts in the middle of the resulting path: + $parts = explode('/', $path); + $result = []; + $last = ''; + foreach ($parts as $part) { + if ('' !== $last && '..' !== $last && '..' === $part) { + array_pop($result); + continue; + } + $last = $part; + $result[] = $part; + } + + return implode('/', $result); + } } diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeInfoFactory.php b/module/VuFindTheme/src/VuFindTheme/ThemeInfoFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e08ed1fbdc578ca5503b6c203caa84e97cd95e29 --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/ThemeInfoFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * ThemeInfo factory. + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindTheme; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ThemeInfo factory. + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class ThemeInfoFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + realpath(APPLICATION_PATH . '/themes'), 'bootprint3' + ); + } +} diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeInfoInjectorFactory.php b/module/VuFindTheme/src/VuFindTheme/ThemeInfoInjectorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..2b781771a7c5853bde656f57517c391d4f4858c6 --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/ThemeInfoInjectorFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for objects that depend on the ThemeInfo object. + * + * PHP version 7 + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindTheme; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for objects that depend on the ThemeInfo object. + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class ThemeInfoInjectorFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName($container->get(ThemeInfo::class)); + } +} diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php deleted file mode 100644 index 662e5dfeeee2f413f54d029580ddbfe8c37602a7..0000000000000000000000000000000000000000 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/Factory.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php -/** - * Factory for VuFindTheme view helpers. - * - * PHP version 7 - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace VuFindTheme\View\Helper; - -use Zend\ServiceManager\ServiceManager; - -/** - * Factory for VuFindTheme view helpers. - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Split config and return prefixed setting with current environment. - * - * @param ServiceManager $sm Service manager. - * - * @return string|bool - */ - protected static function getPipelineConfig(ServiceManager $sm) - { - $config = $sm->get('VuFind\Config\PluginManager')->get('config'); - $default = false; - if (isset($config['Site']['asset_pipeline'])) { - $settings = array_map( - 'trim', - explode(';', $config['Site']['asset_pipeline']) - ); - foreach ($settings as $setting) { - $parts = array_map('trim', explode(':', $setting)); - if (APPLICATION_ENV === $parts[0]) { - return $parts[1]; - } elseif (count($parts) == 1) { - $default = $parts[0]; - } elseif ($parts[0] === '*') { - $default = $parts[1]; - } - } - } - return $default; - } - - /** - * Construct the HeadLink helper. - * - * @param ServiceManager $sm Service manager. - * - * @return HeadLink - */ - public static function getHeadLink(ServiceManager $sm) - { - return new HeadLink( - $sm->get('VuFindTheme\ThemeInfo'), - Factory::getPipelineConfig($sm) - ); - } - - /** - * Construct the HeadScript helper. - * - * @param ServiceManager $sm Service manager. - * - * @return HeadScript - */ - public static function getHeadScript(ServiceManager $sm) - { - return new HeadScript( - $sm->get('VuFindTheme\ThemeInfo'), - Factory::getPipelineConfig($sm) - ); - } - - /** - * Construct the HeadThemeResources helper. - * - * @param ServiceManager $sm Service manager. - * - * @return HeadThemeResources - */ - public static function getHeadThemeResources(ServiceManager $sm) - { - return new HeadThemeResources( - $sm->get('VuFindTheme\ResourceContainer') - ); - } - - /** - * Construct the ImageLink helper. - * - * @param ServiceManager $sm Service manager. - * - * @return ImageLink - */ - public static function getImageLink(ServiceManager $sm) - { - return new ImageLink( - $sm->get('VuFindTheme\ThemeInfo') - ); - } - - /** - * Construct the InlineScript helper. - * - * @param ServiceManager $sm Service manager. - * - * @return InlineScript - */ - public static function getInlineScript(ServiceManager $sm) - { - return new InlineScript( - $sm->get('VuFindTheme\ThemeInfo'), - Factory::getPipelineConfig($sm) - ); - } -} diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php index 949ee27d36db57dc3d061f2ac9427397313fe94b..150fec2f0762a94fa090dbb7c5cf049378101d3d 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadLink.php @@ -144,6 +144,30 @@ class HeadLink extends \Zend\View\Helper\HeadLink } } + /** + * Forcibly prepend a stylesheet removing it from any existing position + * + * @param string $href Stylesheet href + * @param string $media Media + * @param string $conditionalStylesheet Any conditions + * @param array $extras Array of extra attributes + * + * @return void + */ + public function forcePrependStylesheet($href, $media = 'screen', + $conditionalStylesheet = '', $extras = [] + ) { + // Look for existing entry and remove it if found. Comparison method + // copied from isDuplicate(). + foreach ($this->getContainer() as $offset => $item) { + if (($item->rel == 'stylesheet') && ($item->href == $href)) { + $this->offsetUnset($offset); + break; + } + } + parent::prependStylesheet($href, $media, $conditionalStylesheet, $extras); + } + /** * Returns true if file should not be included in the compressed concat file * Required by ConcatTrait diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php index 0ce0bb835d668dbef964d25515f752ed77219e5f..b8d235e92c0b5f255413de258ae49a458d41b91a 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadScript.php @@ -106,6 +106,32 @@ class HeadScript extends \Zend\View\Helper\HeadScript return parent::itemToString($item, $indent, $escapeStart, $escapeEnd); } + /** + * Forcibly prepend a file removing it from any existing position + * + * @param string $src Script src + * @param string $type Script type + * @param array $attrs Array of script attributes + * + * @return void + */ + public function forcePrependFile($src = null, $type = 'text/javascript', + array $attrs = [] + ) { + // Look for existing entry and remove it if found. Comparison method + // copied from isDuplicate(). + foreach ($this->getContainer() as $offset => $item) { + if (($item->source === null) + && array_key_exists('src', $item->attributes) + && ($src === $item->attributes['src']) + ) { + $this->offsetUnset($offset); + break; + } + } + parent::prependFile($src, $type, $attrs); + } + /** * Returns true if file should not be included in the compressed concat file * Required by ConcatTrait diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php index 38a143406590d4372c8e31cdb7b60c761e1edaf2..69804c642c9f064329599ef9e318fd64c6053ab2 100644 --- a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResources.php @@ -129,7 +129,7 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper $parts[1] .= ':' . $parts[2]; array_splice($parts, 2, 1); } - $headLink()->prependStylesheet( + $headLink()->forcePrependStylesheet( trim($parts[0]), isset($parts[1]) ? trim($parts[1]) : 'all', isset($parts[2]) ? trim($parts[2]) : false @@ -140,7 +140,7 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper // theme resources should load before extras added by individual templates): foreach (array_reverse($this->container->getLessCss()) as $current) { $parts = $this->parseSetting($current); - $headLink()->prependStylesheet( + $headLink()->forcePrependStylesheet( $headLink()->addLessStylesheet(trim($parts[0])), isset($parts[1]) ? trim($parts[1]) : 'all', isset($parts[2]) ? trim($parts[2]) : false @@ -170,8 +170,8 @@ class HeadThemeResources extends \Zend\View\Helper\AbstractHelper // Load Javascript (same ordering considerations as CSS, above): $headScript = $this->getView()->plugin('headScript'); foreach (array_reverse($this->container->getJs()) as $current) { - $parts = $this->parseSetting($current); - $headScript()->prependFile( + $parts = $this->parseSetting($current); + $headScript()->forcePrependFile( trim($parts[0]), 'text/javascript', isset($parts[1]) diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResourcesFactory.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResourcesFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a6253e7a1b95e6b4a71cd9506b231ce1beabc08d --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/HeadThemeResourcesFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for HeadThemeResources view helper. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindTheme\View\Helper; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for HeadThemeResources view helper. + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class HeadThemeResourcesFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFindTheme\ResourceContainer::class) + ); + } +} diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLinkFactory.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLinkFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..eeda2a5e1d9c98a3fa926e4dad3422913eaedcc2 --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/ImageLinkFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for ImageLink view helper. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindTheme\View\Helper; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for ImageLink view helper. + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class ImageLinkFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFindTheme\ThemeInfo::class) + ); + } +} diff --git a/module/VuFindTheme/src/VuFindTheme/View/Helper/PipelineInjectorFactory.php b/module/VuFindTheme/src/VuFindTheme/View/Helper/PipelineInjectorFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..dc508babdb33314a0eeabaaaf94b88b5dee68a98 --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/View/Helper/PipelineInjectorFactory.php @@ -0,0 +1,100 @@ +<?php +/** + * Factory for helpers relying on asset pipeline configuration. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFindTheme\View\Helper; + +use Interop\Container\ContainerInterface; +use Zend\Config\Config; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for helpers relying on asset pipeline configuration. + * + * @category VuFind + * @package Theme + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +class PipelineInjectorFactory implements FactoryInterface +{ + /** + * Split config and return prefixed setting with current environment. + * + * @param Config $config Configuration settings + * + * @return string|bool + */ + protected function getPipelineConfig(Config $config) + { + $default = false; + if (isset($config['Site']['asset_pipeline'])) { + $settings = array_map( + 'trim', + explode(';', $config['Site']['asset_pipeline']) + ); + foreach ($settings as $setting) { + $parts = array_map('trim', explode(':', $setting)); + if (APPLICATION_ENV === $parts[0]) { + return $parts[1]; + } elseif (count($parts) == 1) { + $default = $parts[0]; + } elseif ($parts[0] === '*') { + $default = $parts[1]; + } + } + } + return $default; + } + + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $configManager = $container->get(\VuFind\Config\PluginManager::class); + return new $requestedName( + $container->get(\VuFindTheme\ThemeInfo::class), + $this->getPipelineConfig($configManager->get('config')) + ); + } +} diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php index 505d41a63228e6ec899c4ac3a64df03cab1ccd25..a0eb9b7dcbba70d73f2d86b4a085aad02e5e75b7 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/ThemeMobileTest.php @@ -65,7 +65,7 @@ class ThemeMobileTest extends Unit\TestCase */ public function testDetection() { - $detector = $this->getMockBuilder('uagent_info') + $detector = $this->getMockBuilder(\uagent_info::class) ->setMethods(['DetectMobileLong']) ->getMock(); $detector->expects($this->once()) diff --git a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php index 64e078a3934afcf6d21c4779142c85f92de34afe..2f9545f98d4a54d3f6a7c905b5b0c4d69f006613 100644 --- a/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php +++ b/module/VuFindTheme/tests/unit-tests/src/VuFindTest/View/Helper/HeadThemeResourcesTest.php @@ -92,7 +92,7 @@ class HeadThemeResourcesTest extends \VuFindTest\Unit\TestCase */ protected function getMockView() { - $view = $this->createMock('Zend\View\Renderer\PhpRenderer'); + $view = $this->createMock(\Zend\View\Renderer\PhpRenderer::class); $view->expects($this->at(0))->method('plugin') ->with($this->equalTo('headMeta')) ->will($this->returnValue($this->getMockHeadMeta())); @@ -112,7 +112,7 @@ class HeadThemeResourcesTest extends \VuFindTest\Unit\TestCase */ protected function getMockHeadMeta() { - $mock = $this->getMockBuilder('VuFindTheme\View\Helper\HeadMeta') + $mock = $this->getMockBuilder(\VuFindTheme\View\Helper\HeadMeta::class) ->disableOriginalConstructor() ->setMethods(['__invoke', 'prependHttpEquiv', 'appendName']) ->getMock(); @@ -131,7 +131,7 @@ class HeadThemeResourcesTest extends \VuFindTest\Unit\TestCase */ protected function getMockHeadLink() { - $mock = $this->getMockBuilder('VuFindTheme\View\Helper\HeadLink') + $mock = $this->getMockBuilder(\VuFindTheme\View\Helper\HeadLink::class) ->disableOriginalConstructor() ->getMock(); $mock->expects($this->any())->method('__invoke')->will($this->returnValue($mock)); @@ -145,7 +145,7 @@ class HeadThemeResourcesTest extends \VuFindTest\Unit\TestCase */ protected function getMockHeadScript() { - $mock = $this->getMockBuilder('VuFindTheme\View\Helper\HeadScript') + $mock = $this->getMockBuilder(\VuFindTheme\View\Helper\HeadScript::class) ->disableOriginalConstructor() ->getMock(); $mock->expects($this->any())->method('__invoke')->will($this->returnValue($mock)); diff --git a/module/finc/config/module.config.php b/module/finc/config/module.config.php index 640b46118ec83cc2de17613d7655ac90d24f3e8d..f947141f82be5aba8b6578a301a7248042f07232 100644 --- a/module/finc/config/module.config.php +++ b/module/finc/config/module.config.php @@ -108,7 +108,7 @@ $config = [ ], 'auth' => [ 'factories' => [ - 'finc\Auth\ILS' => 'finc\Auth\Factory::getILS' + 'finc\Auth\ILS' => \finc\Auth\ILSFactory::class ], 'aliases' => [ 'VuFind\Auth\ILS' => 'finc\Auth\ILS', @@ -116,11 +116,11 @@ $config = [ ], 'ils_driver' => [ 'factories' => [ - 'finc\ILS\Driver\FincILS' => 'finc\ILS\Driver\Factory::getFincILS', - 'finc\ILS\Driver\PAIA' => 'finc\ILS\Driver\Factory::getPAIA', + 'finc\ILS\Driver\FincILS' => \finc\ILS\Driver\FincILSFactory::class, + 'finc\ILS\Driver\PAIA' => \finc\ILS\Driver\PAIAFactory::class, //finctheca is deprecated: Remove when Bibliotheca support ends - 'finc\ILS\Driver\FincTheca' => 'finc\ILS\Driver\Factory::getFincTheca', - 'finc\ILS\Driver\FincLibero' => 'finc\ILS\Driver\Factory::getFincLibero', + 'finc\ILS\Driver\FincTheca' => \finc\ILS\Driver\PAIAFactory::class, + 'finc\ILS\Driver\FincLibero' => \finc\ILS\Driver\PAIAFactory::class, ], 'aliases' => [ 'fincils' => 'finc\ILS\Driver\FincILS', @@ -136,7 +136,7 @@ $config = [ ], 'recommend' => [ 'factories' => [ - 'finc\Recommend\EbscoResults' => 'finc\Recommend\Factory::getEbscoResults', + 'finc\Recommend\EbscoResults' => \finc\Recommend\EbscoResultsFactory::class, 'finc\Recommend\InterlibraryLoan' => 'finc\Recommend\Factory::getInterlibraryLoan', ], 'aliases' => [ @@ -194,10 +194,6 @@ $config = [ ], 'recordtab' => [ 'factories' => [ - 'finc\RecordTab\StaffViewAI' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'finc\RecordTab\AcquisitionPDA' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'finc\RecordTab\Topics' => 'Zend\ServiceManager\Factory\InvokableFactory', - 'finc\RecordTab\DescriptionLido' => 'Zend\ServiceManager\Factory\InvokableFactory', 'finc\RecordTab\HierarchyTree' => 'finc\RecordTab\HierarchyTreeFactory', ], 'aliases' => [ @@ -249,100 +245,20 @@ $config = [ ], ], ], - 'recorddriver_tabs' => [ - 'finc\RecordDriver\SolrDefault' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => null, - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'finc\RecordDriver\SolrMarc' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => null, - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'finc\RecordDriver\SolrMarcFincPDA' => [ - 'tabs' => [ - /* 'Holdings' => 'HoldingsILS',*/ - 'AcquisitionPDA' => 'AcquisitionPDA', - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => null, - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'finc\RecordDriver\SolrAI' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => null, - 'Details' => 'StaffViewAI', - ], - 'defaultTab' => null, - ], - 'finc\RecordDriver\SolrLido' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'DescriptionLido', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', 'Map' => 'Map', - 'Similar' => 'SimilarItemsCarousel', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - ], - 'recorddriver_collection_tabs' => [ - 'VuFind\RecordDriver\AbstractBase' => [ - 'tabs' => [ - 'CollectionList' => 'CollectionList', - 'HierarchyTree' => 'CollectionHierarchyTree', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'finc\RecordDriver\SolrDico' => [ - 'tabs' => [ - 'CollectionList' => 'CollectionList', - 'HierarchyTree' => 'HierarchyTree', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => 'CollectionList', - ], - ], ], // Authorization configuration: 'zfc_rbac' => [ 'vufind_permission_provider_manager' => [ 'factories' => [ - 'catUserType' => 'finc\Role\PermissionProvider\Factory::getCatUserType', - 'ipRangeFoFor' => 'finc\Role\PermissionProvider\Factory::getIpRangeFoFor', - 'ipRegExFoFor' => 'finc\Role\PermissionProvider\Factory::getIpRegExFoFor' , + 'catUserType' => \finc\Role\PermissionProvider\CatUserTypeFactory::class, + 'ipRangeFoFor' => \finc\Role\PermissionProvider\IpRangeFoForFactory::class, + 'ipRegExFoFor' => \finc\Role\PermissionProvider\IpRegExFoForFactory::class, 'finc\Role\PermissionProvider\CatUserType' => - 'finc\Role\PermissionProvider\Factory::getCatUserType', + \finc\Role\PermissionProvider\CatUserTypeFactory::class, 'finc\Role\PermissionProvider\IpRangeFoFor' => - 'finc\Role\PermissionProvider\Factory::getIpRangeFoFor', + \finc\Role\PermissionProvider\IpRangeFoForFactory::class, 'finc\Role\PermissionProvider\IpRegExFoFor' => - 'finc\Role\PermissionProvider\Factory::getIpRegExFoFor' + \finc\Role\PermissionProvider\IpRegExFoForFactory::class, ], 'aliases' => [ 'CatUserType' => 'finc\Role\PermissionProvider\CatUserType', @@ -376,7 +292,6 @@ $nonTabRecordActions = [ 'PDA', 'EmailHold', 'ReportErrors' ]; - // Define record view routes -- route name => controller // Define record view routes once again to add new nonTabRecordActions $recordRoutes = [ @@ -384,7 +299,6 @@ $recordRoutes = [ 'resources' => 'Resources' ]; - // Define static routes -- Controller/Action strings $staticRoutes = [ 'MyResearch/Acquisition', diff --git a/module/finc/src/finc/Auth/ILS.php b/module/finc/src/finc/Auth/ILS.php index b76f6e95cbf2e0549ff6af32030ac49109cf4b00..c51865d9eeb05fca782d3def84c65aec387b97bf 100644 --- a/module/finc/src/finc/Auth/ILS.php +++ b/module/finc/src/finc/Auth/ILS.php @@ -120,10 +120,11 @@ class ILS extends \VuFind\Auth\ILS $user->password = ''; // Update user information based on ILS data: - $fields = ['firstname', 'lastname', 'email', 'major', 'college']; + $fields = ['firstname', 'lastname', 'major', 'college']; foreach ($fields as $field) { $user->$field = $info[$field] ?? ' '; } + $user->updateEmail($info['email'] ?? ''); // Update the user in the database, then return it to the caller: $user->saveCredentials( diff --git a/module/finc/src/finc/Auth/ILSAuthenticator.php b/module/finc/src/finc/Auth/ILSAuthenticator.php index ec46cc0e969143197fac749ad078256f865fa46b..8f891bb1eb85bf9e7bcc456132026033d90b6202 100644 --- a/module/finc/src/finc/Auth/ILSAuthenticator.php +++ b/module/finc/src/finc/Auth/ILSAuthenticator.php @@ -2,7 +2,7 @@ /** * Class for managing ILS-specific authentication. * - * PHP version 5 + * PHP version 7 * * Copyright (C) Villanova University 2007. * @@ -17,7 +17,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind * @package Authentication @@ -27,8 +27,10 @@ */ namespace finc\Auth; +use VuFind\Auth\EmailAuthenticator as EmailAuthenticator; use VuFind\Auth\Manager as Manager; use VuFind\ILS\Connection as ILSConnection; +use Zend\Session\SessionManager as SessionManager; /** * Class for managing ILS-specific authentication. @@ -58,18 +60,21 @@ class ILSAuthenticator extends \VuFind\Auth\ILSAuthenticator /** * Constructor * - * @param Manager $auth Auth manager - * @param ILSConnection $catalog ILS connection - * @param \Zend\Session\SessionManager $sessionManager Session manager + * @param Manager $auth Auth manager + * @param ILSConnection $catalog ILS connection + * @param SessionManager $sessionManager Session manager + * @param EmailAuthenticator $emailAuth Email authenticator */ public function __construct( Manager $auth, ILSConnection $catalog, - \Zend\Session\SessionManager $sessionManager + SessionManager $sessionManager, + EmailAuthenticator $emailAuth = null ) { $this->auth = $auth; $this->catalog = $catalog; $this->sessionManager = $sessionManager; + $this->emailAuthenticator = $emailAuth; } /** @@ -81,7 +86,10 @@ class ILSAuthenticator extends \VuFind\Auth\ILSAuthenticator { // SessionContainer not defined yet? Build it now: if (null === $this->session) { - $this->session = new \Zend\Session\Container('PAIA', $this->sessionManager); + $this->session = new \Zend\Session\Container( + 'PAIA', + $this->sessionManager + ); } return $this->session; } @@ -134,29 +142,22 @@ class ILSAuthenticator extends \VuFind\Auth\ILSAuthenticator } /** - * Attempt to log in the user to the ILS, and save credentials if it works. + * Update current user account with the patron information * - * @param string $username Catalog username - * @param string $password Catalog password + * @param string $catUsername Catalog username + * @param string $catPassword Catalog password + * @param array $patron Patron * - * Returns associative array of patron data on success, false on failure. - * - * @return array|bool - * @throws ILSException + * @return void */ - public function newCatalogLogin($username, $password) + protected function updateUser($catUsername, $catPassword, $patron) { - $result = $this->catalog->patronLogin($username, $password); - if ($result) { - $user = $this->auth->isLoggedIn(); - if ($user) { - $user->saveCredentials($username, null); - $this->auth->updateSession($user); - // cache for future use - $this->ilsAccount[$username] = $result; - } - return $result; + $user = $this->auth->isLoggedIn(); + if ($user) { + $user->saveCredentials($catUsername, null); + $this->auth->updateSession($user); + // cache for future use + $this->ilsAccount[$catUsername] = $patron; } - return false; } } diff --git a/module/finc/src/finc/Auth/ILSAuthenticatorFactory.php b/module/finc/src/finc/Auth/ILSAuthenticatorFactory.php index 1ec9154aa8cb72006ef1a4f6ca3ae9deaa69dec1..d202d0650bd6b11c3be18b0da4eaa9ddee53031d 100644 --- a/module/finc/src/finc/Auth/ILSAuthenticatorFactory.php +++ b/module/finc/src/finc/Auth/ILSAuthenticatorFactory.php @@ -72,15 +72,16 @@ class ILSAuthenticatorFactory implements FactoryInterface // actually utilized. $callback = function (& $wrapped, $proxy) use ($container, $requestedName) { // Generate wrapped object: - $auth = $container->get('VuFind\Auth\Manager'); - $catalog = $container->get('VuFind\ILS\Connection'); - $sessionManager = $container->get('VuFind\SessionManager'); - $wrapped = new ILSAuthenticator($auth, $catalog, $sessionManager); + $auth = $container->get(\VuFind\Auth\Manager::class); + $catalog = $container->get(\VuFind\ILS\Connection::class); + $emailAuth = $container->get(\VuFind\Auth\EmailAuthenticator::class); + $sessionManager = $container->get(\VuFind\SessionManager::class); + $wrapped = new $requestedName($auth, $catalog, $sessionManager, $emailAuth); // Indicate that initialization is complete to avoid reinitialization: $proxy->setProxyInitializer(null); }; - $cfg = $container->get('ProxyManager\Configuration'); + $cfg = $container->get(\ProxyManager\Configuration::class); $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($cfg); return $factory->createProxy($requestedName, $callback); } diff --git a/module/finc/src/finc/Auth/ILSFactory.php b/module/finc/src/finc/Auth/ILSFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c9c92ae1c55e0dfd29e06fceb421ab7447dadcda --- /dev/null +++ b/module/finc/src/finc/Auth/ILSFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * Factory for ILS authentication module (and others with equivalent constructors). + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace finc\Auth; + +use Interop\Container\ContainerInterface; +use VuFind\XSLT\Import\VuFind; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for ILS authentication module (and others with equivalent constructors). + * + * @category VuFind + * @package Authentication + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class IlsFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName( + $container->get(\VuFind\ILS\Connection::class), + $container->get(ILSAuthenticator::class), + $container->get(\VuFind\Auth\EmailAuthenticator::class) + ); + } +} diff --git a/module/finc/src/finc/Controller/RecordsController.php b/module/finc/src/finc/Controller/RecordsController.php index 09982d550965c0538df2c2ba6243ad64acba137a..17fcb556f8e3bd7db868219bab4e61a27112037d 100644 --- a/module/finc/src/finc/Controller/RecordsController.php +++ b/module/finc/src/finc/Controller/RecordsController.php @@ -38,6 +38,7 @@ use VuFind\Controller\RecordsController as BaseController; * @package Controller * @author Demian Katz <demian.katz@villanova.edu> * @author Dorian Merz <merz@ub.uni-leipzig.de> + * @author Robert Lange <lange@ub.uni-leipzig.de> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ @@ -59,7 +60,8 @@ class RecordsController extends BaseController $this->suppressJump = true; return $this->resultsAction(); } - return parent::homeAction(); + + return $this->resultsAction(); } /** diff --git a/module/finc/src/finc/ILS/Driver/Factory.php b/module/finc/src/finc/ILS/Driver/Factory.php deleted file mode 100644 index dc4f25edffe8a4615b45184dad57fbb71270dc5e..0000000000000000000000000000000000000000 --- a/module/finc/src/finc/ILS/Driver/Factory.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php -/** - * ILS 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package ILS_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - */ -namespace finc\ILS\Driver; - -use Interop\Container\ContainerInterface; - -/** - * ILS Driver Factory Class - * - * @category VuFind - * @package ILS_Drivers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Factory for FincILS driver. - * - * @param ContainerInterface $container Service manager. - * - * @return FincILS - */ - public static function getFincILS(ContainerInterface $container) - { - $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory( - $container->get('VuFind\ProxyConfig') - ); - - $callback = function (& $wrapped, $proxy) use ($container) { - $wrapped = $container->get('ZfcRbac\Service\AuthorizationService'); - - $proxy->setProxyInitializer(null); - }; - - $fl = new FincILS( - $container->get('VuFind\DateConverter'), - $container->get('VuFind\SessionManager'), - $container->get('VuFind\RecordLoader'), - $container->get('VuFind\Search'), - $container->get('VuFind\Config')->get('config'), - $factory->createProxy('ZfcRbac\Service\AuthorizationService', $callback) - ); - - $fl->setCacheStorage( - $container->get('VuFind\CacheManager')->getCache('object') - ); - - return $fl; - } - - /** - * Factory for PAIA driver. - * - * @param ContainerInterface $container Service manager. - * - * @return PAIA - */ - public static function getPAIA(ContainerInterface $container) - { - $paia = new PAIA( - $container->get('VuFind\DateConverter'), - $container->get('VuFind\SessionManager') - ); - - $paia->setCacheStorage( - $container->get('VuFind\CacheManager')->getCache('object') - ); - - return $paia; - } - - /** - * Factory for FincTheca driver. - * - * @param ContainerInterface $container - * - * @return FincTheca - * @deprecated Remove when Bibliotheca support ends - */ - public static function getFincTheca(ContainerInterface $container) - { - $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory( - $container->get('VuFind\ProxyConfig') - ); - - $callback = function (& $wrapped, $proxy) use ($container) { - $wrapped = $container->get('ZfcRbac\Service\AuthorizationService'); - - $proxy->setProxyInitializer(null); - }; - - $fl = new FincTheca( - $container->get('VuFind\DateConverter'), - $container->get('VuFind\SessionManager'), - $container->get('VuFind\RecordLoader'), - $container->get('VuFind\Search'), - $container->get('VuFind\Config')->get('config'), - $factory->createProxy('ZfcRbac\Service\AuthorizationService', $callback) - ); - - $fl->setCacheStorage( - $container->get('VuFind\CacheManager')->getCache('object') - ); - - return $fl; - } - - /** - * Factory for FincLibero driver. - * - * @param ContainerInterface $container Service manager - * @param string $requestedName Service being created - * @param null|array $options Extra options (optional) - * - * @return object - * - */ - public static function getFincLibero(ContainerInterface $container, $requestedName, - array $options = null - ) { - $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory($container->get('VuFind\ProxyConfig')); - - $callback = function (& $wrapped, $proxy) use ($container) { - $wrapped = $container->get('ZfcRbac\Service\AuthorizationService'); - - $proxy->setProxyInitializer(null); - }; - - $fl = new $requestedName( - $container->get('VuFind\DateConverter'), - $container->get('VuFind\SessionManager'), - $container->get('VuFind\RecordLoader'), - $container->get('VuFind\Search'), - $container->get('VuFind\Config')->get('config'), - $factory->createProxy('ZfcRbac\Service\AuthorizationService', $callback) - ); - - $fl->setCacheStorage( - $container->get('VuFind\CacheManager')->getCache('object') - ); - - return $fl; - } - -} diff --git a/module/finc/src/finc/ILS/Driver/FincILS.php b/module/finc/src/finc/ILS/Driver/FincILS.php index 7f8f971642d890cfab5d37eb0ae675cc449a01a1..0b7b0cac63c4fe83b74e33278016524b74ec2d7d 100644 --- a/module/finc/src/finc/ILS/Driver/FincILS.php +++ b/module/finc/src/finc/ILS/Driver/FincILS.php @@ -27,20 +27,15 @@ */ namespace finc\ILS\Driver; -use DateTime; use DateInterval; +use DateTime; use DateTimeZone; use finc\Controller\CustomTraits\SearchTrait as SearchTrait; use Herrera\Json\Exception\Exception; use Sabre\VObject; use VuFind\Exception\ILS as ILSException; -use VuFindSearch\ParamBag; -use VuFindSearch\Query\Query; use VuFindSearch\Service as SearchService; use Zend\Log\LoggerAwareInterface as LoggerAwareInterface; -use Zend\Escaper\Escaper; -use ZfcRbac\Service\AuthorizationServiceAwareInterface; -use ZfcRbac\Service\AuthorizationServiceAwareTrait; /** * Finc specific ILS Driver for VuFind, using PAIA and DAIA services. @@ -55,7 +50,7 @@ use ZfcRbac\Service\AuthorizationServiceAwareTrait; class FincILS extends PAIA implements LoggerAwareInterface { use SearchTrait; - const ILS_IDENTIFIER_BARCODE = 'barcode'; + public const ILS_IDENTIFIER_BARCODE = 'barcode'; // vCard ADR is an ordered list of the following values // 0 - the post office box; @@ -75,6 +70,7 @@ class FincILS extends PAIA implements LoggerAwareInterface ]; protected $root_username; + protected $root_password; /** @@ -420,7 +416,7 @@ class FincILS extends PAIA implements LoggerAwareInterface if ($idType !== self::ILS_IDENTIFIER_BARCODE) { foreach ($ids as &$id) { if ($pos = strrpos($id, ':')) { - $id = substr($id,$pos + 1); + $id = substr($id, $pos + 1); } } } @@ -626,7 +622,7 @@ class FincILS extends PAIA implements LoggerAwareInterface if (isset($vcard->ADR)) { foreach ($vcard->ADR as $adr) { $address[( - (isset($adr->parameters['ALTID'])) + (isset($adr->parameters['ALTID'])) //sets default key 'p' for private address ? (string)$adr->parameters['ALTID'] : 'p' )] = $adr->getParts(); @@ -635,7 +631,7 @@ class FincILS extends PAIA implements LoggerAwareInterface if (isset($vcard->TEL)) { foreach ($vcard->TEL as $tel) { $phone[( - (isset($tel->parameters['TYPE'])) + (isset($tel->parameters['TYPE'])) //sets default key 'home' for private phone number ? (string)$tel->parameters['TYPE'] : 'home' )] = (string)$tel; @@ -673,9 +669,8 @@ class FincILS extends PAIA implements LoggerAwareInterface $addressParameterMap = self::$vcard_address_parameter_map; - $replace = isset($this->config['PAIA']['profileFormEmptyInputReplacement']) - ? $this->config['PAIA']['profileFormEmptyInputReplacement'] - : null; + $replace = $this->config['PAIA']['profileFormEmptyInputReplacement'] + ?? null; if (isset($address)) { foreach ($address as $key => $altid) { @@ -832,8 +827,7 @@ class FincILS extends PAIA implements LoggerAwareInterface $address_array = []; // array_fill(0,7,NULL); //the empty-field marker in the used ILS - $replace = isset($this->config['PAIA']['profileFormEmptyInputReplacement']) - ? $this->config['PAIA']['profileFormEmptyInputReplacement'] : null; + $replace = $this->config['PAIA']['profileFormEmptyInputReplacement'] ?? null; foreach ($inval as $key => $val) { if (empty($val) && null !== $replace) { @@ -1408,8 +1402,7 @@ class FincILS extends PAIA implements LoggerAwareInterface ) { try { // Get time zone - $timezone = isset($this->mainConfig->Site->timezone) - ? $this->mainConfig->Site->timezone : 'America/New_York'; + $timezone = $this->mainConfig->Site->timezone ?? 'America/New_York'; // create DateTime object $dateObj = new DateTime($date, new DateTimeZone($timezone)); @@ -1494,7 +1487,7 @@ class FincILS extends PAIA implements LoggerAwareInterface protected function hasILSData($id) { foreach ($this->config['General']['queryIls'] as $value) { - list($methodName, $methodReturn) = explode(':', $value); + [$methodName, $methodReturn] = explode(':', $value); // if we have one mismatch we can already stop as this record does // not qualify for querying the ILS if ($methodReturn === "") { diff --git a/module/finc/src/finc/ILS/Driver/FincILSFactory.php b/module/finc/src/finc/ILS/Driver/FincILSFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..0b34cc7ebffec099e1cd493d3edd0a80107e046a --- /dev/null +++ b/module/finc/src/finc/ILS/Driver/FincILSFactory.php @@ -0,0 +1,91 @@ +<?php +/** + * FincILS (and FincLibero) Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * FincILS Factory Class + * + * @category VuFind + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class FincILSFactory implements FactoryInterface +{ + /** + * Create a FincILS + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return FincILS + * + * @throws ServiceNotFoundException if unable to resolve the service + * @throws ServiceNotCreatedException if creating a service throws an exception + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $factory = new \ProxyManager\Factory\LazyLoadingValueHolderFactory( + $container->get('VuFind\ProxyConfig') + ); + + $callback = function (& $wrapped, $proxy) use ($container) { + $wrapped = $container->get('ZfcRbac\Service\AuthorizationService'); + $proxy->setProxyInitializer(null); + }; + + $fincILS = new $requestedName( + $container->get('VuFind\DateConverter'), + $container->get('VuFind\SessionManager'), + $container->get('VuFind\RecordLoader'), + $container->get('VuFind\Search'), + $container->get('VuFind\Config')->get('config'), + $factory->createProxy('ZfcRbac\Service\AuthorizationService', $callback) + ); + + $fincILS->setCacheStorage( + $container->get('VuFind\CacheManager')->getCache('object') + ); + + return $fincILS; + } +} diff --git a/module/finc/src/finc/ILS/Driver/FincLibero.php b/module/finc/src/finc/ILS/Driver/FincLibero.php index dc778687d223be7632dc39e64ce1f384266eec47..70cfbbc270aecf45971dc1cf3c2a72ef89ee5488 100644 --- a/module/finc/src/finc/ILS/Driver/FincLibero.php +++ b/module/finc/src/finc/ILS/Driver/FincLibero.php @@ -27,10 +27,11 @@ * @link http://vufind.org/wiki/vufind2:building_an_ils_driver Wiki */ namespace finc\ILS\Driver; + use finc\ILS\InitException; +use VuFind\Exception\ILS as ILSException; +use VuFind\I18n\Translator\TranslatorAwareInterface; use VuFind\I18n\Translator\TranslatorAwareTrait; -use VuFind\I18n\Translator\TranslatorAwareInterface, - VuFind\Exception\ILS as ILSException; use VuFindSearch\Query\Query; /** @@ -45,8 +46,8 @@ use VuFindSearch\Query\Query; */ class FincLibero extends FincILS implements TranslatorAwareInterface { - const DELETE_NOTIFICATIONS_SUCCESS = '1'; - const DELETE_NOTIFICATIONS_ERROR = '0'; + public const DELETE_NOTIFICATIONS_SUCCESS = '1'; + public const DELETE_NOTIFICATIONS_ERROR = '0'; use LiberoWachtlTrait; use TranslatorAwareTrait; @@ -125,12 +126,17 @@ class FincLibero extends FincILS implements TranslatorAwareInterface parent::init(); $this->setMemberFromConfig('boundItemIdPattern', 'General', 'bound_item_id_pattern'); - $this->setMemberFromConfig('boundItemLabelPattern', 'General', - 'bound_item_label_pattern'); + $this->setMemberFromConfig( + 'boundItemLabelPattern', + 'General', + 'bound_item_label_pattern' + ); // Get the base URI to extend departments. $this->setMemberFromConfig( - 'departmentLocationBase', 'General', 'departmentLocationBase', + 'departmentLocationBase', + 'General', + 'departmentLocationBase', "No departmentLocationBase defined in FincLibero.ini." ); @@ -271,7 +277,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface */ public function getDaiaIdPrefixNamespace() { - return preg_quote(substr($this->daiaIdPrefix, 0, strpos($this->daiaIdPrefix, ':')+1)); + return preg_quote(substr($this->daiaIdPrefix, 0, strpos($this->daiaIdPrefix, ':') + 1)); } /** @@ -330,10 +336,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface && ( !isset($this->noTitleHoldStatuses) - || - empty($this->noTitleHoldStatuses) - || - !in_array($item['localIlsStatus'],$this->noTitleHoldStatuses) + || empty($this->noTitleHoldStatuses) + || !in_array($item['localIlsStatus'], $this->noTitleHoldStatuses) ) ) { $result_item['item_id'] = $titleHoldId; @@ -343,9 +347,9 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // custom DAIA field used in getHoldLink() $result_item['ilslink'] - = (isset($item['href']) ? $item['href'] : $doc_href); + = ($item['href'] ?? $doc_href); if ($isTitleHold) { - $result_item['addTitleHoldLink'] = TRUE; + $result_item['addTitleHoldLink'] = true; } // count items @@ -420,7 +424,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface try { $array_response = $this->paiaPostAsArray( - 'core/'.$patron['cat_username'].'/request', $post_data + 'core/' . $patron['cat_username'] . '/request', + $post_data ); } catch (Exception $e) { $this->debug($e->getMessage()); @@ -450,8 +455,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // which should be shown instead to the user (localIlsStatus) $details = [ 'success' => true, - 'sysMessage' => isset($element['localIlsStatus']) - ? $element['localIlsStatus'] : 'Successfully requested' + 'sysMessage' => $element['localIlsStatus'] ?? 'Successfully requested' ]; // if caching is enabled for DAIA remove the cached data for the // current item otherwise the changed status will not be shown @@ -528,11 +532,9 @@ class FincLibero extends FincILS implements TranslatorAwareInterface } $session->patron - = isset($responseArray['patron']) - ? $responseArray['patron'] : null; + = $responseArray['patron'] ?? null; $session->access_token - = isset($responseArray['access_token']) - ? $responseArray['access_token'] : null; + = $responseArray['access_token'] ?? null; $session->scope = isset($responseArray['scope']) ? explode(' ', $responseArray['scope']) : null; @@ -541,8 +543,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface ? (time() + ($responseArray['expires_in'])) : null; // Customization for internal Libero Id $session->borrower_id - = isset($responseArray['borrower_id']) - ? $responseArray['borrower_id'] : null; + = $responseArray['borrower_id'] ?? null; return true; } @@ -690,7 +691,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface array_merge( (array)$return['status'], $return['item_notes'], - (array) $about + (array)$about ) ); } @@ -708,8 +709,11 @@ class FincLibero extends FincILS implements TranslatorAwareInterface 'presentation', ]; $res = array_intersect($prio, $services); - if (empty($res)) return 0; - else return current($res); + if (empty($res)) { + return 0; + } else { + return current($res); + } } /** @@ -720,7 +724,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface */ protected function getStatusString($item) { - return isset($item['localIlsStatus']) ? $item['localIlsStatus'] : ''; + return $item['localIlsStatus'] ?? ''; } /** @@ -766,7 +770,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // via customData array foreach ($criteria as $key => $value) { if ($this->checkEmailHoldValidationCriteria( - [$key=>$limitation['id']]) + [$key=>$limitation['id']] + ) ) { $customData['emailHoldLimitationContent'][] = $limitation['content']; } @@ -864,7 +869,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface 'storageid' => $this->stackURIs ],*/ 'endtime' => null, - 'regex' => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"] + 'regex' => ['item' => "/^(" . $this->getDaiaIdPrefixNamespace() . ").*$/"] ]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); @@ -889,7 +894,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface $filter = [ 'status' => [3], 'endtime' => null, - 'regex' => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"] + 'regex' => ['item' => "/^(" . $this->getDaiaIdPrefixNamespace() . ").*$/"] ]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); @@ -916,7 +921,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // status = 1 - reserved $filter = [ 'status' => [1,2], - 'regex' => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"] + 'regex' => ['item' => "/^(" . $this->getDaiaIdPrefixNamespace() . ").*$/"] ]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); @@ -968,7 +973,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface $filter = [ 'status' => [3], 'exclude' => ['endtime' => null], - 'regex' => ['item' => "/^(".$this->getDaiaIdPrefixNamespace().").*$/"] + 'regex' => ['item' => "/^(" . $this->getDaiaIdPrefixNamespace() . ").*$/"] ]; // get items-docs for given filters $items = $this->paiaGetItems($patron, $filter); @@ -990,9 +995,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface $patron, $messageIdList = null, $toDate = null - ) - { - return $this->paiaRemoveSystemMessages($patron,$messageIdList); + ) { + return $this->paiaRemoveSystemMessages($patron, $messageIdList); } /** @@ -1065,8 +1069,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface ) { $pickUpLocations[] = [ 'locationID' => $limitation['id'], - 'locationDisplay' => isset($limitation['content']) - ? $limitation['content'] : $limitation['id'], + 'locationDisplay' => $limitation['content'] ?? $limitation['id'], ]; } } @@ -1075,7 +1078,6 @@ class FincLibero extends FincILS implements TranslatorAwareInterface return $pickUpLocations; } - /** * Helper method to check whether this item is bound with another item and needs * to be ordered via this bound item @@ -1086,7 +1088,9 @@ class FincLibero extends FincILS implements TranslatorAwareInterface */ protected function getBoundItemId($item) { - if (!isset($this->boundItemIdPattern)) return null; + if (!isset($this->boundItemIdPattern)) { + return null; + } $availabilities = ['available', 'unavailable']; // start logic to check if we have a bound item that needs to be ordered @@ -1101,7 +1105,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // being bound to another item contain the RSN of the // bound item in their current available service href if (isset($available['href']) - && preg_match($this->boundItemIdPattern, + && preg_match( + $this->boundItemIdPattern, $available['href'], $matches ) @@ -1119,7 +1124,6 @@ class FincLibero extends FincILS implements TranslatorAwareInterface return null; } - /** * Helper method to query bound item by rsn for record_id * @@ -1127,8 +1131,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface * * @return string record_id */ - protected function queryBoundItem($key) { - + protected function queryBoundItem($key) + { $query = $this->getBoundItemQueryString($key); if (empty($query)) { return null; @@ -1178,7 +1182,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface * * @return bool */ - public function hasTitleHolds() { + public function hasTitleHolds() + { return isset($this->titleHoldLimitations); } @@ -1209,7 +1214,8 @@ class FincLibero extends FincILS implements TranslatorAwareInterface $post_data['doc'][] = $doc; try { $array_response = $this->paiaPostAsArray( - 'core/'.$patron['cat_username'].'/request', $post_data + 'core/' . $patron['cat_username'] . '/request', + $post_data ); } catch (Exception $e) { $this->debug($e->getMessage()); @@ -1237,8 +1243,7 @@ class FincLibero extends FincILS implements TranslatorAwareInterface // which should be shown instead to the user (localIlsStatus) $details = [ 'success' => true, - 'sysMessage' => isset($element['localIlsStatus']) - ? $element['localIlsStatus'] : 'Successfully requested' + 'sysMessage' => $element['localIlsStatus'] ?? 'Successfully requested' ]; // if caching is enabled for DAIA remove the cached data for the // current item otherwise the changed status will not be shown diff --git a/module/finc/src/finc/ILS/Driver/LiberoWachtlTrait.php b/module/finc/src/finc/ILS/Driver/LiberoWachtlTrait.php index 21fcdc09c3ff203ea58b8ae0d49c39ecd2a36d2d..b7488b994e4bea43e2689f0ec3792378fa607bad 100644 --- a/module/finc/src/finc/ILS/Driver/LiberoWachtlTrait.php +++ b/module/finc/src/finc/ILS/Driver/LiberoWachtlTrait.php @@ -29,7 +29,6 @@ namespace finc\ILS\Driver; use VuFind\Exception\ILS as ILSException; -use Zend\Log\LoggerAwareInterface as LoggerAwareInterface; /** * Finc specific LiberoWachtl trait providing all the functions necessary for @@ -54,7 +53,6 @@ trait LiberoWachtlTrait * @param int $connectTimeout * * @return int Connection timeout - * @access protected */ protected function getConnectTimeout($connectTimeout = 500) { @@ -70,7 +68,6 @@ trait LiberoWachtlTrait * @param int $responseTimeout * * @return int Response timeout. - * @access protected */ protected function getResponseTimeout($responseTimeout = 1000) { @@ -116,7 +113,6 @@ trait LiberoWachtlTrait /** * Check if there exists a connection to a url. * - * @access public * @return boolean Returns true if a connection exists * @throws \Exception Throws ILSException */ @@ -133,7 +129,7 @@ trait LiberoWachtlTrait try { $result = $this->httpService->post( - $this->getLiberoWachtlUrl() .'liberoPing.jsp', + $this->getLiberoWachtlUrl() . 'liberoPing.jsp', http_build_query($params), 'application/json; charset=UTF-8', null, @@ -165,7 +161,6 @@ trait LiberoWachtlTrait * @param int $amount Paid fees in eurocent * * @return boolean True if passed, false if ILS request fails - * @access public * @throws ILSException */ public function balanceFinesOfUser($patron, $amount) @@ -177,7 +172,7 @@ trait LiberoWachtlTrait try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'payAnyFee.jsp', + $this->getLiberoWachtlUrl() . 'payAnyFee.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -217,7 +212,7 @@ trait LiberoWachtlTrait try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'getMySystemMessages.jsp', + $this->getLiberoWachtlUrl() . 'getMySystemMessages.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -260,16 +255,16 @@ trait LiberoWachtlTrait $params['memberCode'] = $patron['cat_username']; $params['password'] = $patron['cat_password']; - if (!is_null($messageIdList)) { + if (null !== $messageIdList) { $params['messageIdList'] = implode(',', $messageIdList); } - if (!is_null($toDate)) { + if (null !== $toDate) { $params['toDate'] = $toDate; } try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'removeMySystemMessages.jsp', + $this->getLiberoWachtlUrl() . 'removeMySystemMessages.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -306,7 +301,7 @@ trait LiberoWachtlTrait $params['password'] = $patron['cat_password']; try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'lockMyAccount.jsp', + $this->getLiberoWachtlUrl() . 'lockMyAccount.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -323,7 +318,7 @@ trait LiberoWachtlTrait return false; } // Log error for debugging - if( true === ($bool = $this->getLiberoWachtlResultBool($result))) { + if (true === ($bool = $this->getLiberoWachtlResultBool($result))) { // Remove explicitly session vars of PAIA connection // Compliance security issue $session = $this->getSession(); @@ -351,8 +346,7 @@ trait LiberoWachtlTrait */ public function getIgnoredProfileFields() { - return isset($this->config['LiberoWachtl']['ignoredProfileFields']) ? - $this->config['LiberoWachtl']['ignoredProfileFields'] : []; + return $this->config['LiberoWachtl']['ignoredProfileFields'] ?? []; } /** @@ -362,8 +356,7 @@ trait LiberoWachtlTrait */ public function getRestrictedUserGroups() { - return isset($this->config['LiberoWachtl']['restrictedUserGroups']) ? - $this->config['LiberoWachtl']['restrictedUserGroups'] : []; + return $this->config['LiberoWachtl']['restrictedUserGroups'] ?? []; } /** @@ -374,8 +367,7 @@ trait LiberoWachtlTrait */ public function getItemsPerViewLoanHistory() { - return isset($this->config['LiberoWachtl']['itemsPerPageLoanHistory']) ? - $this->config['LiberoWachtl']['itemsPerPageLoanHistory'] : 20; + return $this->config['LiberoWachtl']['itemsPerPageLoanHistory'] ?? 20; } /** @@ -398,7 +390,7 @@ trait LiberoWachtlTrait try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'getMyProfile.jsp', + $this->getLiberoWachtlUrl() . 'getMyProfile.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -504,7 +496,7 @@ trait LiberoWachtlTrait try { $result = $this->httpService->get( - $this->getLiberoWachtlUrl() .'changeUserPin.jsp', + $this->getLiberoWachtlUrl() . 'changeUserPin.jsp', $params, null, $this->getLiberoWachtlRequestHeaders() @@ -532,7 +524,6 @@ trait LiberoWachtlTrait * @param boolean $reverse If true swap key and value pairs. * * @return array Return array of mappings. - * @access private */ private static function profileDataMapper($reverse = false) { @@ -608,7 +599,7 @@ trait LiberoWachtlTrait return false; } - return isset($details[$resultKey]) ? $details[$resultKey] : []; + return $details[$resultKey] ?? []; } /** @@ -618,7 +609,6 @@ trait LiberoWachtlTrait * @param array $result Result * * @return array - * @access private */ private function getLiberoWachtlResultBool($result) { @@ -631,7 +621,6 @@ trait LiberoWachtlTrait return true; } - /** * Customized getMyLoanHistory * @@ -727,7 +716,6 @@ trait LiberoWachtlTrait return $retval; } - /** * Return count of all items at getMyLoanHistory * @@ -735,7 +723,6 @@ trait LiberoWachtlTrait * * @return array Array with items of loan history * - ['total'] Maximum entries at loan history - * @access public * @throws Exception * @throws ILSException */ @@ -773,11 +760,12 @@ trait LiberoWachtlTrait } if ($details['rowCount'] > 0) { - return (array_map( + return array_map( function ($n) { return sprintf('fake_%03d', $n); - }, range(1, $details['rowCount']) - )); + }, + range(1, $details['rowCount']) + ); } return []; } diff --git a/module/finc/src/finc/ILS/Driver/PAIA.php b/module/finc/src/finc/ILS/Driver/PAIA.php index 9a48ad2715119283de953b44dc39453a5228e8dd..8628e929a6cec503d7a63f97afc484235b679323 100644 --- a/module/finc/src/finc/ILS/Driver/PAIA.php +++ b/module/finc/src/finc/ILS/Driver/PAIA.php @@ -29,7 +29,6 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki */ - namespace finc\ILS\Driver; use VuFind\Exception\Auth as AuthException; @@ -55,17 +54,17 @@ class PAIA extends \VuFind\ILS\Driver\PAIA /** * PAIA scopes as defined in http://gbv.github.io/paia/paia.html#access-tokens-and-scopes */ - const SCOPE_READ_PATRON = 'read_patron'; - const SCOPE_UPDATE_PATRON = 'update_patron'; - const SCOPE_UPDATE_PATRON_NAME = 'update_patron_name'; - const SCOPE_UPDATE_PATRON_EMAIL = 'update_patron_email'; - const SCOPE_UPDATE_PATRON_ADDRESS = 'update_patron_address'; - const SCOPE_READ_FEES = 'read_fees'; - const SCOPE_READ_ITEMS = 'read_items'; - const SCOPE_WRITE_ITEMS = 'write_items'; - const SCOPE_CHANGE_PASSWORD = 'change_password'; - const SCOPE_READ_NOTIFICATIOS = 'read_notifications'; - const SCOPE_DELETE_NOTIFICATIONS = 'delete_notifications'; + public const SCOPE_READ_PATRON = 'read_patron'; + public const SCOPE_UPDATE_PATRON = 'update_patron'; + public const SCOPE_UPDATE_PATRON_NAME = 'update_patron_name'; + public const SCOPE_UPDATE_PATRON_EMAIL = 'update_patron_email'; + public const SCOPE_UPDATE_PATRON_ADDRESS = 'update_patron_address'; + public const SCOPE_READ_FEES = 'read_fees'; + public const SCOPE_READ_ITEMS = 'read_items'; + public const SCOPE_WRITE_ITEMS = 'write_items'; + public const SCOPE_CHANGE_PASSWORD = 'change_password'; + public const SCOPE_READ_NOTIFICATIOS = 'read_notifications'; + public const SCOPE_DELETE_NOTIFICATIONS = 'delete_notifications'; protected $last_error = null; @@ -149,7 +148,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $array_response = $this->paiaPostAsArray( - 'core/'.$patron['cat_username'].'/cancel', + 'core/' . $patron['cat_username'] . '/cancel', $post_data ); } catch (\Exception $e) { @@ -257,8 +256,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA 'success' => false, 'status' => $array_response['error'], 'sysMessage' => - isset($array_response['error']) - ? $array_response['error'] : ' ' . + $array_response['error'] ?? ' ' . isset($array_response['error_description']) ? $array_response['error_description'] : ' ' ]; @@ -300,7 +298,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $fees = $this->paiaGetAsArray( - 'core/'.$patron['cat_username'].'/fees' + 'core/' . $patron['cat_username'] . '/fees' ); } catch (\Exception $e) { // all error handling is done in paiaHandleErrors so pass on the excpetion @@ -313,7 +311,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $paiaCurrencyPattern = "/^(\-?[0-9]+\.[0-9][0-9]) ([A-Z][A-Z][A-Z])$/"; if (preg_match($paiaCurrencyPattern, $fee, $feeMatches)) { // VuFind expects fees in PENNIES - return ($feeMatches[1]*100); + return $feeMatches[1] * 100; } return null; }; @@ -330,7 +328,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA 'checkout' => '', // fee.feetype 0..1 string textual description of the type // of service that caused the fee - 'fine' => (isset($fee['feetype']) ? $fee['feetype'] : null), + 'fine' => ($fee['feetype'] ?? null), 'balance' => $amount, // fee.date 0..1 date date when the fee was claimed 'createdate' => (isset($fee['date']) @@ -376,10 +374,8 @@ class PAIA extends \VuFind\ILS\Driver\PAIA // PAIA specific custom values 'expires' => isset($patron['expires']) ? $this->convertDate($patron['expires']) : null, - 'statuscode' => isset($patron['status']) - ? $patron['status'] : null, - 'note' => isset($patron['note']) - ? $patron['note'] : null, + 'statuscode' => $patron['status'] ?? null, + 'note' => $patron['note'] ?? null, 'canWrite' => in_array(self::SCOPE_WRITE_ITEMS, $this->getScope()), ]; @@ -450,9 +446,8 @@ class PAIA extends \VuFind\ILS\Driver\PAIA // access_denied 403 Wrong or missing credentials to get an access token case 'access_denied': throw new AuthException( - isset($array['error_description']) - ? $array['error_description'] : $array['error'], - isset($array['code']) ? $array['code'] : '' + $array['error_description'] ?? $array['error'], + $array['code'] ?? '' ); // not_found 404 // Unknown request URL or unknown patron. Implementations SHOULD @@ -492,9 +487,8 @@ class PAIA extends \VuFind\ILS\Driver\PAIA case 'gateway_timeout': default: throw new ILSException( - isset($array['error_description']) - ? $array['error_description'] : $array['error'], - isset($array['code']) ? $array['code'] : '' + $array['error_description'] ?? $array['error'], + $array['code'] ?? '' ); } } @@ -534,7 +528,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $array_response = $this->paiaPostAsArray( - 'core/'.$patron['cat_username'].'/request', + 'core/' . $patron['cat_username'] . '/request', $post_data ); } catch (\Exception $e) { @@ -615,7 +609,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $array_response = $this->paiaPostAsArray( - 'core/'.$patron['cat_username'].'/renew', + 'core/' . $patron['cat_username'] . '/renew', $post_data ); } catch (\Exception $e) { @@ -709,7 +703,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA if (!isset($itemsResponse) || $itemsResponse == null) { try { $itemsResponse = $this->paiaGetAsArray( - 'core/'.$patron['cat_username'].'/items' + 'core/' . $patron['cat_username'] . '/items' ); } catch (\Exception $e) { // all error handling is done in paiaHandleErrors so pass on the excpetion @@ -755,7 +749,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA */ private function notificationsCacheKey($patron) { - return $patron['cat_username'].'_notifications'; + return $patron['cat_username'] . '_notifications'; } /** @@ -799,7 +793,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $response = $this->paiaGetAsArray( - 'core/'.$patron['cat_username'].'/notifications' + 'core/' . $patron['cat_username'] . '/notifications' ); } catch (\Exception $e) { // all error handling is done in paiaHandleErrors so pass on the excpetion @@ -852,7 +846,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA try { $response = $this->paiaDeleteRequest( - 'core/'.$patron['cat_username'].'/notifications/'.$this->getPaiaNotificationsId($messageId) + 'core/' . $patron['cat_username'] . '/notifications/' . $this->getPaiaNotificationsId($messageId) ); } catch (\Exception $e) { // all error handling is done in paiaHandleErrors so pass on the excpetion @@ -903,14 +897,14 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result = []; // item (0..1) URI of a particular copy - $result['item_id'] = (isset($doc['item']) ? $doc['item'] : ''); + $result['item_id'] = ($doc['item'] ?? ''); $result['cancel_details'] = ( isset($doc['cancancel']) && $doc['cancancel'] && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS) - ) ? $result['item_id'] : ''; + ) ? $result['item_id'] : ''; // edition (0..1) URI of a the document (no particular copy) // hook for retrieving alternative ItemId in case PAIA does not @@ -921,16 +915,16 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result['type'] = $this->paiaStatusString($doc['status']); // storage (0..1) textual description of location of the document - $result['location'] = (isset($doc['storage']) ? $doc['storage'] : null); + $result['location'] = ($doc['storage'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['position'] = (isset($doc['queue']) ? $doc['queue'] : null); + $result['position'] = ($doc['queue'] ?? null); // only true if status == 4 $result['available'] = false; // about (0..1) textual description of the document - $result['title'] = (isset($doc['about']) ? $doc['about'] : null); + $result['title'] = ($doc['about'] ?? null); // PAIA custom field // label (0..1) call number, shelf mark or similar item label @@ -1008,14 +1002,14 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result = []; // item (0..1) URI of a particular copy - $result['item_id'] = (isset($doc['item']) ? $doc['item'] : ''); + $result['item_id'] = ($doc['item'] ?? ''); $result['cancel_details'] = ( isset($doc['cancancel']) && $doc['cancancel'] && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS) - ) ? $result['item_id'] : ''; + ) ? $result['item_id'] : ''; // edition (0..1) URI of a the document (no particular copy) // hook for retrieving alternative ItemId in case PAIA does not @@ -1026,16 +1020,16 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result['type'] = $this->paiaStatusString($doc['status']); // storage (0..1) textual description of location of the document - $result['location'] = (isset($doc['storage']) ? $doc['storage'] : null); + $result['location'] = ($doc['storage'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['position'] = (isset($doc['queue']) ? $doc['queue'] : null); + $result['position'] = ($doc['queue'] ?? null); // only true if status == 4 $result['available'] = false; // about (0..1) textual description of the document - $result['title'] = (isset($doc['about']) ? $doc['about'] : null); + $result['title'] = ($doc['about'] ?? null); // PAIA custom field // label (0..1) call number, shelf mark or similar item label @@ -1078,17 +1072,17 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $result['renewable'] = ( isset($doc['canrenew']) && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS) - ) ? $doc['canrenew'] : false; + ) ? $doc['canrenew'] : false; // item (0..1) URI of a particular copy - $result['item_id'] = (isset($doc['item']) ? $doc['item'] : ''); + $result['item_id'] = ($doc['item'] ?? ''); $result['renew_details'] = ( isset($doc['canrenew']) && $doc['canrenew'] && $this->paiaCheckScope(self::SCOPE_WRITE_ITEMS) - ) ? $result['item_id'] : ''; + ) ? $result['item_id'] : ''; // edition (0..1) URI of a the document (no particular copy) // hook for retrieving alternative ItemId in case PAIA does not @@ -1099,17 +1093,17 @@ class PAIA extends \VuFind\ILS\Driver\PAIA // requested (0..1) URI that was originally requested // about (0..1) textual description of the document - $result['title'] = (isset($doc['about']) ? $doc['about'] : null); + $result['title'] = ($doc['about'] ?? null); // queue (0..1) number of waiting requests for the document or item - $result['request'] = (isset($doc['queue']) ? $doc['queue'] : null); + $result['request'] = ($doc['queue'] ?? null); // renewals (0..1) number of times the document has been renewed - $result['renew'] = (isset($doc['renewals']) ? $doc['renewals'] : null); + $result['renew'] = ($doc['renewals'] ?? null); // reminder (0..1) number of times the patron has been reminded $result['reminder'] = ( - isset($doc['reminder']) ? $doc['reminder'] : null + $doc['reminder'] ?? null ); // custom PAIA field @@ -1129,11 +1123,10 @@ class PAIA extends \VuFind\ILS\Driver\PAIA // canceled // error (0..1) error message, for instance if a request was rejected - $result['message'] = (isset($doc['error']) ? $doc['error'] : ''); + $result['message'] = ($doc['error'] ?? ''); // storage (0..1) textual description of location of the document - $result['borrowingLocation'] = (isset($doc['storage']) - ? $doc['storage'] : ''); + $result['borrowingLocation'] = ($doc['storage'] ?? ''); // storageid (0..1) location URI @@ -1288,11 +1281,9 @@ class PAIA extends \VuFind\ILS\Driver\PAIA $session = $this->getSession(); $session->patron - = isset($responseArray['patron']) - ? $responseArray['patron'] : null; + = $responseArray['patron'] ?? null; $session->access_token - = isset($responseArray['access_token']) - ? $responseArray['access_token'] : null; + = $responseArray['access_token'] ?? null; $session->scope = isset($responseArray['scope']) ? explode(' ', $responseArray['scope']) : null; @@ -1335,12 +1326,10 @@ class PAIA extends \VuFind\ILS\Driver\PAIA return $this->paiaParseUserDetails($patron, $responseArray); } - /** * Get valid patron keys * * @return array - * @access public */ public function getValidPatronUpdateKeys() { @@ -1351,7 +1340,6 @@ class PAIA extends \VuFind\ILS\Driver\PAIA ]; } - /** * update patron information using the PAIA function 'update patron' * see https://gbv.github.io/paia/paia.html#update-patron @@ -1463,7 +1451,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA public function checkRequestIsValid($id, $data, $patron) { // TODO: make this more configurable - if (isset($patron['status']) && $patron['status'] == 0 + if (isset($patron['status']) && $patron['status'] == 0 && isset($patron['expires']) && $patron['expires'] > date('Y-m-d') && in_array(self::SCOPE_WRITE_ITEMS, $this->getScope()) ) { @@ -1476,7 +1464,6 @@ class PAIA extends \VuFind\ILS\Driver\PAIA * Get last error * * @return null - * @access public */ public function getLastError() { @@ -1494,7 +1481,7 @@ class PAIA extends \VuFind\ILS\Driver\PAIA */ protected function paiaDeleteRequest($file, $access_token = null) { - if (is_null($access_token)) { + if (null === $access_token) { $access_token = $this->getSession()->access_token; } diff --git a/module/finc/src/finc/ILS/Driver/PAIAFactory.php b/module/finc/src/finc/ILS/Driver/PAIAFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f4fdb0a8e87bc6e3b750e38221f69f621fcbde16 --- /dev/null +++ b/module/finc/src/finc/ILS/Driver/PAIAFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * PAIA Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\ILS\Driver; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * PAIA Factory Class + * + * @category VuFind + * @package ILS_Drivers + * @author André Lahmann <lahmann@ub.uni-leipzig.de> + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class PAIAFactory implements FactoryInterface +{ + /** + * Create a FincILS + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return PAIA + * + * @throws ServiceNotFoundException if unable to resolve the service + * @throws ServiceNotCreatedException if creating a service throws an exception + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + $paia = new PAIA( + $container->get('VuFind\DateConverter'), + $container->get('VuFind\SessionManager') + ); + + $paia->setCacheStorage( + $container->get('VuFind\CacheManager')->getCache('object') + ); + + return $paia; + } +} diff --git a/module/finc/src/finc/Recommend/EbscoResults.php b/module/finc/src/finc/Recommend/EbscoResults.php index b90396fbe62223a8d833b4b25de0d8984bdc1d9c..5db3a26577a85d3312113d1689e75267a62d29ff 100644 --- a/module/finc/src/finc/Recommend/EbscoResults.php +++ b/module/finc/src/finc/Recommend/EbscoResults.php @@ -53,7 +53,6 @@ class EbscoResults implements * Ebsco service 3rdparty base URL * * @var string - * @access protected */ protected $baseUrl; @@ -61,7 +60,6 @@ class EbscoResults implements * Completed Ebsco service 3rdparty target URL to resolve * * @var string - * @access protected */ protected $targetUrl; @@ -69,7 +67,6 @@ class EbscoResults implements * Search string * * @var string - * @access protected */ protected $lookfor; @@ -77,7 +74,6 @@ class EbscoResults implements * Namespace of queried institution defined by 3rdparty service provider * * @var string - * @access protected */ protected $namespace; @@ -90,8 +86,6 @@ class EbscoResults implements /** * Constructor - * - * @access public */ public function __construct() { @@ -103,7 +97,6 @@ class EbscoResults implements * @param string $settings Settings from searches.ini. * * @return void - * @access public * @throws \Exception Isil as namespace for service has not been set yet or * is false. */ @@ -127,12 +120,11 @@ class EbscoResults implements * recommendation module and for reading any existing search parameters that may * be needed. * - * @param \VuFind\Search\Base\Params $params Search parameter object - * @param \Zend\StdLib\Parameters $request Parameter object representing user + * @param \VuFind\Search\Base\Params $params Search parameter object + * @param \Zend\StdLib\Parameters $request Parameter object representing user * request. * * @return void - * @access public */ public function init($params, $request) { @@ -156,7 +148,6 @@ class EbscoResults implements * @param \VuFind\Search\Base\Results $results Search results object * * @return void - * @access public * @throws \Exception JSON type error */ public function process($results) @@ -178,7 +169,7 @@ class EbscoResults implements if (json_last_error() !== JSON_ERROR_NONE) { $this->debug( 'JSON type error "' - . json_last_error_msg().'"' + . json_last_error_msg() . '"' . (empty($results->getBody()) ? '. EBSCO result was empty.' : '') ); } @@ -218,7 +209,6 @@ class EbscoResults implements * @param string $namespace Namespace of institution * * @return string The url to be sent - * @access protected */ protected function getURL($targetUrl, $namespace) { @@ -232,7 +222,6 @@ class EbscoResults implements * @param mixed $sortOrder Order of sort. Default: SORT_DESC * * @return array $results - * @access protected */ protected function sortByHits($results, $sortOrder = SORT_DESC) { diff --git a/module/finc/src/finc/Recommend/EbscoResultsFactory.php b/module/finc/src/finc/Recommend/EbscoResultsFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8930ec5af3c3380466f1745e9a5b3b71027658f6 --- /dev/null +++ b/module/finc/src/finc/Recommend/EbscoResultsFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * EbscoResults Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\Recommend; + +use finc\Recommend\EbscoResults; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * EbscoResults Factory Class + * + * @category VuFind + * @package Authorization + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * + * @codeCoverageIgnore + */ +class EbscoResultsFactory implements FactoryInterface +{ + /** + * Create a EbscoResults + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return EbscoResults + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new EbscoResults(); + } +} diff --git a/module/finc/src/finc/Resolver/Driver/Factory.php b/module/finc/src/finc/Resolver/Driver/Factory.php deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/module/finc/src/finc/Role/PermissionProvider/CatUserType.php b/module/finc/src/finc/Role/PermissionProvider/CatUserType.php index 8dcdaf0ce3d2d38efb155ebc98edba035cdb29fa..48fb81aff41fa67edaf44044027f98c23e9c6a98 100644 --- a/module/finc/src/finc/Role/PermissionProvider/CatUserType.php +++ b/module/finc/src/finc/Role/PermissionProvider/CatUserType.php @@ -80,7 +80,7 @@ class CatUserType implements \VuFind\Role\PermissionProvider\PermissionProviderI $patron = $this->ilsAuth->storedCatalogLogin(); if (isset($patron['type']) - && array_intersect($patron['type'], (array) $options)) { + && array_intersect($patron['type'], (array)$options)) { return ['loggedin']; } diff --git a/module/finc/src/finc/Role/PermissionProvider/Factory.php b/module/finc/src/finc/Role/PermissionProvider/CatUserTypeFactory.php similarity index 64% rename from module/finc/src/finc/Role/PermissionProvider/Factory.php rename to module/finc/src/finc/Role/PermissionProvider/CatUserTypeFactory.php index b22572c82178e71d212edcb5cde8ebaf7a042618..31a8ab74d216c3dc563e91d42fa93e3de8d5edf0 100644 --- a/module/finc/src/finc/Role/PermissionProvider/Factory.php +++ b/module/finc/src/finc/Role/PermissionProvider/CatUserTypeFactory.php @@ -1,6 +1,6 @@ <?php /** - * Permission Provider Factory Class + * CatUserType Factory Class * * PHP version 5 * @@ -27,6 +27,8 @@ */ namespace finc\Role\PermissionProvider; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; use Zend\ServiceManager\ServiceManager; /** @@ -40,49 +42,33 @@ use Zend\ServiceManager\ServiceManager; * * @codeCoverageIgnore */ -class Factory extends \VuFind\Role\PermissionProvider\Factory +class CatUserTypeFactory implements FactoryInterface { /** - * Factory for Username + * Create a CatUserType * - * @param ServiceManager $container Service manager. + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) * * @return CatUserType + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs */ - public static function getCatUserType(ServiceManager $container) - { + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } return new CatUserType( $container->get('ZfcRbac\Service\AuthorizationService'), $container->get('VuFind\ILSAuthenticator') ); } - - /** - * Factory for IpRangeFoFor - * - * @param ServiceManager $container Service manager. - * - * @return IpRangeFoFor - */ - public static function getIpRangeFoFor(ServiceManager $container) - { - return new IpRangeFoFor( - $container->get('Request'), - $container->get('VuFind\IpAddressUtils') - ); - } - - /** - * Factory for IpRegExFoFor - * - * @param ServiceManager $container Service manager. - * - * @return IpRegExFoFor - */ - public static function getIpRegExFoFor(ServiceManager $container) - { - return new IpRegExFoFor( - $container->get('Request') - ); - } } diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php index 37106e439210a6b3eae83e5838085b769c1fbf15..14d47f1af9a888b1689a1d9ef4b6c5867601b927 100644 --- a/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php +++ b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoFor.php @@ -45,20 +45,20 @@ namespace finc\Role\PermissionProvider; */ class IpRangeFoFor extends \VuFind\Role\PermissionProvider\IpRange { - /** * returns remote address based on eventual proxy headers - * + * * @return string */ - private function getRemoteAddr() { + private function getRemoteAddr() + { // a list of ips the request is forwarded for - first is latest $HttpXForwardedForList = explode(',', $this->request->getServer()->get('HTTP_X_FORWARDED_FOR')); - + if ($ip = array_shift($HttpXForwardedForList)) { return $ip; } - + // often provided by nginx-reverse-proxies, should be used since its the nature of the value if ($ip = $this->request->getServer()->get('HTTP_X_REAL_IP')) { return $ip; @@ -80,7 +80,6 @@ class IpRangeFoFor extends \VuFind\Role\PermissionProvider\IpRange public function getPermissions($options) { - // Check if any regex matches.... if ($this->ipAddressUtils->isInRange($this->getRemoteAddr(), (array)$options)) { // Match? Grant to all users (guest or logged in). diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRangeFoForFactory.php b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoForFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c8c3127c40be1c7294c6a794c5c423fa2ec7a05c --- /dev/null +++ b/module/finc/src/finc/Role/PermissionProvider/IpRangeFoForFactory.php @@ -0,0 +1,74 @@ +<?php +/** + * IpRangeFoFor 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; +use Zend\ServiceManager\ServiceManager; + +/** + * IpRangeFoFor Factory Class + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * + * @codeCoverageIgnore + */ +class IpRangeFoForFactory implements FactoryInterface +{ + /** + * Create an IpRangeFoFor + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return IpRangeFoFor + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new IpRangeFoFor( + $container->get('Request'), + $container->get('VuFind\IpAddressUtils') + ); + } +} diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php index c54b27513668296471c0f9843f86463f15f772ac..1e171c3f76d97553d870ef963da82adcc9964492 100644 --- a/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php +++ b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoFor.php @@ -42,17 +42,18 @@ class IpRegExFoFor extends \VuFind\Role\PermissionProvider\IpRegEx { /** * returns remote address based on eventual proxy headers - * + * * @return string */ - private function getRemoteAddr() { + private function getRemoteAddr() + { // a list of ips the request is forwarded for - first is latest $HttpXForwardedForList = explode(',', $this->request->getServer()->get('HTTP_X_FORWARDED_FOR')); - + if ($ip = array_shift($HttpXForwardedForList)) { return $ip; } - + // often provided by nginx-reverse-proxies, should be used since its the nature of the value if ($ip = $this->request->getServer()->get('HTTP_X_REAL_IP')) { return $ip; diff --git a/module/finc/src/finc/Role/PermissionProvider/IpRegExFoForFactory.php b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoForFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..651a6167bac8af77b93aea66556a91d59aa4277e --- /dev/null +++ b/module/finc/src/finc/Role/PermissionProvider/IpRegExFoForFactory.php @@ -0,0 +1,73 @@ +<?php +/** + * IpRegExFoFor 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\Role\PermissionProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; +use Zend\ServiceManager\ServiceManager; + +/** + * IpRegExFoFor Factory Class + * + * @category VuFind + * @package Authorization + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + * + * @codeCoverageIgnore + */ +class IpRegExFoForFactory implements FactoryInterface +{ + /** + * Create an IpRegExFoFor + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return IpRegExFoFor + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new IpRegExFoFor( + $container->get('Request') + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/BranchInfoViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/BranchInfoViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d69e56bb37f5fc45b8b3d846c8a8ea7687c8e602 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/BranchInfoViewHelperFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * BranchInfo Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class BranchInfoViewHelperFactory implements FactoryInterface +{ + /** + * Create a BranchInfo + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return BranchInfo + * + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new BranchInfo( + $container + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/CitationViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/CitationViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..17aa0ee617ff8c15f1ae1e279130436008ea91d5 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/CitationViewHelperFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Citation Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class CitationViewHelperFactory implements FactoryInterface +{ + /** + * Create a Citation + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return Citation + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new Citation($container->get('VuFind\DateConverter')); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLinkHelperFactory.php b/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLinkHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..04ff87339c44ca5eac7f48da17a8fe1fded9bb4e --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/ExternalCatalogueLinkHelperFactory.php @@ -0,0 +1,83 @@ +<?php +/** + * ExternalCatalogueLink Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class ExternalCatalogueLinkHelperFactory implements FactoryInterface +{ + /** + * Create an ExternalCatalogueLink + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return ExternalCatalogueLink + * + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + + // check if config json exists, as fallback empty array is passed to + // constructor + if (file_exists( + \VuFind\Config\Locator::getConfigPath('ExternalCatalogue.json') + ) + ) { + $externalAccessLinks = json_decode( + file_get_contents( + \VuFind\Config\Locator::getConfigPath('ExternalCatalogue.json') + ), + true + ); + } + + return new ExternalCatalogueLink( + $container->get('VuFind\Config')->get('config'), + empty($externalAccessLinks) ? [] : $externalAccessLinks + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/Factory.php b/module/finc/src/finc/View/Helper/Root/Factory.php deleted file mode 100644 index 02bc100e51034fe15eb82b8ce14163138c07c221..0000000000000000000000000000000000000000 --- a/module/finc/src/finc/View/Helper/Root/Factory.php +++ /dev/null @@ -1,234 +0,0 @@ -<?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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - */ -namespace finc\View\Helper\Root; - -use finc\Rewrite\EblRewrite; -use Interop\Container\ContainerInterface; - -/** - * Factory for Root view helpers. - * - * @category VuFind - * @package View_Helpers - * @author Demian Katz <demian.katz@villanova.edu> - * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org/wiki/development Wiki - * - * @codeCoverageIgnore - */ -class Factory -{ - /** - * Construct the Permission helper. - * - * @param ContainerInterface $container Service manager. - * - * @return Permission - */ - public static function getPermission(ContainerInterface $container) - { - return new Permission( - $container->get('VuFind\AuthManager'), - $container->get('ZfcRbac\Service\AuthorizationService') - ); - } - - /** - * Construct the Record helper. - * - * @param ContainerInterface $container Service manager. - * - * @return Record - */ - public static function getRecord(ContainerInterface $container) - { - $return = new Record( - $container->get('VuFind\Config')->get('config'), - $container->get('ViewHelperManager')->get('url'), - $container->get('VuFind\AuthManager'), - $container->get(EblRewrite::class), - $container->get('VuFind\Config')->get('Resolver'), - $container->get('VuFind\Config')->get('iconMapping') - ); - - //due to https://github.com/vufind-org/vufind/pull/718 - //we have to explicitly add the cover router (cf. # - $return->setCoverRouter($container->get('VuFind\Cover\Router')); - - return $return; - } - - /** - * Construct the RecordLink helper. - * - * @param ContainerInterface $container Service manager. - * - * @return RecordLink - */ - public static function getRecordLink(ContainerInterface $container) - { - $config = $container->get('VuFind\Config')->get('config'); - return new RecordLink( - $container->get('VuFind\RecordRouter'), - $container->get('VuFind\RecordLoader'), - $container->get('VuFind\Search'), - $config->Hierarchy->toArray() - ); - } - - /** - * Construct the Record helper. - * - * @return RecordLink - */ - public static function getInterlibraryLoanLink() - { - return new InterlibraryLoanLink(); - } - - /** - * Construct the Citation helper. - * - * @param ContainerInterface $container Service manager. - * - * @return Citation - */ - public static function getCitation(ContainerInterface $container) - { - return new Citation($container->get('VuFind\DateConverter')); - } - - /** - * Construct the Branches.yaml helper. - * - * @param ContainerInterface $container Service manager. - * - * @return BranchInfo - */ - public static function getBranchInfo(ContainerInterface $container) - { - return new BranchInfo( - $container - ); - } - - /** - * Construct the OpenUrl helper. - * - * @param ContainerInterface $container Service manager. - * - * @return OpenUrl - */ - public static function getOpenUrl(ContainerInterface $container) - { - $config = $container->get('VuFind\Config')->get('Resolver'); - - // check if config json exists, as fallback empty array is passed to - // constructor - if (file_exists( - \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') - ) - ) { - $openUrlRules = json_decode( - file_get_contents( - \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') - ), - true - ); - } - - return new OpenUrl( - $container->get('ViewHelperManager')->get('context'), - empty($openUrlRules) ? [] : $openUrlRules, - isset($config->General) ? $config : null - ); - } - - /** - * Construct the SideFacet helper. - * - * @param ContainerInterface $container Service manager. - * - * @return SideFacet - */ - public static function getSideFacet(ContainerInterface $container) - { - return new SideFacet( - $container->get('VuFind\Config')->get('facets') - ); - } - - /** - * Construct the External Catalogue Link Record helper. - * - * @param ContainerInterface $container Service manager - * - * @return ExternalCatalogueLink - */ - public static function getExternalCatalogueLink(ContainerInterface $container) - { - // check if config json exists, as fallback empty array is passed to - // constructor - if (file_exists( - \VuFind\Config\Locator::getConfigPath('ExternalCatalogue.json') - ) - ) { - $externalAccessLinks = json_decode( - file_get_contents( - \VuFind\Config\Locator::getConfigPath('ExternalCatalogue.json') - ), - true - ); - } - - return new ExternalCatalogueLink( - $container->get('VuFind\Config')->get('config'), - empty($externalAccessLinks) ? [] : $externalAccessLinks - ); - } - - /** - * Construct ExternalLink - * - * @return ExternalLink - */ - public static function getExternalLink() - { - return new ExternalLink(); - } - - /** - * Construct RenderArray - * - * @return RenderArray - */ - public static function getRenderArray() - { - return new RenderArray(); - } -} diff --git a/module/finc/src/finc/View/Helper/Root/OpenUrlViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/OpenUrlViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..ce4b4efaf003fa71ddabe98e773c8e97bca904f0 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/OpenUrlViewHelperFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * OpenUrl Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class OpenUrlViewHelperFactory implements FactoryInterface +{ + /** + * Create an OpenUrl + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return OpenUrl + * + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config')->get('Resolver'); + + // check if config json exists, as fallback empty array is passed to + // constructor + if (file_exists( + \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') + ) + ) { + $openUrlRules = json_decode( + file_get_contents( + \VuFind\Config\Locator::getConfigPath('OpenUrlRules.json') + ), + true + ); + } + + return new OpenUrl( + $container->get('ViewHelperManager')->get('context'), + empty($openUrlRules) ? [] : $openUrlRules, + isset($config->General) ? $config : null + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/PermissionViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/PermissionViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a47259af4143794f9169a78a467c3cae3a72dcc3 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/PermissionViewHelperFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * PermissionViewHelper Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class PermissionViewHelperFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return Permission + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new Permission( + $container->get('VuFind\AuthManager'), + $container->get('ZfcRbac\Service\AuthorizationService') + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/RecordLinkViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/RecordLinkViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a2ba78abb7b1f94972115bf67d3941cb1b7a450b --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/RecordLinkViewHelperFactory.php @@ -0,0 +1,75 @@ +<?php +/** + * RecordLinkViewHelper Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use finc\Rewrite\EblRewrite; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class RecordLinkViewHelperFactory implements FactoryInterface +{ + /** + * Create a RecordLink + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return RecordLink + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $config = $container->get('VuFind\Config')->get('config'); + return new RecordLink( + $container->get('VuFind\RecordRouter'), + $container->get('VuFind\RecordLoader'), + $container->get('VuFind\Search'), + $config->Hierarchy->toArray() + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/RecordViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/RecordViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..18da624d6eda7899a77277a9e99568da436a94ae --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/RecordViewHelperFactory.php @@ -0,0 +1,82 @@ +<?php +/** + * RecordViewHelper Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use finc\Rewrite\EblRewrite; +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class RecordViewHelperFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return Record + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $recordViewHelper = new Record( + $container->get('VuFind\Config')->get('config'), + $container->get('ViewHelperManager')->get('url'), + $container->get('VuFind\AuthManager'), + $container->get(EblRewrite::class), + $container->get('VuFind\Config')->get('Resolver'), + $container->get('VuFind\Config')->get('iconMapping') + ); + + //due to https://github.com/vufind-org/vufind/pull/718 + //we have to explicitly add the cover router (cf. # + $recordViewHelper->setCoverRouter($container->get('VuFind\Cover\Router')); + + return $recordViewHelper; + } +} diff --git a/module/finc/src/finc/View/Helper/Root/SideFacetViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/SideFacetViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a3d7b49aaa38fef712b47e55c1a2aec1a4d1b38d --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/SideFacetViewHelperFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * SideFacet Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class SideFacetViewHelperFactory implements FactoryInterface +{ + /** + * Create a SideFacet + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return SideFacet + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new SideFacet( + $container->get('VuFind\Config')->get('facets') + ); + } +} diff --git a/module/finc/src/finc/View/Helper/Root/ViewHelperFactory.php b/module/finc/src/finc/View/Helper/Root/ViewHelperFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5dcc84552402d6a64299b1b50b1d088fdbbd7789 --- /dev/null +++ b/module/finc/src/finc/View/Helper/Root/ViewHelperFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * ViewHelper Factory Class + * + * PHP version 7 + * + * Copyright (C) Leipzig University Library 2022. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +namespace finc\View\Helper\Root; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * ViewHelper Factory Class + * + * @category VuFind + * @package View_Helpers + * @author Robert Lange <lange@ub.uni-leipzig.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:hierarchy_components Wiki + */ +class ViewHelperFactory implements FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + */ + public function __invoke( + ContainerInterface $container, + $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + return new $requestedName(); + } +} diff --git a/package.json b/package.json index f427f8ac88d5244aa55811f2395e3e207526e647..8ea3aa89849874897d425384c921131596734dd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vufind", - "version": "5.1.1", + "version": "6.1.2", "description": "Dev tools to handle css preprocessing, js magic, and compression", "repository": { "type": "git", @@ -13,8 +13,8 @@ "grunt": "^1.0.0", "grunt-cli": "^1.2.0", "grunt-contrib-less": "1.3.0", - "grunt-less-to-sass": "0.0.10", - "grunt-sass": "^1.0.0", + "grunt-less-to-sass": "duvillierA/grunt-less-to-sass#0.0.11", + "grunt-sass": "^2.0.0", "jit-grunt": "^0.9.1", "node-sass": "^4.11.0" }, diff --git a/packages/DEBIAN/changelog b/packages/DEBIAN/changelog index 919258e8921d7dc56723a949fc1c7883c338df37..a45a887f997af488dd9a5349d36eface54d8e1de 100644 --- a/packages/DEBIAN/changelog +++ b/packages/DEBIAN/changelog @@ -1,3 +1,33 @@ +vufind 6.1.2 distribution; urgency=low + + * VuFind 6.1.2 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 13 Jul 2020 10:00:00 UTC + +vufind 6.1.1 distribution; urgency=low + + * VuFind 6.1.1 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 16 Mar 2020 10:00:00 UTC + +vufind 6.1 distribution; urgency=low + + * VuFind 6.1 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 3 Feb 2020 10:00:00 UTC + +vufind 6.0.1 distribution; urgency=low + + * VuFind 6.0.1 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 16 Sep 2019 10:00:00 UTC + +vufind 6.0 distribution; urgency=low + + * VuFind 6.0 release (see http://vufind.org/wiki/changelog for details) + + -- maintainer VuFind Project Administration Team <vufind-admins@lists.sourceforge.net> Mo 15 Jul 2019 10:00:00 UTC + vufind 5.1.1 distribution; urgency=low * VuFind 5.1.1 release (see http://vufind.org/wiki/changelog for details) diff --git a/packages/DEBIAN/control b/packages/DEBIAN/control index 00e368d8321cb88706985e470614c63660bf6aa3..8e95f06574f548258e81b9fcd94f5747640ef3f4 100644 --- a/packages/DEBIAN/control +++ b/packages/DEBIAN/control @@ -1,5 +1,5 @@ Package: vufind -Version: 5.1.1 +Version: 6.1.2 Section: World Wide Web Priority: Optional Architecture: all diff --git a/public/index.php b/public/index.php index 50aa986dc3b00130a468a5d12e7e2e3cc056f7a0..78117fc5094218fb6949c86c77aadabf62e063ca 100644 --- a/public/index.php +++ b/public/index.php @@ -1,13 +1,25 @@ <?php -// If the XHProf profiler is enabled, set it up now: -$xhprof = getenv('VUFIND_PROFILER_XHPROF'); -if (!empty($xhprof)) { - if (extension_loaded('xhprof')) { - xhprof_enable(); - } elseif (extension_loaded('tideways')) { - tideways_enable(); - } else { - $xhprof = false; +// If the profiler is enabled, set it up now: +$vufindProfiler = getenv('VUFIND_PROFILER_XHPROF'); +if (!empty($vufindProfiler)) { + if (extension_loaded('tideways_xhprof')) { + tideways_xhprof_enable(); + + // Handle final profiling details, if necessary: + register_shutdown_function(function () use ($vufindProfiler) { + $xhprofData = tideways_xhprof_disable(); + $xhprofRunId = uniqid(); + $suffix = 'vufind'; + $dir = ini_get('xhprof.output_dir'); + if (empty($dir)) { + $dir = sys_get_temp_dir(); + } + file_put_contents( + "$dir/$xhprofRunId.$suffix.xhprof", serialize($xhprofData) + ); + $url = "$vufindProfiler?run=$xhprofRunId&source=$suffix"; + echo "<a href='$url'>Profiler output</a>"; + }); } } @@ -69,17 +81,3 @@ if (!class_exists('Zend\Loader\AutoloaderFactory')) { // Run the application! Zend\Mvc\Application::init(require 'config/application.config.php')->run(); - -// Handle final profiling details, if necessary: -if ($xhprof) { - $xhprofData = extension_loaded('xhprof') ? xhprof_disable() : tideways_disable(); - $xhprofRunId = uniqid(); - $suffix = 'vufind'; - $dir = ini_get('xhprof.output_dir'); - if (empty($dir)) { - $dir = sys_get_temp_dir(); - } - file_put_contents("$dir/$xhprofRunId.$suffix.xhprof", serialize($xhprofData)); - $url = "$xhprof?run=$xhprofRunId&source=$suffix"; - echo "<a href='$url'>Profiler output</a>"; -} diff --git a/solr.bat b/solr.bat index fbce82ff4b61b5db258cdb604a8b55d9d8bcb75e..cff8418c1252537236020d60f85dd237f15b3049 100644 --- a/solr.bat +++ b/solr.bat @@ -40,8 +40,8 @@ rem Unrecognized action -- display help text if "!%1!"=="!!" goto usage rem Set VUFIND_HOME (if not already set) -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound -rem VUFIND_HOME not set -- try to call env.bat to +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound +rem VUFIND_HOME not set -- try to call env.bat to rem fix the problem before we give up completely if exist env.bat goto useenvbat rem If env.bat doesn't exist, the user hasn't run the installer yet. @@ -53,7 +53,7 @@ set VUFIND_HOME=%VUFIND_HOME:~0,-1% goto vufindhomefound :useenvbat call env > nul -if not (!%VUFIND_HOME%!)==(!!) goto vufindhomefound +if not "!%VUFIND_HOME%!"=="!!" goto vufindhomefound echo You need to set the VUFIND_HOME environmental variable before running this script. goto end :vufindhomefound diff --git a/solr/vufind/jars/sqlite-jdbc-3.27.2.jar b/solr/vufind/jars/sqlite-jdbc-3.27.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..25e777f66aca8307d98825cd3407c185a3726f42 Binary files /dev/null and b/solr/vufind/jars/sqlite-jdbc-3.27.2.jar differ diff --git a/themes/bootprint3/less/bootprint.less b/themes/bootprint3/less/bootprint.less index 9a12b16632d4950d52327f46c16db0e2d2279c2d..215ccd22f10e70dc0b10dfa18d1a5425c6baab3b 100644 --- a/themes/bootprint3/less/bootprint.less +++ b/themes/bootprint3/less/bootprint.less @@ -127,6 +127,12 @@ ul.random.image li img { margin: 0 auto; } margin: 5px 10px 5px 0; } +/* --- Record --- */ +.record-nav .action-toolbar { + display: table; + margin: 0 auto 1rem; +} + /* --- Sidebar --- */ .sidebar { .facet-group { margin-bottom: 5px; } @@ -145,8 +151,6 @@ ul.random.image li img { margin: 0 auto; } .jstree-node.active .badge, .jstree-node.active .badge .fa { color: @active-orange; } .facet .badge a { color: #fff; } - - .facet.facetOR { padding-left: 1rem; } } .checkboxFilter .facet { padding-left: 1px; } diff --git a/themes/bootprint3/less/icons.less b/themes/bootprint3/less/icons.less index 3728166f92b7196489fdd61b08714db6be2e6e7a..7839fdddb49e96be0485a324ab004a251a508722 100644 --- a/themes/bootprint3/less/icons.less +++ b/themes/bootprint3/less/icons.less @@ -1,6 +1,9 @@ @bp3-icon-path: '../../bootprint3/images/icons'; + .doi-icon { width: 16px; } -.bp-icon { + +.bp-icon, +[class*="btn-type-"]::before { background-position: center center; background-repeat: no-repeat; color: transparent !important; @@ -14,8 +17,42 @@ width: 16px; } .fa-x { background-image: url("@{bp3-icon-path}/page_white.png"); &:extend(.bp-icon); } -i.fa-archive { background-image: url("@{bp3-icon-path}/package.png"); &:extend(.bp-icon); } + +/** + * Toolbar Button Icons + */ +[class*="btn-type-"]::before { + margin-right: .25rem; +} +.btn-type-add::before, +i.fa-plus-circle { background-image: url("@{bp3-icon-path}/add.png"); &:extend(.bp-icon); } +.btn-type-cite::before, i.fa-asterisk { background-image: url("@{bp3-icon-path}/list.png"); &:extend(.bp-icon); } +.btn-type-delete::before, +i.fa-trash, +i.fa-trash-o { background-image: url("@{bp3-icon-path}/bin.png"); &:extend(.bp-icon); } +.btn-type-email::before, +i.fa-email, +i.fa-envelope, +i.fa-envelope-o { background-image: url("@{bp3-icon-path}/email.png"); &:extend(.bp-icon); } +.btn-type-empty::before, +i.fa-close { background-image: url("@{bp3-icon-path}/cross.png"); &:extend(.bp-icon); } +.btn-type-export::before, +i.fa-external-link { background-image: url("@{bp3-icon-path}/link_go.png"); &:extend(.bp-icon); } +.btn-type-minus::before, +i.fa-minus-circle, +i.fa-minus-sign { background-image: url("@{bp3-icon-path}/delete.png"); &:extend(.bp-icon); } +.btn-type-phone::before, +i.fa-mobile { background-image: url("@{bp3-icon-path}/phone.png"); &:extend(.bp-icon); } +.btn-type-print::before, +i.fa-print { background-image: url("@{bp3-icon-path}/printer.png"); &:extend(.bp-icon); } +.btn-type-save::before, +i.fa-star { background-image: url("@{bp3-icon-path}/star.png"); &:extend(.bp-icon); } + +/** + * Other FontAwesome overrides + */ +i.fa-archive { background-image: url("@{bp3-icon-path}/package.png"); &:extend(.bp-icon); } i.fa-atlas { background-image: url("@{bp3-icon-path}/map.png"); &:extend(.bp-icon); } i.fa-bell { background-image: url("@{bp3-icon-path}/bell.png"); &:extend(.bp-icon); } i.fa-book { background-image: url("@{bp3-icon-path}/book.png"); &:extend(.bp-icon); } @@ -32,17 +69,12 @@ i.fa-cdrom { background-image: url("@{bp3-icon-path}/cd.png"); &:extend(.bp-icon i.fa-chart { background-image: url("@{bp3-icon-path}/chart_bar.png"); &:extend(.bp-icon); } i.fa-chipcartridge { background-image: url("@{bp3-icon-path}/server.png"); &:extend(.bp-icon); } i.fa-collage { background-image: url("@{bp3-icon-path}/pictures.png"); &:extend(.bp-icon); } -i.fa-close { background-image: url("@{bp3-icon-path}/cross.png"); &:extend(.bp-icon); } i.fa-disccartridge { background-image: url("@{bp3-icon-path}/cd.png"); &:extend(.bp-icon); } i.fa-drawing { background-image: url("@{bp3-icon-path}/photo.png"); &:extend(.bp-icon); } i.fa-ebook { background-image: url("@{bp3-icon-path}/book_addresses.png"); &:extend(.bp-icon); } i.fa-edit { background-image: url("@{bp3-icon-path}/edit.png"); &:extend(.bp-icon); } i.fa-electronic { background-image: url("@{bp3-icon-path}/mouse.png"); &:extend(.bp-icon); } -i.fa-email, -i.fa-envelope, -i.fa-envelope-o { background-image: url("@{bp3-icon-path}/email.png"); &:extend(.bp-icon); } i.fa-exchange { background-image: url("@{bp3-icon-path}/arrow_refresh.png"); &:extend(.bp-icon); } -i.fa-external-link { background-image: url("@{bp3-icon-path}/link_go.png"); &:extend(.bp-icon); } i.fa-filmstrip { background-image: url("@{bp3-icon-path}/film.png"); &:extend(.bp-icon); } i.fa-flag { background-image: url("@{bp3-icon-path}/flag_red.png"); &:extend(.bp-icon); } i.fa-flashcard { background-image: url("@{bp3-icon-path}/table_lightening.png"); &:extend(.bp-icon); } @@ -61,8 +93,6 @@ i.fa-lock { background-image: url("@{bp3-icon-path}/lock.png"); &:extend(.bp-ico i.fa-manuscript { background-image: url("@{bp3-icon-path}/script.png"); &:extend(.bp-icon); } i.fa-map { background-image: url("@{bp3-icon-path}/map.png"); &:extend(.bp-icon); } i.fa-microfilm { background-image: url("@{bp3-icon-path}/film.png"); &:extend(.bp-icon); } -i.fa-minus-circle,i.fa-minus-sign { background-image: url("@{bp3-icon-path}/delete.png"); &:extend(.bp-icon); } -i.fa-mobile { background-image: url("@{bp3-icon-path}/phone.png"); &:extend(.bp-icon); } i.fa-motionpicture { background-image: url("@{bp3-icon-path}/television.png"); &:extend(.bp-icon); } i.fa-musicalscore { background-image: url("@{bp3-icon-path}/music.png"); &:extend(.bp-icon); } i.fa-musicrecording { background-image: url("@{bp3-icon-path}/music.png"); &:extend(.bp-icon); } @@ -74,15 +104,12 @@ i.fa-photo { background-image: url("@{bp3-icon-path}/photo.png"); &:extend(.bp-i i.fa-photonegative { background-image: url("@{bp3-icon-path}/film.png"); &:extend(.bp-icon); } i.fa-physicalobject { background-image: url("@{bp3-icon-path}/box.png"); &:extend(.bp-icon); } i.fa-plus { background-image: url("@{bp3-icon-path}/add.png"); &:extend(.bp-icon); } -i.fa-plus-circle { background-image: url("@{bp3-icon-path}/add.png"); &:extend(.bp-icon); } -i.fa-print { background-image: url("@{bp3-icon-path}/printer.png"); &:extend(.bp-icon); } i.fa-qrcode { background-image: url("@{bp3-icon-path}/qrcode.png"); &:extend(.bp-icon); } i.fa-remove { background-image: url("@{bp3-icon-path}/delete.png"); &:extend(.bp-icon); } i.fa-renew { background-image: url("@{bp3-icon-path}/renew.png"); &:extend(.bp-icon); } i.fa-renew-all { background-image: url("@{bp3-icon-path}/renewAll.png"); &:extend(.bp-icon); } i.fa-report { background-image: url("@{bp3-icon-path}/report.png"); &:extend(.bp-icon); } i.fa-rss { background-image: url("@{bp3-icon-path}/feed.png"); &:extend(.bp-icon); } -i.fa-save { background-image: url("@{bp3-icon-path}/disk.png"); &:extend(.bp-icon); } i.fa-search { background-image: url("@{bp3-icon-path}/magnifier.png"); &:extend(.bp-icon); } i.fa-search-plus { background-image: url("@{bp3-icon-path}/magnifier_zoom_in.png"); &:extend(.bp-icon); } i.fa-sensorimage { background-image: url("@{bp3-icon-path}/photo.png"); &:extend(.bp-icon); } @@ -96,15 +123,12 @@ i.fa-soundcassette { background-image: url("@{bp3-icon-path}/sound.png"); &:exte i.fa-sounddisc { background-image: url("@{bp3-icon-path}/cd.png"); &:extend(.bp-icon); } i.fa-soundrecording { background-image: url("@{bp3-icon-path}/sound.png"); &:extend(.bp-icon); } i.fa-spinner { background-image: url("@{bp3-icon-path}/ajax_loading.gif"); &:extend(.bp-icon); } -i.fa-star { background-image: url("@{bp3-icon-path}/star.png"); &:extend(.bp-icon); } i.fa-status-unknown { background-image: url("@{bp3-icon-path}/bullet_orange.png"); &:extend(.bp-icon); } i.fa-suitcase { background-image: url("@{bp3-icon-path}/bookbag.png"); &:extend(.bp-icon); } i.fa-tapecartridge { background-image: url("@{bp3-icon-path}/drive.png"); &:extend(.bp-icon); } i.fa-tapecassette { background-image: url("@{bp3-icon-path}/drive.png"); &:extend(.bp-icon); } i.fa-tapereel { background-image: url("@{bp3-icon-path}/film.png"); &:extend(.bp-icon); } i.fa-transparency { background-image: url("@{bp3-icon-path}/film.png"); &:extend(.bp-icon); } -i.fa-trash, -i.fa-trash-o { background-image: url("@{bp3-icon-path}/bin.png"); &:extend(.bp-icon); } i.fa-tree { background-image: url("@{bp3-icon-path}/treeCurrent.png"); &:extend(.bp-icon); } i.fa-tree-muted { background-image: url("@{bp3-icon-path}/treeMuted.png"); &:extend(.bp-icon); } i.fa-unknown { background-image: url("@{bp3-icon-path}/page_white.png"); &:extend(.bp-icon); } diff --git a/themes/bootprint3/less/search.less b/themes/bootprint3/less/search.less index 772c18080713332fda656b357cd640f1302bad10..4cde81e90d3c68df6a05b930916599dc85033dff 100644 --- a/themes/bootprint3/less/search.less +++ b/themes/bootprint3/less/search.less @@ -5,6 +5,14 @@ } .group .match { margin-top: .5em; } +.toolbar-btn, +.record-nav .cart-add, +.record-nav .cart-remove, +.reset-filters-btn { + padding-left: .5rem; + padding-right: .5rem; +} + /* --- Search form --- */ .searchForm_lookfor, .searchForm_type { border-color: @brand-primary; } @@ -48,7 +56,6 @@ } } -.bulkActionButtons { padding-bottom: 1rem; } .top-facets .facet a { vertical-align: middle; } .result { padding: 1rem; diff --git a/themes/bootprint3/scss/bootprint.scss b/themes/bootprint3/scss/bootprint.scss index c7463c8bdd1863993ed159a4633a2c250ed67617..a061553dce0666fd5880b60f665ac82b8a5db5db 100644 --- a/themes/bootprint3/scss/bootprint.scss +++ b/themes/bootprint3/scss/bootprint.scss @@ -127,6 +127,12 @@ ul.random.image li img { margin: 0 auto; } margin: 5px 10px 5px 0; } +/* --- Record --- */ +.record-nav .action-toolbar { + display: table; + margin: 0 auto 1rem; +} + /* --- Sidebar --- */ .sidebar { .facet-group { margin-bottom: 5px; } @@ -145,8 +151,6 @@ ul.random.image li img { margin: 0 auto; } .jstree-node.active .badge, .jstree-node.active .badge .fa { color: $active-orange; } .facet .badge a { color: #fff; } - - .facet.facetOR { padding-left: 1rem; } } .checkboxFilter .facet { padding-left: 1px; } diff --git a/themes/bootprint3/scss/icons.scss b/themes/bootprint3/scss/icons.scss index 48823e0f51b1c30be1167f84658954a214a303a9..899b21f2425106080791207f0027ae3caed0bae5 100644 --- a/themes/bootprint3/scss/icons.scss +++ b/themes/bootprint3/scss/icons.scss @@ -1,6 +1,8 @@ $bp3-icon-path: '../../bootprint3/images/icons'; .doi-icon { width: 16px; } -.bp-icon { + +.bp-icon, +[class*="btn-type-"]::before { background-position: center center; background-repeat: no-repeat; color: transparent !important; @@ -14,8 +16,42 @@ $bp3-icon-path: '../../bootprint3/images/icons'; width: 16px; } .fa-x { background-image: url("#{$bp3-icon-path}/page_white.png"); @extend .bp-icon; } -i.fa-archive { background-image: url("#{$bp3-icon-path}/package.png"); @extend .bp-icon; } + +/** + * Toolbar Button Icons + */ +[class*="btn-type-"]::before { + margin-right: .25rem; +} +.btn-type-add::before, +i.fa-plus-circle { background-image: url("#{$bp3-icon-path}/add.png"); @extend .bp-icon; } +.btn-type-cite::before, i.fa-asterisk { background-image: url("#{$bp3-icon-path}/list.png"); @extend .bp-icon; } +.btn-type-delete::before, +i.fa-trash, +i.fa-trash-o { background-image: url("#{$bp3-icon-path}/bin.png"); @extend .bp-icon; } +.btn-type-email::before, +i.fa-email, +i.fa-envelope, +i.fa-envelope-o { background-image: url("#{$bp3-icon-path}/email.png"); @extend .bp-icon; } +.btn-type-empty::before, +i.fa-close { background-image: url("#{$bp3-icon-path}/cross.png"); @extend .bp-icon; } +.btn-type-export::before, +i.fa-external-link { background-image: url("#{$bp3-icon-path}/link_go.png"); @extend .bp-icon; } +.btn-type-minus::before, +i.fa-minus-circle, +i.fa-minus-sign { background-image: url("#{$bp3-icon-path}/delete.png"); @extend .bp-icon; } +.btn-type-phone::before, +i.fa-mobile { background-image: url("#{$bp3-icon-path}/phone.png"); @extend .bp-icon; } +.btn-type-print::before, +i.fa-print { background-image: url("#{$bp3-icon-path}/printer.png"); @extend .bp-icon; } +.btn-type-save::before, +i.fa-star { background-image: url("#{$bp3-icon-path}/star.png"); @extend .bp-icon; } + +/** + * Other FontAwesome overrides + */ +i.fa-archive { background-image: url("#{$bp3-icon-path}/package.png"); @extend .bp-icon; } i.fa-atlas { background-image: url("#{$bp3-icon-path}/map.png"); @extend .bp-icon; } i.fa-bell { background-image: url("#{$bp3-icon-path}/bell.png"); @extend .bp-icon; } i.fa-book { background-image: url("#{$bp3-icon-path}/book.png"); @extend .bp-icon; } @@ -32,17 +68,12 @@ i.fa-cdrom { background-image: url("#{$bp3-icon-path}/cd.png"); @extend .bp-icon i.fa-chart { background-image: url("#{$bp3-icon-path}/chart_bar.png"); @extend .bp-icon; } i.fa-chipcartridge { background-image: url("#{$bp3-icon-path}/server.png"); @extend .bp-icon; } i.fa-collage { background-image: url("#{$bp3-icon-path}/pictures.png"); @extend .bp-icon; } -i.fa-close { background-image: url("#{$bp3-icon-path}/cross.png"); @extend .bp-icon; } i.fa-disccartridge { background-image: url("#{$bp3-icon-path}/cd.png"); @extend .bp-icon; } i.fa-drawing { background-image: url("#{$bp3-icon-path}/photo.png"); @extend .bp-icon; } i.fa-ebook { background-image: url("#{$bp3-icon-path}/book_addresses.png"); @extend .bp-icon; } i.fa-edit { background-image: url("#{$bp3-icon-path}/edit.png"); @extend .bp-icon; } i.fa-electronic { background-image: url("#{$bp3-icon-path}/mouse.png"); @extend .bp-icon; } -i.fa-email, -i.fa-envelope, -i.fa-envelope-o { background-image: url("#{$bp3-icon-path}/email.png"); @extend .bp-icon; } i.fa-exchange { background-image: url("#{$bp3-icon-path}/arrow_refresh.png"); @extend .bp-icon; } -i.fa-external-link { background-image: url("#{$bp3-icon-path}/link_go.png"); @extend .bp-icon; } i.fa-filmstrip { background-image: url("#{$bp3-icon-path}/film.png"); @extend .bp-icon; } i.fa-flag { background-image: url("#{$bp3-icon-path}/flag_red.png"); @extend .bp-icon; } i.fa-flashcard { background-image: url("#{$bp3-icon-path}/table_lightening.png"); @extend .bp-icon; } @@ -61,8 +92,6 @@ i.fa-lock { background-image: url("#{$bp3-icon-path}/lock.png"); @extend .bp-ico i.fa-manuscript { background-image: url("#{$bp3-icon-path}/script.png"); @extend .bp-icon; } i.fa-map { background-image: url("#{$bp3-icon-path}/map.png"); @extend .bp-icon; } i.fa-microfilm { background-image: url("#{$bp3-icon-path}/film.png"); @extend .bp-icon; } -i.fa-minus-circle,i.fa-minus-sign { background-image: url("#{$bp3-icon-path}/delete.png"); @extend .bp-icon; } -i.fa-mobile { background-image: url("#{$bp3-icon-path}/phone.png"); @extend .bp-icon; } i.fa-motionpicture { background-image: url("#{$bp3-icon-path}/television.png"); @extend .bp-icon; } i.fa-musicalscore { background-image: url("#{$bp3-icon-path}/music.png"); @extend .bp-icon; } i.fa-musicrecording { background-image: url("#{$bp3-icon-path}/music.png"); @extend .bp-icon; } @@ -74,15 +103,12 @@ i.fa-photo { background-image: url("#{$bp3-icon-path}/photo.png"); @extend .bp-i i.fa-photonegative { background-image: url("#{$bp3-icon-path}/film.png"); @extend .bp-icon; } i.fa-physicalobject { background-image: url("#{$bp3-icon-path}/box.png"); @extend .bp-icon; } i.fa-plus { background-image: url("#{$bp3-icon-path}/add.png"); @extend .bp-icon; } -i.fa-plus-circle { background-image: url("#{$bp3-icon-path}/add.png"); @extend .bp-icon; } -i.fa-print { background-image: url("#{$bp3-icon-path}/printer.png"); @extend .bp-icon; } i.fa-qrcode { background-image: url("#{$bp3-icon-path}/qrcode.png"); @extend .bp-icon; } i.fa-remove { background-image: url("#{$bp3-icon-path}/delete.png"); @extend .bp-icon; } i.fa-renew { background-image: url("#{$bp3-icon-path}/renew.png"); @extend .bp-icon; } i.fa-renew-all { background-image: url("#{$bp3-icon-path}/renewAll.png"); @extend .bp-icon; } i.fa-report { background-image: url("#{$bp3-icon-path}/report.png"); @extend .bp-icon; } i.fa-rss { background-image: url("#{$bp3-icon-path}/feed.png"); @extend .bp-icon; } -i.fa-save { background-image: url("#{$bp3-icon-path}/disk.png"); @extend .bp-icon; } i.fa-search { background-image: url("#{$bp3-icon-path}/magnifier.png"); @extend .bp-icon; } i.fa-search-plus { background-image: url("#{$bp3-icon-path}/magnifier_zoom_in.png"); @extend .bp-icon; } i.fa-sensorimage { background-image: url("#{$bp3-icon-path}/photo.png"); @extend .bp-icon; } @@ -96,15 +122,12 @@ i.fa-soundcassette { background-image: url("#{$bp3-icon-path}/sound.png"); @exte i.fa-sounddisc { background-image: url("#{$bp3-icon-path}/cd.png"); @extend .bp-icon; } i.fa-soundrecording { background-image: url("#{$bp3-icon-path}/sound.png"); @extend .bp-icon; } i.fa-spinner { background-image: url("#{$bp3-icon-path}/ajax_loading.gif"); @extend .bp-icon; } -i.fa-star { background-image: url("#{$bp3-icon-path}/star.png"); @extend .bp-icon; } i.fa-status-unknown { background-image: url("#{$bp3-icon-path}/bullet_orange.png"); @extend .bp-icon; } i.fa-suitcase { background-image: url("#{$bp3-icon-path}/bookbag.png"); @extend .bp-icon; } i.fa-tapecartridge { background-image: url("#{$bp3-icon-path}/drive.png"); @extend .bp-icon; } i.fa-tapecassette { background-image: url("#{$bp3-icon-path}/drive.png"); @extend .bp-icon; } i.fa-tapereel { background-image: url("#{$bp3-icon-path}/film.png"); @extend .bp-icon; } i.fa-transparency { background-image: url("#{$bp3-icon-path}/film.png"); @extend .bp-icon; } -i.fa-trash, -i.fa-trash-o { background-image: url("#{$bp3-icon-path}/bin.png"); @extend .bp-icon; } i.fa-tree { background-image: url("#{$bp3-icon-path}/treeCurrent.png"); @extend .bp-icon; } i.fa-tree-muted { background-image: url("#{$bp3-icon-path}/treeMuted.png"); @extend .bp-icon; } i.fa-unknown { background-image: url("#{$bp3-icon-path}/page_white.png"); @extend .bp-icon; } diff --git a/themes/bootprint3/scss/search.scss b/themes/bootprint3/scss/search.scss index 6b11e7e684ea141a4c8c3760750cdf77cd31fb5b..21c882b60ba754a4ef036e0d47267172a32502bf 100644 --- a/themes/bootprint3/scss/search.scss +++ b/themes/bootprint3/scss/search.scss @@ -5,6 +5,14 @@ } .group .match { margin-top: .5em; } +.toolbar-btn, +.record-nav .cart-add, +.record-nav .cart-remove, +.reset-filters-btn { + padding-left: .5rem; + padding-right: .5rem; +} + /* --- Search form --- */ .searchForm_lookfor, .searchForm_type { border-color: $brand-primary; } @@ -48,7 +56,6 @@ } } -.bulkActionButtons { padding-bottom: 1rem; } .top-facets .facet a { vertical-align: middle; } .result { padding: 1rem; diff --git a/themes/bootstrap3/css/print.css b/themes/bootstrap3/css/print.css index ed5f07cbc820edcade024cd14a681b8f16e9f535..bb3c4c478e0c6f6c30b9fe8807371b1a77b47ff1 100644 --- a/themes/bootstrap3/css/print.css +++ b/themes/bootstrap3/css/print.css @@ -6,4 +6,7 @@ a[href]:after{ content: ''; } /* hide following urls */ .container { margin:0; } /* print margins */ /* print utilities */ .hidden-print, -.hidden-print * { display:none !important; } \ No newline at end of file +.hidden-print * { display:none !important; } + +.bulkActionButtons, +.action-toolbar { display: none !important; } \ No newline at end of file diff --git a/themes/bootstrap3/js/account_ajax.js b/themes/bootstrap3/js/account_ajax.js index 20d54942ea42780e0ce324edb27f108760e80073..b15ebd5ac49296330bdf13d9c66fee8bfadc591f 100644 --- a/themes/bootstrap3/js/account_ajax.js +++ b/themes/bootstrap3/js/account_ajax.js @@ -122,17 +122,8 @@ VuFind.register('account', function Account() { var init = function init() { // Update information when certain actions are performed - $("#renewals").submit(function clearCheckedOut() { - clearCache("checkedOut"); - }); - $('#cancelHold, [name="placeHold"]').submit(function clearHolds() { - clearCache("holds"); - }); - $('#ILLRequestForm, #cancelILLRequest').submit(function clearHolds() { - clearCache("illRequests"); - }); - $('[name="placeStorageRetrievalRequest"], #cancelStorageRetrievalRequest').submit(function clearStorageRetrievals() { - clearCache("storageRetrievalRequests"); + $("[data-clear-account-cache]").submit(function dataClearCache() { + clearCache($(this).attr("data-clear-account-cache")); }); $("#library_card").change(function clearChangeLibraryCard() { clearCache(/* all */); diff --git a/themes/bootstrap3/js/check_item_statuses.js b/themes/bootstrap3/js/check_item_statuses.js index 6942ce5137ebd7b046457a1721147a9c5cc7a18f..4b7c763f4476db76e42a1818a5b8ffa92c943de6 100644 --- a/themes/bootstrap3/js/check_item_statuses.js +++ b/themes/bootstrap3/js/check_item_statuses.js @@ -1,168 +1,197 @@ /*global Hunt, VuFind */ -/*exported checkItemStatuses, itemStatusFail */ -function linkCallnumbers(callnumber, callnumber_handler) { - if (callnumber_handler) { - var cns = callnumber.split(',\t'); - for (var i = 0; i < cns.length; i++) { - cns[i] = '<a href="' + VuFind.path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; +VuFind.register('itemStatuses', function ItemStatuses() { + function linkCallnumbers(callnumber, callnumber_handler) { + if (callnumber_handler) { + var cns = callnumber.split(',\t'); + for (var i = 0; i < cns.length; i++) { + cns[i] = '<a href="' + VuFind.path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; + } + return cns.join(',\t'); } - return cns.join(',\t'); + return callnumber; } - return callnumber; -} -function displayItemStatus(result, $item) { - $item.removeClass('js-item-pending'); - $item.find('.status').empty().append(result.availability_message); - $item.find('.ajax-availability').removeClass('ajax-availability hidden'); - if (typeof(result.error) != 'undefined' - && result.error.length > 0 - ) { - // Only show error message if we also have a status indicator active: - if ($item.find('.status').length > 0) { + function displayItemStatus(result, $item) { + $item.addClass('js-item-done').removeClass('js-item-pending'); + $item.find('.status').empty().append(result.availability_message); + $item.find('.ajax-availability').removeClass('ajax-availability hidden'); + if (typeof(result.error) != 'undefined' + && result.error.length > 0 + ) { $item.find('.callnumAndLocation').empty().addClass('text-danger').append(result.error); - } else { - $item.find('.callnumAndLocation').addClass('hidden'); - } - $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); - } else if (typeof(result.full_status) != 'undefined' - && result.full_status.length > 0 - && $item.find('.callnumAndLocation').length > 0 - ) { - // Full status mode is on -- display the HTML and hide extraneous junk: - $item.find('.callnumAndLocation').empty().append(result.full_status); - $item.find('.callnumber,.hideIfDetailed,.location,.status').addClass('hidden'); - } else if (typeof(result.missing_data) != 'undefined' - && result.missing_data - ) { - // No data is available -- hide the entire status area: - $item.find('.callnumAndLocation,.status').addClass('hidden'); - } else if (result.locationList) { - // We have multiple locations -- build appropriate HTML and hide unwanted labels: - $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); - var locationListHTML = ""; - for (var x = 0; x < result.locationList.length; x++) { - locationListHTML += '<div class="groupLocation">'; - if (result.locationList[x].availability) { - locationListHTML += '<span class="text-success"><i class="fa fa-ok" aria-hidden="true"></i> ' - + result.locationList[x].location + '</span> '; - } else if (typeof(result.locationList[x].status_unknown) !== 'undefined' - && result.locationList[x].status_unknown - ) { - if (result.locationList[x].location) { - locationListHTML += '<span class="text-warning"><i class="fa fa-status-unknown" aria-hidden="true"></i> ' - + result.locationList[x].location + '</span> '; + $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); + } else if (typeof(result.full_status) != 'undefined' + && result.full_status.length > 0 + && $item.find('.callnumAndLocation').length > 0 + ) { + // Full status mode is on -- display the HTML and hide extraneous junk: + $item.find('.callnumAndLocation').empty().append(result.full_status); + $item.find('.callnumber,.hideIfDetailed,.location,.status').addClass('hidden'); + } else if (typeof(result.missing_data) !== 'undefined' + && result.missing_data + ) { + // No data is available -- hide the entire status area: + $item.find('.callnumAndLocation,.status').addClass('hidden'); + } else if (result.locationList) { + // We have multiple locations -- build appropriate HTML and hide unwanted labels: + $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); + var locationListHTML = ""; + for (var x = 0; x < result.locationList.length; x++) { + locationListHTML += '<div class="groupLocation">'; + if (result.locationList[x].availability) { + locationListHTML += '<span class="text-success"><i class="fa fa-ok" aria-hidden="true"></i> ' + + result.locationList[x].location + '</span> '; + } else if (typeof(result.locationList[x].status_unknown) !== 'undefined' + && result.locationList[x].status_unknown + ) { + if (result.locationList[x].location) { + locationListHTML += '<span class="text-warning"><i class="fa fa-status-unknown" aria-hidden="true"></i> ' + + result.locationList[x].location + '</span> '; + } + } else { + locationListHTML += '<span class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> ' + + result.locationList[x].location + '</span> '; } - } else { - locationListHTML += '<span class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> ' - + result.locationList[x].location + '</span> '; + locationListHTML += '</div>'; + locationListHTML += '<div class="groupCallnumber">'; + locationListHTML += (result.locationList[x].callnumbers) + ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; + locationListHTML += '</div>'; } - locationListHTML += '</div>'; - locationListHTML += '<div class="groupCallnumber">'; - locationListHTML += (result.locationList[x].callnumbers) - ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; - locationListHTML += '</div>'; + $item.find('.locationDetails').removeClass('hidden'); + $item.find('.locationDetails').html(locationListHTML); + } else { + // Default case -- load call number and location into appropriate containers: + $item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler) + '<br/>'); + $item.find('.location').empty().append( + result.reserve === 'true' + ? result.reserve_message + : result.location + ); } - $item.find('.locationDetails').removeClass('hidden'); - $item.find('.locationDetails').html(locationListHTML); - } else { - // Default case -- load call number and location into appropriate containers: - $item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler) + '<br/>'); - $item.find('.location').empty().append( - result.reserve === 'true' - ? result.reserve_message - : result.location - ); - } -} -function itemStatusFail(response, textStatus) { - if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { - return; } - // display the error message on each of the ajax status place holder - $('.js-item-pending .callnumAndLocation').addClass('text-danger').empty().removeClass('hidden') - .append(typeof response.responseJSON.data === 'string' ? response.responseJSON.data : VuFind.translate('error_occurred')); -} - -var itemStatusIds = []; -var itemStatusEls = {}; -var itemStatusTimer = null; -var itemStatusDelay = 200; -var itemStatusRunning = false; -function runItemAjaxForQueue() { - // Only run one item status AJAX request at a time: - if (itemStatusRunning) { - itemStatusTimer = setTimeout(runItemAjaxForQueue, itemStatusDelay); - return; - } - itemStatusRunning = true; - $.ajax({ + var ItemStatusHandler = { + name: "default", + //array to hold IDs and elements + itemStatusIds: [], itemStatusEls: [], + url: '/AJAX/JSON?method=getItemStatuses', + itemStatusRunning: false, dataType: 'json', method: 'POST', - url: VuFind.path + '/AJAX/JSON?method=getItemStatuses', - data: { 'id': itemStatusIds } - }) - .done(function checkItemStatusDone(response) { - for (var j = 0; j < response.data.statuses.length; j++) { - var status = response.data.statuses[j]; - displayItemStatus(status, itemStatusEls[status.id]); - itemStatusIds.splice(itemStatusIds.indexOf(status.id), 1); + itemStatusTimer: null, + itemStatusDelay: 200, + + checkItemStatusDone: function checkItemStatusDone(response) { + var data = response.data; + for (var j = 0; j < data.statuses.length; j++) { + var status = data.statuses[j]; + displayItemStatus(status, this.itemStatusEls[status.id]); + this.itemStatusIds.splice(this.itemStatusIds.indexOf(status.id), 1); + } + }, + itemStatusFail: function itemStatusFail(response, textStatus) { + if (textStatus === 'error' || textStatus === 'abort' || typeof response.responseJSON === 'undefined') { + return; } - itemStatusRunning = false; - }) - .fail(function checkItemStatusFail(response, textStatus) { - itemStatusFail(response, textStatus); - itemStatusRunning = false; - }); -} + // display the error message on each of the ajax status place holder + $('.js-item-pending .callnumAndLocation').addClass('text-danger').empty().removeClass('hidden') + .append(typeof response.responseJSON.data === 'string' ? response.responseJSON.data : VuFind.translate('error_occurred')); + }, + itemQueueAjax: function itemQueueAjax(id, el) { + clearTimeout(this.itemStatusTimer); + this.itemStatusIds.push(id); + this.itemStatusEls[id] = el; + this.itemStatusTimer = setTimeout(this.runItemAjaxForQueue.bind(this), this.itemStatusDelay); + el.addClass('js-item-pending').removeClass('hidden'); + el.find('.callnumAndLocation').removeClass('hidden'); + el.find('.callnumAndLocation .ajax-availability').removeClass('hidden'); + el.find('.status').removeClass('hidden'); + }, -function itemQueueAjax(id, el) { - if (el.hasClass('js-item-pending')) { - return; - } - clearTimeout(itemStatusTimer); - itemStatusIds.push(id); - itemStatusEls[id] = el; - itemStatusTimer = setTimeout(runItemAjaxForQueue, itemStatusDelay); - el.addClass('js-item-pending').removeClass('hidden'); - el.find('.callnumAndLocation').removeClass('hidden'); - el.find('.callnumAndLocation .ajax-availability').removeClass('hidden'); - el.find('.status').removeClass('hidden'); -} + runItemAjaxForQueue: function runItemAjaxForQueue() { + if (this.itemStatusRunning) { + this.itemStatusTimer = setTimeout(this.runItemAjaxForQueue.bind(this), this.itemStatusDelay); + return; + } + $.ajax({ + dataType: this.dataType, + method: this.method, + url: VuFind.path + this.url, + context: this, + data: { 'id': this.itemStatusIds } + }) + .done(this.checkItemStatusDone) + .fail( this.itemStatusFail) + .always(function queueAjaxAlways() { + this.itemStatusRunning = false; + }); + }//end runItemAjax + }; -function checkItemStatus(el) { - var $item = $(el); - if ($item.find('.hiddenId').length === 0) { - return false; - } - var id = $item.find('.hiddenId').val(); - itemQueueAjax(id + '', $item); -} + //add you own overridden handler here + var OdItemStatusHandler = Object.create(ItemStatusHandler); + OdItemStatusHandler.url = '/Overdrive/getStatus'; + OdItemStatusHandler.itemStatusDelay = 200; + OdItemStatusHandler.name = "overdrive"; + OdItemStatusHandler.itemStatusIds = []; + OdItemStatusHandler.itemStatusEls = []; -var itemStatusObserver = null; -function checkItemStatuses(_container) { - var container = typeof _container === 'undefined' - ? document.body - : _container; + //store the handlers in a "hash" obj + var checkItemHandlers = { + 'ils': ItemStatusHandler, + 'overdrive': OdItemStatusHandler, + }; - var ajaxItems = $(container).find('.ajaxItem'); - for (var i = 0; i < ajaxItems.length; i++) { - var id = $(ajaxItems[i]).find('.hiddenId').val(); - itemQueueAjax(id, $(ajaxItems[i])); + function checkItemStatus(el) { + var $item = $(el); + if ($item.hasClass('js-item-pending') || $item.hasClass('js-item-done')) { + return; + } + if ($item.find('.hiddenId').length === 0) { + return false; + } + var id = $item.find('.hiddenId').val(); + var handlerName = 'ils'; + if ($item.find('.handler-name').length > 0) { + handlerName = $item.find('.handler-name').val(); + } + + //queue the element into the handler + checkItemHandlers[handlerName].itemQueueAjax(id, $item); } - // Stop looking for a scroll loader - if (itemStatusObserver) { - itemStatusObserver.disconnect(); + + function checkItemStatuses(_container) { + var container = typeof _container === 'undefined' + ? document.body + : _container; + + var ajaxItems = $(container).find('.ajaxItem'); + for (var i = 0; i < ajaxItems.length; i++) { + var id = $(ajaxItems[i]).find('.hiddenId').val(); + var handlerName = 'ils'; + if ($(ajaxItems[i]).find('.handler-name').length > 0) { + handlerName = $(ajaxItems[i]).find('.handler-name').val(); + } + if ($(ajaxItems[i]).data("handler-name")) { + handlerName = $(ajaxItems[i]).data("handler-name"); + } + checkItemHandlers[handlerName].itemQueueAjax(id, $(ajaxItems[i])); + } } -} -$(document).ready(function checkItemStatusReady() { - if (typeof Hunt === 'undefined') { - checkItemStatuses(); - } else { - itemStatusObserver = new Hunt( - $('.ajaxItem').toArray(), - { enter: checkItemStatus } - ); + function init(_container) { + if (typeof Hunt === 'undefined') { + checkItemStatuses(_container); + } else { + var container = typeof _container === 'undefined' + ? document.body + : _container; + new Hunt( + $(container).find('.ajaxItem').toArray(), + { enter: checkItemStatus } + ); + } } + + return { init: init, check: checkItemStatuses }; }); diff --git a/themes/bootstrap3/js/combined-search.js b/themes/bootstrap3/js/combined-search.js index dd4c75d9117eb51142b04e646f50ccf42edc8fba..e7f1ad913db75b264990a0acad55aaaa4fd13d2c 100644 --- a/themes/bootstrap3/js/combined-search.js +++ b/themes/bootstrap3/js/combined-search.js @@ -1,4 +1,4 @@ -/*global VuFind, checkItemStatuses, checkSaveStatuses */ +/*global VuFind, checkSaveStatuses */ VuFind.combinedSearch = (function CombinedSearch() { var init = function init(container, url) { container.load(url, '', function containerLoad(responseText) { @@ -6,7 +6,7 @@ VuFind.combinedSearch = (function CombinedSearch() { container.hide(); } else { VuFind.openurl.init(container); - checkItemStatuses(container); + VuFind.itemStatuses.init(container); checkSaveStatuses(container); } }); diff --git a/themes/bootstrap3/js/common.js b/themes/bootstrap3/js/common.js index 3d7d75276c35c88891f690bb4422989c1abf3e0c..4860cfd4266c29935022b6f03d740589ce9ed203 100644 --- a/themes/bootstrap3/js/common.js +++ b/themes/bootstrap3/js/common.js @@ -1,5 +1,5 @@ /*global grecaptcha, isPhoneNumberValid */ -/*exported VuFind, htmlEncode, deparam, moreFacets, lessFacets, getUrlRoot, phoneNumberFormHandler, recaptchaOnLoad, resetCaptcha, bulkFormHandler */ +/*exported VuFind, htmlEncode, deparam, moreFacets, lessFacets, getUrlRoot, phoneNumberFormHandler, recaptchaOnLoad, resetCaptcha, bulkFormHandler, setupMultiILSLoginFields */ // IE 9< console polyfill window.console = window.console || { log: function polyfillLog() {} }; @@ -163,13 +163,16 @@ function getUrlRoot(url) { var urlroot = null; var urlParts = url.split(/[?#]/); var urlWithoutFragment = urlParts[0]; - if (VuFind.path === '') { + var slashSlash = urlWithoutFragment.indexOf('//'); + if (VuFind.path === '' || VuFind.path === '/') { // special case -- VuFind installed at site root: var chunks = urlWithoutFragment.split('/'); - urlroot = '/' + chunks[3] + '/' + chunks[4]; + // We need to extract different offsets if this is a full vs. relative URL: + urlroot = slashSlash > -1 + ? ('/' + chunks[3] + '/' + chunks[4]) + : ('/' + chunks[1] + '/' + chunks[2]); } else { // standard case -- VuFind has its own path under site: - var slashSlash = urlWithoutFragment.indexOf('//'); var pathInUrl = slashSlash > -1 ? urlWithoutFragment.indexOf(VuFind.path, slashSlash + 2) : urlWithoutFragment.indexOf(VuFind.path); @@ -355,6 +358,26 @@ function setupJumpMenus(_container) { container.find('select.jumpMenu').change(function jumpMenu(){ $(this).parent('form').submit(); }); } +function setupMultiILSLoginFields(loginMethods, idPrefix) { + var searchPrefix = idPrefix ? '#' + idPrefix : '#'; + $(searchPrefix + 'target').change(function onChangeLoginTarget() { + var target = $(this).val(); + var $usernameGroup = $(searchPrefix + 'username').closest('.form-group'); + var $password = $(searchPrefix + 'password'); + if (loginMethods[target] === 'email') { + $usernameGroup.find('label.password-login').addClass('hidden'); + $usernameGroup.find('label.email-login').removeClass('hidden'); + $password.closest('.form-group').addClass('hidden'); + // Set password to a dummy value so that any checks for username+password work + $password.val('****'); + } else { + $usernameGroup.find('label.password-login').removeClass('hidden'); + $usernameGroup.find('label.email-login').addClass('hidden'); + $password.closest('.form-group').removeClass('hidden'); + } + }).change(); +} + $(document).ready(function commonDocReady() { // Start up all of our submodules VuFind.init(); @@ -408,21 +431,18 @@ $(document).ready(function commonDocReady() { if (url.indexOf('?' + 'print' + '=') !== -1 || url.indexOf('&' + 'print' + '=') !== -1) { $("link[media='print']").attr("media", "all"); $(document).ajaxStop(function triggerPrint() { - window.print(); + // Print dialogs cause problems during testing, so disable them + // when the "test mode" cookie is set. This should never happen + // under normal usage outside of the Phing startup process. + if (document.cookie.indexOf('VuFindTestSuiteRunning=') === -1) { + window.print(); + } else { + console.log("Printing disabled due to test mode."); + } }); // Make an ajax call to ensure that ajaxStop is triggered $.getJSON(VuFind.path + '/AJAX/JSON', {method: 'keepAlive'}); } - // retain filter sessionStorage - $('.searchFormKeepFilters').click(function retainFiltersInSessionStorage() { - sessionStorage.setItem('vufind_retain_filters', this.checked ? 'true' : 'false'); - $('.applied-filter').prop('checked', this.checked); - }); - if (sessionStorage.getItem('vufind_retain_filters')) { - var state = (sessionStorage.getItem('vufind_retain_filters') === 'true'); - $('.searchFormKeepFilters,.applied-filter').prop('checked', state); - } - setupIeSupport(); }); diff --git a/themes/bootstrap3/js/facets.js b/themes/bootstrap3/js/facets.js index 77829a5d1b66192efba03046878789aa41b1b29d..89740d655f879d75eefd14620efe9ce3b5b3cd74 100644 --- a/themes/bootstrap3/js/facets.js +++ b/themes/bootstrap3/js/facets.js @@ -26,8 +26,11 @@ function buildFacetNodes(data, currentPath, allowExclude, excludeTitle, counts) $i.appendTo($item); $item.append(' '); } + var $description = $('<span/>') + .addClass('facet-value') + .append(this.displayText); + $item.append($description); - $item.append(this.displayText); $item.appendTo($html); if (!this.isApplied && counts) { @@ -248,7 +251,7 @@ VuFind.register('sideFacets', function SideFacets() { $.support.transition = false; if ((' ' + storedItem + ' ').indexOf(' in ') > -1) { $(item).collapse('show'); - } else { + } else if (!$(item).data('forceIn')) { $(item).collapse('hide'); } } finally { @@ -266,6 +269,16 @@ VuFind.register('sideFacets', function SideFacets() { loadAjaxSideFacets(); }); loadAjaxSideFacets(); + + // Keep filter dropdowns on screen + $(".search-filter-dropdown").on("shown.bs.dropdown", function checkFilterDropdownWidth(e) { + var $dropdown = $(e.target).find(".dropdown-menu"); + if ($(e.target).position().left + $dropdown.width() >= window.innerWidth) { + $dropdown.addClass("dropdown-menu-right"); + } else { + $dropdown.removeClass("dropdown-menu-right"); + } + }); } return { init: init, showLoadingOverlay: showLoadingOverlay }; diff --git a/themes/bootstrap3/js/hierarchyTree.js b/themes/bootstrap3/js/hierarchyTree.js index bf0b82668c322c427f87b9c16a48026b780b9713..e8849ccdbcb170bd8c4a5a3c54fcb21b1b81f007 100644 --- a/themes/bootstrap3/js/hierarchyTree.js +++ b/themes/bootstrap3/js/hierarchyTree.js @@ -1,6 +1,6 @@ /*global VuFind */ -var hierarchyID, recordID, htmlID, hierarchyContext, hierarchySettings; +var hierarchyID, recordID, hierarchySource, htmlID, hierarchyContext, hierarchySettings; /* Utility functions */ function htmlEncodeId(id) { @@ -27,7 +27,7 @@ function html_entity_decode(string) { function getRecord(id) { $.ajax({ - url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id}), + url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id, hierarchySource: hierarchySource}), dataType: 'html' }) .done(function getRecordDone(response) { @@ -75,6 +75,7 @@ function doTreeSearch() { url: VuFind.path + '/Hierarchy/SearchTree?' + $.param({ lookfor: keyword, hierarchyID: hierarchyID, + hierarchySource: hierarchySource, type: $("#treeSearchType").val() }) + "&format=true" }) @@ -147,6 +148,7 @@ function buildTreeWithXml(cb) { hierarchyID: hierarchyID, id: recordID, context: hierarchyContext, + hierarchySource: hierarchySource, mode: 'Tree' } }) @@ -160,6 +162,9 @@ $(document).ready(function hierarchyTreeReady() { // Code for the search button hierarchyID = $("#hierarchyTree").find(".hiddenHierarchyId")[0].value; recordID = $("#hierarchyTree").find(".hiddenRecordId")[0].value; + hierarchySource = $("#hierarchyTree").find(".hiddenHierarchySource"); + hierarchySource = hierarchySource.length ? hierarchySource[0].value : 'Solr'; + htmlID = htmlEncodeId(recordID); hierarchyContext = $("#hierarchyTree").find(".hiddenContext")[0].value; var inLightbox = $("#hierarchyTree").parents("#modal").length > 0; @@ -219,7 +224,8 @@ $(document).ready(function hierarchyTreeReady() { url: VuFind.path + '/Hierarchy/GetTreeJSON', data: { hierarchyID: hierarchyID, - id: recordID + id: recordID, + hierarchySource: hierarchySource }, statusCode: { 200: function jsTree200Status(json /*, status, request*/) { diff --git a/themes/bootstrap3/js/lightbox.js b/themes/bootstrap3/js/lightbox.js index 4f324c9180886a15f5b06c9b183dd7a24a38bf72..24e5f57e6c366323928ccccfbff8e0c7de50fe54 100644 --- a/themes/bootstrap3/js/lightbox.js +++ b/themes/bootstrap3/js/lightbox.js @@ -1,4 +1,4 @@ -/*global grecaptcha, recaptchaOnLoad, resetCaptcha, VuFind */ +/*global recaptchaOnLoad, resetCaptcha, VuFind */ VuFind.register('lightbox', function Lightbox() { // State var _originalUrl = false; @@ -141,9 +141,11 @@ VuFind.register('lightbox', function Lightbox() { _xhr.always(function lbAjaxAlways() { _xhr = false; }) .done(function lbAjaxDone(content, status, jq_xhr) { var errorMsgs = []; + var flashMessages = []; if (jq_xhr.status !== 205) { var testDiv = $('<div/>').html(content); errorMsgs = testDiv.find('.flash-message.alert-danger:not([data-lightbox-ignore])'); + flashMessages = testDiv.find('.flash-message:not([data-lightbox-ignore])'); // Place Hold error isolation if (obj.url.match(/\/Record\/.*(Hold|Request)\?/)) { if (errorMsgs.length && testDiv.find('.record').length) { @@ -164,7 +166,7 @@ VuFind.register('lightbox', function Lightbox() { obj.method && ( obj.url.match(/catalogLogin/) || obj.url.match(/MyResearch\/(?!Bulk|Delete|Recover)/) - ) && errorMsgs.length === 0 + ) && flashMessages.length === 0 ) { var eventResult = _emit('VuFind.lightbox.login', { @@ -230,10 +232,16 @@ VuFind.register('lightbox', function Lightbox() { * data-lightbox-title = Lightbox title (overrides any title the page provides) */ _constrainLink = function constrainLink(event) { - if (typeof $(this).data('lightboxIgnore') != 'undefined' - || typeof this.attributes.href === 'undefined' - || this.attributes.href.value.charAt(0) === '#' - || this.href.match(/^[a-zA-Z]+:[^/]/) // ignore resource identifiers (mailto:, tel:, etc.) + var $link = $(this); + if (typeof $link.data("lightboxIgnore") != "undefined" + || typeof $link.attr("href") === "undefined" + || $link.attr("href").charAt(0) === "#" + || $link.attr("href").match(/^[a-zA-Z]+:[^/]/) // ignore resource identifiers (mailto:, tel:, etc.) + || (typeof $link.attr("target") !== "undefined" + && ( + $link.attr("target").toLowerCase() === "_new" + || $link.attr("target").toLowerCase() === "new" + )) ) { return true; } @@ -270,13 +278,6 @@ VuFind.register('lightbox', function Lightbox() { // Gather data var form = event.target; var data = $(form).serializeArray(); - // Check for recaptcha - if (typeof grecaptcha !== 'undefined') { - var recaptcha = $(form).find('.g-recaptcha'); - if (recaptcha.length > 0) { - data.push({ name: 'g-recaptcha-response', value: grecaptcha.getResponse(recaptcha.data('captchaId')) }); - } - } // Force layout data.push({ name: 'layout', value: 'lightbox' }); // Return in lightbox, please // Add submit button information diff --git a/themes/bootstrap3/js/record.js b/themes/bootstrap3/js/record.js index 542ba53f24a7c556c7d6eaf055775cca070ed3c6..b9cb2d4792cb9956c27103f539b97d220f2e2130 100644 --- a/themes/bootstrap3/js/record.js +++ b/themes/bootstrap3/js/record.js @@ -242,7 +242,7 @@ function backgroundLoadTab(tabid) { return; } var newTab = getNewRecordTab(tabid); - $('.nav-tabs a.' + tabid).closest('.result,.record').find('.tab-content').append(newTab); + $('[data-tab="' + tabid + '"]').closest('.result,.record').find('.tab-content').append(newTab); return ajaxLoadTab(newTab, tabid, false); } @@ -303,7 +303,7 @@ function recordDocReady() { }); $('[data-background]').each(function setupBackgroundTabs(index, el) { - backgroundLoadTab(el.className); + backgroundLoadTab(el.dataset.tab); }); registerTabEvents(); diff --git a/themes/bootstrap3/less/bootstrap.less b/themes/bootstrap3/less/bootstrap.less index b4c17ec5915f27b57acc151e765d3ad9d50f101c..8995a66076a46ec68fe85731e73a5dfe8d846530 100644 --- a/themes/bootstrap3/less/bootstrap.less +++ b/themes/bootstrap3/less/bootstrap.less @@ -225,18 +225,6 @@ footer { white-space: pre; } -/* ------ Facets ------ */ -.date-fields { - display: flex; - - .date-from { padding-right: .25rem; } - .date-to { padding-left: .25rem; } -} -@media (min-width: 768px) { - .author-facets { display: flex; } - .author-list { flex: 0 1 100%; } -} - /* ------ MyResearch ------ */ .form-login .btn-link ~ .btn-link { padding-left: 0; } // Login link spacing .myresearch-menu a { @@ -403,3 +391,8 @@ div.holding-notes { padding-left: 0.5rem; padding-right: 0.5rem; } + +.odbrand { + padding: 7px; +} + diff --git a/themes/bootstrap3/less/components/accessibility.less b/themes/bootstrap3/less/components/accessibility.less index 3fc07c552777af6778962dde5079c43de9e2fe69..2b5494ad786eac5d50895c9b1af53c1a0ab75cf7 100644 --- a/themes/bootstrap3/less/components/accessibility.less +++ b/themes/bootstrap3/less/components/accessibility.less @@ -24,3 +24,41 @@ a { color: @state-danger-text; } } + +/** + * OVERRIDE BS3 COLLAPSE MENU HIDDEN + * + * instead of display: none, keep things sr accessible + * https://tailwindcss.com/docs/screen-readers/ + */ +/* +.collapse.collapse:not(.in) { + position: absolute; + display: block; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + color: #000; + background-color: #fff; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.long-view.collapse:not(.in) { + display: none; +} +*/ +@media (min-width: 768px) { + .navbar-collapse.collapse:not(.in) { + position: static; + width: auto; + height: auto; + padding: 0; + margin: 0; + overflow: visible; + clip: auto; + white-space: normal; + } +} diff --git a/themes/bootstrap3/less/components/form.less b/themes/bootstrap3/less/components/form.less index 5a67b247fb004365778008ac06ddae6b3d05f2a9..a6ef56c3b99c29838aac1556a9e3b295e64aa655 100644 --- a/themes/bootstrap3/less/components/form.less +++ b/themes/bootstrap3/less/components/form.less @@ -15,7 +15,36 @@ form { margin-left: 15px; margin-top: 5px; } - .form-group label.required::before { + .form-group label.required::before, + .form-group .radio-label.required::before, + { content: '* '; } + + input[type=checkbox], + input[type=radio] { + height: 1em; + } + + .form-group.checkbox, + .form-group.radio { + margin-bottom: 15px; + label.control-label { + padding-left: 0; + font-weight: 700; + margin-bottom: 5px; + } + label { + display: table; + margin: 7px 0; + input { + width: auto; + margin-right: 10px; + margin-top: 3px; + } + } + .radio-label { + font-weight: 700; + } + } } diff --git a/themes/bootstrap3/less/components/icons.less b/themes/bootstrap3/less/components/icons.less index 751dfbcd26d889176ecb36577a05820cb45cd6a8..58f1470337091165cece08d6dab7fb953b949cfe 100644 --- a/themes/bootstrap3/less/components/icons.less +++ b/themes/bootstrap3/less/components/icons.less @@ -1,3 +1,24 @@ +/** + * Toolbar Button Icons + */ +[class*="btn-type-"]::before { + display: inline-block; + margin-right: .25rem; + font-family: "FontAwesome"; + color: inherit; +} +.btn-type-add::before { content: "\f055"; } // .fa-plus-circle +.btn-type-cite::before { content: "\f069"; } // .fa-asterisk +.btn-type-delete::before { content: "\f1f8"; } // .fa-trash +.btn-type-email::before { content: "\f0e0"; } // .fa-envelope +.btn-type-empty::before { content: "\f00d"; } // .fa-times +.btn-type-export::before { content: "\f08e"; } // .fa-external-link +.btn-type-minus::before { content: "\f056"; } // .fa-minus-circle +.btn-type-phone::before { content: "\f10b"; } // .fa-mobile +.btn-type-print::before { content: "\f02f"; } // .fa-print +.btn-type-save::before { content: "\f005"; } // .fa-star + + /** * Assign catalog formats to Font Awesome icons */ diff --git a/themes/bootstrap3/less/components/js-tree.less b/themes/bootstrap3/less/components/js-tree.less index 7f4c55daa7dede3a8d1390dea70c52b0ddf9626d..c57ea983cb0bffd9e1769c9bdac69027fb6a62f8 100644 --- a/themes/bootstrap3/less/components/js-tree.less +++ b/themes/bootstrap3/less/components/js-tree.less @@ -76,6 +76,16 @@ color: #000; } +.collection-hierarchytree { + .jstree-clicked { + color: @list-group-active-color; + background-color: @list-group-active-bg; + .jstree-icon { + color: #fff; + } + } +} + /* --- Facets --- */ .facet .jstree-ocl:before { float: left; diff --git a/themes/bootstrap3/less/components/record.less b/themes/bootstrap3/less/components/record.less index 370f62c40c8c5f63f2eecccf77310f96a988005f..a6c4d247f27da67706ba06310cc272b1614c538a 100644 --- a/themes/bootstrap3/less/components/record.less +++ b/themes/bootstrap3/less/components/record.less @@ -132,3 +132,41 @@ /* ------ Relais ------ */ .relaisLink { display: inline-block; } + +/* ------ Collection ------ */ +.collection-list-controls { + display: flex; + flex-flow: row wrap; + + .collection-control { + white-space: nowrap; + margin: 0 0.5rem 0 0; + } +} +.collectionDetails .active-filters .filters { + padding: 0 0 5px 0; +} +.collection-list-results { + margin-top: 0.5rem; +} + +/* ------ Tabs ------ */ +.tab-pane::after { + display: table; + clear: both; + content: ""; +} +.tab-pane .result { + margin-left: 0; +} + +/* ------ OpenURL Links ------ */ +.openurls { + .openurl-notes { + display: block; + font-style: italic; + } + .openurl-authentication { + display: block; + } +} \ No newline at end of file diff --git a/themes/bootstrap3/less/components/search.less b/themes/bootstrap3/less/components/search.less index 53cfc094de839024d1d84b392f52b1d69e450df5..9d4ea494cf47aa85c718acbb1a3b7431749f3887 100644 --- a/themes/bootstrap3/less/components/search.less +++ b/themes/bootstrap3/less/components/search.less @@ -5,7 +5,6 @@ // Hide these on mobile @media (max-width: 767px) { - .bulkActionButtons, #datevispublishDatexWrapper, .result .checkbox { display: none; } } @@ -33,8 +32,8 @@ header .container.navbar { margin-bottom: 0; } -.search-sort { display: flex; } -.search-sort label { +.search-sort, .search-result-limit { display: flex; } +.search-sort label, .search-result-limit label { line-height: @input-height-base; padding-right: .5rem; } @@ -50,10 +49,26 @@ header .container.navbar { margin-bottom: 0; } .search-controls { text-align: right; } } +.record-nav, +.bulkActionButtons { + .clearfix(); + margin: 1rem 0; + padding: 0; +} .bulk-checkbox, .bulkActionButtons label { display: inline-block; } .bulkActionButtons label input { margin-top: 2px; } -.bulkActionButtons { .clearfix(); } + +.action-toolbar { + display: inline-block; + margin: 0; + padding: 0; + list-style: none; +} +.action-toolbar li { + display: inline-block; +} + @media (max-width: 767px) { .grid { min-height: 250px; } } @@ -327,3 +342,170 @@ body.rtl { height: auto !important; } } + +/* ------ CURRENT FILTERS ------ */ +.active-filters { + clear: both; + display: flex; + justify-content: flex-start; + align-items: baseline; + .retain-filters { + float: right; + } + .checkbox { + flex: 1; + white-space: nowrap; + } + .filters-toggle { + flex: 1; + display: inline-block; + margin-top: 10px; + cursor: pointer; + &::after { + content: '\25B2'; + } + &.collapsed::after { + content: '\25BC'; + } + @media (min-width: @screen-sm-min) { + float: left; + } + } + .filters { + flex: 10 1 auto; + padding: 5px 0 5px 10px; + .collapse { + display: none; + .in { + display: inline-block; + } + } + + .checkbox label { + display: inline-block; + padding: 10px 0px; + @media (max-width: @screen-sm-min) { + float: right; + padding: 0px; + } + } + .filters-term { + font-size: 0.8em + } + .filter-value { + padding: 2px 8px; + margin: 5px; + font-weight: 600; + color: #fff; + background: @list-group-active-bg; + white-space: nowrap; + display: inline-block; + } + .checkbox { + display: inline; + padding-left: 0; + padding-right: 0; + @media (min-width: @screen-sm-min) { + padding-right: 20px; + } + } + .title-value-pair { + display: inline-block; + } + } +} + +.toolbar-btn, +.record-nav .cart-add, +.record-nav .cart-remove, +.reset-filters-btn { + display: inline-block; + margin-bottom: 4px; + padding: .5rem 1rem; + border: 0; + color: #555; + background-color: rgba(0,0,0,.05); + border-radius: 4px; + + &:hover { + background-color: rgba(0,0,0,.1); + } +} +.toolbar-btn:hover, +.record-nav .cart-add:hover, +.record-nav .cart-remove:hover, +.reset-filters-btn:hover { + color: #555; + text-decoration: none; +} +.search-filter-remove { + margin: 4px 1px 4px 4px; + font-weight: normal; + color: #fff; +} +.active-filters .search-filter-remove:hover { + color: red; +} +.search-filter-remove::after { + content: "\f2d3"; /* fa-window-close */ + font-family: "FontAwesome"; +} +.search-filter-dropdown { + display: inline-block; + .btn { + color: #fff; + background: @list-group-active-bg; + vertical-align: baseline; + } + .btn::after { + content: "\f107"; + font-family: "FontAwesome"; + } + + .dropdown-menu { + .filters-term { + display: block; + text-align: center; + } + .filter-value { + display: flex; + + .text { + flex-grow: 1; + margin-right: 0.5em; + } + a { + flex-shrink: 0; + margin: 0; + } + } + } +} + +.search-schedule-header { + text-decoration: underline; + text-decoration-style: dotted; + cursor: pointer; +} + +.search-history-table { + &:extend(.table all); + &:extend(.table-striped all); +} +table.search-history-table { + table-layout: auto; + + tbody > tr > td { vertical-align: middle; } +} + +/* Break early to prevent table overflow */ +@media (max-width: 991px) { + .template-dir-search.template-name-history { + .mainbody, + .sidebar { + float: none; + width: auto; + padding: 0; + } + } +} diff --git a/themes/bootstrap3/less/components/sidebar.less b/themes/bootstrap3/less/components/sidebar.less index b27cedde1e06a5a932f04bf796871bbd5722989f..3f4944d1428f6067140a8747aea3448d526d1d2d 100644 --- a/themes/bootstrap3/less/components/sidebar.less +++ b/themes/bootstrap3/less/components/sidebar.less @@ -165,3 +165,19 @@ body.rtl .full-facet-list .active .fa.fa-times { float: left; } border-bottom: 1px solid @list-group-border; } } + + +.facet-range-form { + width: 100%; +} +.date-fields { + display: flex; + + .date-from, .date-to { flex: 0 1 100%; } + .date-from { padding-right: .25rem; } + .date-to { padding-left: .25rem; } +} +@media (min-width: 768px) { + .author-facets { display: flex; } + .author-list { flex: 0 1 100%; } +} diff --git a/themes/bootstrap3/scss/bootstrap.scss b/themes/bootstrap3/scss/bootstrap.scss index 3b1ccd9bdbc096829e3dd854add7783fcd8b94f5..7c0c8a17aa7851f43de78440af56dfe19c3aef42 100644 --- a/themes/bootstrap3/scss/bootstrap.scss +++ b/themes/bootstrap3/scss/bootstrap.scss @@ -225,18 +225,6 @@ footer { white-space: pre; } -/* ------ Facets ------ */ -.date-fields { - display: flex; - - .date-from { padding-right: .25rem; } - .date-to { padding-left: .25rem; } -} -@media (min-width: 768px) { - .author-facets { display: flex; } - .author-list { flex: 0 1 100%; } -} - /* ------ MyResearch ------ */ .form-login .btn-link ~ .btn-link { padding-left: 0; } // Login link spacing .myresearch-menu a { @@ -403,3 +391,8 @@ div.holding-notes { padding-left: 0.5rem; padding-right: 0.5rem; } + +.odbrand { + padding: 7px; +} + diff --git a/themes/bootstrap3/scss/components/accessibility.scss b/themes/bootstrap3/scss/components/accessibility.scss index a987269a49d2b6b67d5b32480ed54e4664283f8b..2664115977b23fa776981cd38608e08f18012902 100644 --- a/themes/bootstrap3/scss/components/accessibility.scss +++ b/themes/bootstrap3/scss/components/accessibility.scss @@ -1,26 +1,68 @@ -@import "../vendor/bootstrap-accessibility/bootstrap-accessibility"; -@import "../vendor/a11y"; +@import '../vendor/bootstrap-accessibility/bootstrap-accessibility'; +@import '../vendor/a11y'; -/* ACCESSIBILITY IMPROVEMENTS */ +// ACCESSIBILITY IMPROVEMENTS a { text-decoration: underline; } -/* AAA Color Contrasts */ -$badge-bg: #595959; +// AAA Color Contrasts +$badge-bg: #595959 !default; -$breadcrumb-color: #535353; -$breadcrumb-active-color: #444; +$breadcrumb-color: #535353 !default; +$breadcrumb-active-color: #444 !default; + +$state-danger-text: #8a211e !default; -$state-danger-text: #8a211e; .alert-danger, .alert-danger a { color: $state-danger-text; + .btn-danger { - color: white; + color: #fff; } + .btn-danger:hover { color: $state-danger-text; } } + +/** + * OVERRIDE BS3 COLLAPSE MENU HIDDEN + * + * instead of display: none, keep things sr accessible + * https://tailwindcss.com/docs/screen-readers/ + */ +/* +.collapse.collapse:not(.in) { + position: absolute; + display: block; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + color: #000; + background-color: #fff; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +.long-view.collapse:not(.in) { + display: none; +} +*/ + +@media (min-width: 768px) { + .navbar-collapse.collapse:not(.in) { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + padding: 0; + position: static; + white-space: normal; + width: auto; + } +} diff --git a/themes/bootstrap3/scss/components/form.scss b/themes/bootstrap3/scss/components/form.scss index 5a67b247fb004365778008ac06ddae6b3d05f2a9..a6ef56c3b99c29838aac1556a9e3b295e64aa655 100644 --- a/themes/bootstrap3/scss/components/form.scss +++ b/themes/bootstrap3/scss/components/form.scss @@ -15,7 +15,36 @@ form { margin-left: 15px; margin-top: 5px; } - .form-group label.required::before { + .form-group label.required::before, + .form-group .radio-label.required::before, + { content: '* '; } + + input[type=checkbox], + input[type=radio] { + height: 1em; + } + + .form-group.checkbox, + .form-group.radio { + margin-bottom: 15px; + label.control-label { + padding-left: 0; + font-weight: 700; + margin-bottom: 5px; + } + label { + display: table; + margin: 7px 0; + input { + width: auto; + margin-right: 10px; + margin-top: 3px; + } + } + .radio-label { + font-weight: 700; + } + } } diff --git a/themes/bootstrap3/scss/components/icons.scss b/themes/bootstrap3/scss/components/icons.scss index 751dfbcd26d889176ecb36577a05820cb45cd6a8..58f1470337091165cece08d6dab7fb953b949cfe 100644 --- a/themes/bootstrap3/scss/components/icons.scss +++ b/themes/bootstrap3/scss/components/icons.scss @@ -1,3 +1,24 @@ +/** + * Toolbar Button Icons + */ +[class*="btn-type-"]::before { + display: inline-block; + margin-right: .25rem; + font-family: "FontAwesome"; + color: inherit; +} +.btn-type-add::before { content: "\f055"; } // .fa-plus-circle +.btn-type-cite::before { content: "\f069"; } // .fa-asterisk +.btn-type-delete::before { content: "\f1f8"; } // .fa-trash +.btn-type-email::before { content: "\f0e0"; } // .fa-envelope +.btn-type-empty::before { content: "\f00d"; } // .fa-times +.btn-type-export::before { content: "\f08e"; } // .fa-external-link +.btn-type-minus::before { content: "\f056"; } // .fa-minus-circle +.btn-type-phone::before { content: "\f10b"; } // .fa-mobile +.btn-type-print::before { content: "\f02f"; } // .fa-print +.btn-type-save::before { content: "\f005"; } // .fa-star + + /** * Assign catalog formats to Font Awesome icons */ diff --git a/themes/bootstrap3/scss/components/js-tree.scss b/themes/bootstrap3/scss/components/js-tree.scss index 03b7358f2bfecbcf99599e8d07e66d89b064fcf9..bc734b254286fa35cb177c0d93897bac977b879c 100644 --- a/themes/bootstrap3/scss/components/js-tree.scss +++ b/themes/bootstrap3/scss/components/js-tree.scss @@ -76,6 +76,16 @@ color: #000; } +.collection-hierarchytree { + .jstree-clicked { + color: $list-group-active-color; + background-color: $list-group-active-bg; + .jstree-icon { + color: #fff; + } + } +} + /* --- Facets --- */ .facet .jstree-ocl:before { float: left; diff --git a/themes/bootstrap3/scss/components/record.scss b/themes/bootstrap3/scss/components/record.scss index 7ff7fd966d834a75b9127fccfac8035d36dc1b7c..3d08854276555f702850e374f84d2ae18dda7b01 100644 --- a/themes/bootstrap3/scss/components/record.scss +++ b/themes/bootstrap3/scss/components/record.scss @@ -132,3 +132,41 @@ /* ------ Relais ------ */ .relaisLink { display: inline-block; } + +/* ------ Collection ------ */ +.collection-list-controls { + display: flex; + flex-flow: row wrap; + + .collection-control { + white-space: nowrap; + margin: 0 0.5rem 0 0; + } +} +.collectionDetails .active-filters .filters { + padding: 0 0 5px 0; +} +.collection-list-results { + margin-top: 0.5rem; +} + +/* ------ Tabs ------ */ +.tab-pane::after { + display: table; + clear: both; + content: ""; +} +.tab-pane .result { + margin-left: 0; +} + +/* ------ OpenURL Links ------ */ +.openurls { + .openurl-notes { + display: block; + font-style: italic; + } + .openurl-authentication { + display: block; + } +} \ No newline at end of file diff --git a/themes/bootstrap3/scss/components/search.scss b/themes/bootstrap3/scss/components/search.scss index cb9dc7aa975a3fd3619ececec78e83a0b31f9ecf..33da24a21a268310ff41d81ff51c3d37d5c29e4f 100644 --- a/themes/bootstrap3/scss/components/search.scss +++ b/themes/bootstrap3/scss/components/search.scss @@ -5,7 +5,6 @@ $thumbnail-width-large: 160px; // Hide these on mobile @media (max-width: 767px) { - .bulkActionButtons, #datevispublishDatexWrapper, .result .checkbox { display: none; } } @@ -33,8 +32,8 @@ $thumbnail-width-large: 160px; header .container.navbar { margin-bottom: 0; } -.search-sort { display: flex; } -.search-sort label { +.search-sort, .search-result-limit { display: flex; } +.search-sort label, .search-result-limit label { line-height: $input-height-base; padding-right: .5rem; } @@ -50,10 +49,26 @@ header .container.navbar { margin-bottom: 0; } .search-controls { text-align: right; } } +.record-nav, +.bulkActionButtons { + @include clearfix(); + margin: 1rem 0; + padding: 0; +} .bulk-checkbox, .bulkActionButtons label { display: inline-block; } .bulkActionButtons label input { margin-top: 2px; } -.bulkActionButtons { @include clearfix(); } + +.action-toolbar { + display: inline-block; + margin: 0; + padding: 0; + list-style: none; +} +.action-toolbar li { + display: inline-block; +} + @media (max-width: 767px) { .grid { min-height: 250px; } } @@ -327,3 +342,170 @@ body.rtl { height: auto !important; } } + +/* ------ CURRENT FILTERS ------ */ +.active-filters { + clear: both; + display: flex; + justify-content: flex-start; + align-items: baseline; + .retain-filters { + float: right; + } + .checkbox { + flex: 1; + white-space: nowrap; + } + .filters-toggle { + flex: 1; + display: inline-block; + margin-top: 10px; + cursor: pointer; + &::after { + content: '\25B2'; + } + &.collapsed::after { + content: '\25BC'; + } + @media (min-width: $screen-sm-min) { + float: left; + } + } + .filters { + flex: 10 1 auto; + padding: 5px 0 5px 10px; + .collapse { + display: none; + .in { + display: inline-block; + } + } + + .checkbox label { + display: inline-block; + padding: 10px 0px; + @media (max-width: $screen-sm-min) { + float: right; + padding: 0px; + } + } + .filters-term { + font-size: 0.8em + } + .filter-value { + padding: 2px 8px; + margin: 5px; + font-weight: 600; + color: #fff; + background: $list-group-active-bg; + white-space: nowrap; + display: inline-block; + } + .checkbox { + display: inline; + padding-left: 0; + padding-right: 0; + @media (min-width: $screen-sm-min) { + padding-right: 20px; + } + } + .title-value-pair { + display: inline-block; + } + } +} + +.toolbar-btn, +.record-nav .cart-add, +.record-nav .cart-remove, +.reset-filters-btn { + display: inline-block; + margin-bottom: 4px; + padding: .5rem 1rem; + border: 0; + color: #555; + background-color: rgba(0,0,0,.05); + border-radius: 4px; + + &:hover { + background-color: rgba(0,0,0,.1); + } +} +.toolbar-btn:hover, +.record-nav .cart-add:hover, +.record-nav .cart-remove:hover, +.reset-filters-btn:hover { + color: #555; + text-decoration: none; +} +.search-filter-remove { + margin: 4px 1px 4px 4px; + font-weight: normal; + color: #fff; +} +.active-filters .search-filter-remove:hover { + color: red; +} +.search-filter-remove::after { + content: "\f2d3"; /* fa-window-close */ + font-family: "FontAwesome"; +} +.search-filter-dropdown { + display: inline-block; + .btn { + color: #fff; + background: $list-group-active-bg; + vertical-align: baseline; + } + .btn::after { + content: "\f107"; + font-family: "FontAwesome"; + } + + .dropdown-menu { + .filters-term { + display: block; + text-align: center; + } + .filter-value { + display: flex; + + .text { + flex-grow: 1; + margin-right: 0.5em; + } + a { + flex-shrink: 0; + margin: 0; + } + } + } +} + +.search-schedule-header { + text-decoration: underline; + text-decoration-style: dotted; + cursor: pointer; +} + +.search-history-table { + @extend .table; + @extend .table-striped; +} +table.search-history-table { + table-layout: auto; + + tbody > tr > td { vertical-align: middle; } +} + +/* Break early to prevent table overflow */ +@media (max-width: 991px) { + .template-dir-search.template-name-history { + .mainbody, + .sidebar { + float: none; + width: auto; + padding: 0; + } + } +} diff --git a/themes/bootstrap3/scss/components/sidebar.scss b/themes/bootstrap3/scss/components/sidebar.scss index 163c15744cdcc981f12ef30311641f1be0b30177..85a4fd9343f2afca2d130076477496b57daac66f 100644 --- a/themes/bootstrap3/scss/components/sidebar.scss +++ b/themes/bootstrap3/scss/components/sidebar.scss @@ -165,3 +165,19 @@ body.rtl .full-facet-list .active .fa.fa-times { float: left; } border-bottom: 1px solid $list-group-border; } } + + +.facet-range-form { + width: 100%; +} +.date-fields { + display: flex; + + .date-from, .date-to { flex: 0 1 100%; } + .date-from { padding-right: .25rem; } + .date-to { padding-left: .25rem; } +} +@media (min-width: 768px) { + .author-facets { display: flex; } + .author-list { flex: 0 1 100%; } +} diff --git a/themes/bootstrap3/templates/Auth/Email/loginfields.phtml b/themes/bootstrap3/templates/Auth/Email/loginfields.phtml new file mode 100644 index 0000000000000000000000000000000000000000..20b9986ebaa0c096bdc90dc8f239048423aa91a3 --- /dev/null +++ b/themes/bootstrap3/templates/Auth/Email/loginfields.phtml @@ -0,0 +1,4 @@ +<div class="form-group"> + <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Email')?>:</label> + <input type="text" name="username" id="login_<?=$this->escapeHtmlAttr($topClass)?>_username" value="<?=$this->escapeHtmlAttr($this->request->get('username'))?>" class="form-control"/> +</div> diff --git a/themes/bootstrap3/templates/Auth/ILS/loginfields.phtml b/themes/bootstrap3/templates/Auth/ILS/loginfields.phtml new file mode 100644 index 0000000000000000000000000000000000000000..e8e0fb9c6164d3fbb0b78eb40c1bed1000e5d7d4 --- /dev/null +++ b/themes/bootstrap3/templates/Auth/ILS/loginfields.phtml @@ -0,0 +1,16 @@ +<?php $loginMethod = $this->auth()->getManager()->getILSLoginMethod(); ?> +<?php if ('email' === $loginMethod): ?> + <div class="form-group"> + <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Email')?>:</label> + <input type="text" name="username" id="login_<?=$this->escapeHtmlAttr($topClass)?>_username" value="<?=$this->escapeHtmlAttr($this->request->get('username'))?>" class="form-control"/> + </div> +<?php else: ?> + <div class="form-group"> + <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Username')?>:</label> + <input type="text" name="username" id="login_<?=$this->escapeHtmlAttr($topClass)?>_username" value="<?=$this->escapeHtmlAttr($this->request->get('username'))?>" class="form-control"/> + </div> + <div class="form-group"> + <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_password"><?=$this->transEsc('Password')?>:</label> + <input type="password" name="password" id="login_<?=$this->escapeHtmlAttr($topClass)?>_password" class="form-control"/> + </div> +<?php endif; ?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml b/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml index e7136a43d327f4895a8fda87acd5db459f83be33..a7b253261bd7c09bedf9c2f15c40c4c0e82e0b95 100644 --- a/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml +++ b/themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml @@ -1,17 +1,32 @@ +<?php $loginTargets = $this->auth()->getManager()->getLoginTargets(); ?> <div class="form-group"> <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> <?php $currentTarget = $this->request->get('target'); if (!$currentTarget) $currentTarget = $this->auth()->getManager()->getDefaultLoginTarget();?> - <select id="login_target" name="target" class="form-control"> - <?php foreach ($this->auth()->getManager()->getLoginTargets() as $target):?> + <select id="login_<?=$this->escapeHtmlAttr($topClass)?>_target" name="target" class="form-control"> + <?php foreach ($loginTargets as $target):?> <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target == $currentTarget ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> <?php endforeach ?> </select> </div> <div class="form-group"> - <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Username')?>:</label> + <label class="control-label password-login" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Username')?>:</label> + <label class="control-label email-login hidden" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Email')?>:</label> <input id="login_<?=$this->escapeHtmlAttr($topClass)?>_username" type="text" name="username" value="<?=$this->escapeHtmlAttr($this->request->get('username'))?>" class="form-control"/> </div> <div class="form-group"> <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_password"><?=$this->transEsc('Password')?>:</label> <input id="login_<?=$this->escapeHtmlAttr($topClass)?>_password" type="password" name="password" class="form-control"/> </div> + +<?php + $methods = []; + $authManager = $this->auth()->getManager(); + foreach ($loginTargets as $target) { + $methods[$target] = $authManager->getILSLoginMethod($target); + } + $methods = json_encode($methods); + $script = "setupMultiILSLoginFields($methods, 'login_{$topClass}_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); +?> diff --git a/themes/bootstrap3/templates/Auth/PasswordAccess/loginfields.phtml b/themes/bootstrap3/templates/Auth/PasswordAccess/loginfields.phtml new file mode 100644 index 0000000000000000000000000000000000000000..a3de4e613cbda65c6d964b9a3ebf35bfbcfcd6c1 --- /dev/null +++ b/themes/bootstrap3/templates/Auth/PasswordAccess/loginfields.phtml @@ -0,0 +1,4 @@ +<div class="form-group"> + <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_password"><?=$this->transEsc('Password')?>:</label> + <input type="password" name="password" id="login_<?=$this->escapeHtmlAttr($topClass)?>_password" class="form-control"/> +</div> diff --git a/themes/bootstrap3/templates/ContentBlock/FacetList.phtml b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml index fe60f9bd34ade42cf3068350233d13b126af4bd4..a889fb248a746523a5243eaa381bb42bb2610577 100644 --- a/themes/bootstrap3/templates/ContentBlock/FacetList.phtml +++ b/themes/bootstrap3/templates/ContentBlock/FacetList.phtml @@ -11,7 +11,7 @@ <?php if ($isHierarchy = in_array($field, $hierarchicalFacets ?? [])): $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); $this->headScript()->appendFile('facets.js'); - $sort = $hierarchicalFacetSortOptions[$field] ?? ''; + $sort = $hierarchicalFacetSortOptions[$field] ?? $hierarchicalFacetSortOptions['*'] ?? 'all'; $script = <<<JS $(document).ready(function() { $('#facet_{$this->escapeHtml($field)}_container').removeClass('hide'); @@ -29,7 +29,7 @@ JS; data-exclude="0" data-operator="AND" data-exclude-title="<?=$this->transEsc('exclude_facet')?>" - data-sort="all"> + data-sort="<?=$sort?>"> </div> </div> <noscript> diff --git a/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml b/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml index 9a74b529767e1c665ab44e50e981cb507e25a696..845877df7ae90ab86ce979ddac56f6d438f320ba 100644 --- a/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml +++ b/themes/bootstrap3/templates/Recommend/AuthorityRecommend.phtml @@ -14,7 +14,7 @@ ?> <?php if (!empty($content)): ?> <div class="authoritybox"> - <div><strong><?=$this->transEsc('See also')?>:</strong></div> + <div><strong><?=$this->transEsc($this->recommend->getHeader())?>:</strong></div> <div><?=$content?></div> </div> <?php endif; ?> diff --git a/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml b/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml index a0fc65e58334db347ba36ae5c728353b18bbf57e..41bcc4f0abcdb878947e1114f3274913fe0dad86 100644 --- a/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/CollectionSideFacets.phtml @@ -1,43 +1,5 @@ <?php - $this->overrideSideFacetCaption = 'In This Collection'; + $this->overrideSideFacetCaption = 'Filter Collection'; + $this->baseUriExtra = $this->recommend->getResults()->getParams()->getCollectionId(); ?> -<?php if ($this->recommend->keywordFilterEnabled()): ?> - <?php - $keywordFilter = $this->recommend->getKeywordFilter(); - if (!empty($keywordFilter)) { - $this->extraSideFacetFilters = [ - 'Keyword' => [ - [ - 'value' => $keywordFilter, - 'displayText' => $keywordFilter, - 'specialType' => 'keyword', - 'operator' => 'OR' - ] - ] - ]; - } - ?> - <?php ob_start() ?> - <div class="panel panel-default"> - <div class="panel-heading"> - <h4 class="panel-title"> - <?=$this->transEsc('Keyword Filter')?> - </h4> - </div> - <div class="panel-body"> - <form class="form-inline" role="form" method="get" name="keywordFilterForm" id="keywordFilterForm"> - <input id="keywordFilter_lookfor" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($keywordFilter)?>" class="form-control"/> - <?php foreach ($this->recommend->getResults()->getParams()->getFilterList(true) as $field => $filters): ?> - <?php foreach ($filters as $filter): ?> - <input type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($filter['field'])?>:"<?=$this->escapeHtmlAttr($filter['value'])?>"" /> - <?php endforeach; ?> - <?php endforeach; ?> - <input class="btn btn-default" type="submit" name="submit" value="<?=$this->transEsc('Set')?>"/> - </form> - </div> - </div> - <?php $this->sideFacetExtraControls = ob_get_contents(); ?> - <?php ob_end_clean(); ?> -<?php endif; ?> -<?php $this->baseUriExtra = $this->recommend->getResults()->getParams()->getCollectionId(); ?> <?=$this->render('Recommend/SideFacets.phtml')?> diff --git a/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml b/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4d78777719321213f5cde3bea0f82321570e1a8e --- /dev/null +++ b/themes/bootstrap3/templates/Recommend/ExternalSearch.phtml @@ -0,0 +1,3 @@ +<div class="alert alert-info"> + <a href="<?=$this->recommend->getUrl()?>"><?=$this->transEsc($this->recommend->getLinkText())?></a> +</div> diff --git a/themes/bootstrap3/templates/Recommend/RecommendLinks.phtml b/themes/bootstrap3/templates/Recommend/RecommendLinks.phtml new file mode 100644 index 0000000000000000000000000000000000000000..76c40526c70b1e79cd13de03c86d59b4a5fd2795 --- /dev/null +++ b/themes/bootstrap3/templates/Recommend/RecommendLinks.phtml @@ -0,0 +1,13 @@ +<?php $links = $this->recommend->getLinks(); ?> +<?php if (count($links) > 0) :?> + <div class="alert alert-info"> + <?=$this->transEsc('recommend_links_text')?> + <ul> + <?php foreach($links as $title => $href): ?> + <li> + <a href="<?=$this->escapeHtmlAttr($href)?>"><?=$this->transEsc($title)?></a> + </li> + <?php endforeach; ?> + </ul> + </div> +<?php endif;?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets.phtml b/themes/bootstrap3/templates/Recommend/SideFacets.phtml index 9e3e5fdad05eac7c9d44f0953b5ff902c5a95822..7817bff86e9813efe3e554337910c0ecaf79a32b 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets.phtml @@ -4,6 +4,20 @@ // Save results/options to $this so they are available to sub-templates: $this->results = $results = $this->recommend->getResults(); $this->options = $options = $results->getOptions(); + $collapsedFacets = $this->recommend->getCollapsedFacets(); + $forceUncollapsedFacets = []; + + // Make sure facets with active selections are not collapsed: + $filterList = $results->getParams()->getFilterList(true); + foreach ($filterList as $field => $filters) { + foreach ($filters as $filter) { + $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false; + if ($index !== false) { + unset($collapsedFacets[$index]); // Open if we have a match + $forceUncollapsedFacets[] = $filter['field']; + } + } + } $hierarchicalFacets = $this->recommend->getHierarchicalFacets(); if ($hierarchicalFacets) { @@ -16,11 +30,11 @@ <?php if ($results->getResultTotal() > 0): ?> <h2><?=$this->transEsc(isset($this->overrideSideFacetCaption) ? $this->overrideSideFacetCaption : 'Narrow Search')?></h2> <?php endif; ?> -<?php $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?> +<?php $checkboxFilters = $this->recommend->getCheckboxFacetSet(); ?> <?php $checkboxesShown = false; ?> <?php if (count($checkboxFilters) > 0): foreach ($checkboxFilters as $current) { - if ($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible']) { + if ($results->getResultTotal() > 0 || $current['alwaysVisible']) { $checkboxesShown = true; break; } @@ -32,16 +46,6 @@ </div> <?php endif; ?> <?php endif; ?> -<?php $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?> -<?php $collapsedFacets = $this->recommend->getCollapsedFacets() ?> -<?php $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?> -<?php if (!empty($filterList)): ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/filter-list.phtml', [ - 'collapsedFacets' => $collapsedFacets, - 'extraFilters' => $extraFilters, - 'filterList' => $filterList, - ]); ?> -<?php endif; ?> <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> <?php $sideFacetSet = $this->recommend->getFacetSet(); ?> <?php $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?> @@ -49,16 +53,17 @@ <?php if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> <?php foreach ($sideFacetSet as $title => $cluster): ?> <div class="facet-group" id="side-panel-<?=$this->escapeHtmlAttr($title) ?>"> - <button class="title<?php if(in_array($title, $collapsedFacets)): ?> collapsed<?php endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > + <button class="title<?php if (in_array($title, $collapsedFacets)): ?> collapsed<?php endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > <?=$this->transEsc($cluster['label'])?> </button> - <div id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<?php if(!in_array($title, $collapsedFacets)): ?> in<?php endif ?>"> + <div id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<?php if (!in_array($title, $collapsedFacets)): ?> in<?php endif ?>"<?php if (in_array($title, $forceUncollapsedFacets)): ?> data-force-in="1"<?php endif ?>> <?=$this->context($this)->renderInContext( 'Recommend/SideFacets/facet.phtml', [ 'facet' => $title, 'cluster' => $cluster, - 'collapsedFacets' => $collapsedFacets ] + 'collapsedFacets' => $collapsedFacets + ] ); ?> </div> </div> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/facet.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/facet.phtml index 6ca7e7a7f8ab9cc06dda9120bb84346475a20ecd..502b89ff9d89a395808d0b81ac1713a67d01a03e 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/facet.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/facet.phtml @@ -3,7 +3,6 @@ $this->options = $options = $results->getOptions(); $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions(); $hierarchicalFacets = $this->recommend->getHierarchicalFacets(); - $collapsedFacets = $this->recommend->getCollapsedFacets(); $rangeFacets = $this->recommend->getAllRangeFacets(); $facet = $this->facet; ?> @@ -29,8 +28,8 @@ [ 'allowExclude' => $this->recommend->excludeAllowed($facet), 'title' => $facet, - 'sortOptions' => $hierarchicalFacetSortOptions[$facet] ?? '', - 'collapsedFacets' => $collapsedFacets + 'sortOptions' => $hierarchicalFacetSortOptions[$facet] ?? $hierarchicalFacetSortOptions['*'] ?? null, + 'collapsedFacets' => $this->collapsedFacets ] ); ?> <noscript> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml deleted file mode 100644 index fa43b26a56ec9d54b3b1e5de1b91031a7b660878..0000000000000000000000000000000000000000 --- a/themes/bootstrap3/templates/Recommend/SideFacets/filter-list.phtml +++ /dev/null @@ -1,30 +0,0 @@ -<div class="facet-group active-filters"> - <div class="title"><?=$this->transEsc('Remove Filters')?></div> - <?php foreach ($filterList as $field => $filters): ?> - <?php foreach ($filters as $i => $filter): ?> - <?php - $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false; - if ($index !== false) { - unset($collapsedFacets[$index]); // Open if we have a match - } - if (isset($filter['specialType']) && $filter['specialType'] == 'keyword') { - $removeLink = $this->currentPath() . $results->getUrlQuery()->replaceTerm($filter['value'], ''); - } else { - $removeLink = $this->currentPath() . $results->getUrlQuery()->removeFacet($filter['field'], $filter['value'], $filter['operator']); - } - if ($filter['displayText'] == '[* TO *]') { - $filter['displayText'] = $this->translate('filter_wildcard'); - } - ?> - <a class="facet" href="<?=$removeLink ?>" title="<?=$this->transEsc('clear_tag_filter') ?>"> - <span class="sr-only"><?=$this->transEsc('clear_tag_filter') ?></span> - <span class="text"> - <?php if ($filter['operator'] == 'NOT'): ?><?=$this->transEsc('NOT') ?><?php endif; ?> - <?php if ($filter['operator'] == 'OR' && $i > 0): ?><?=$this->transEsc('OR') ?><?php endif; ?> - <?=$this->transEsc($field) ?>: <?=$this->escapeHtml($filter['displayText']) ?> - </span> - <span class="status"><i class="fa fa-times" aria-hidden="true"></i></span> - </a> - <?php endforeach; ?> - <?php endforeach; ?> -</div> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml index 35f3de7c54311067177fdb847b5f20bb7b75e915..e9281fc6a83e8d7b8dfc6fa2dffd43f15dcaa1e3 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/range-slider.phtml @@ -1,5 +1,5 @@ <div class="facet"> - <form name="<?=$this->escapeHtmlAttr($this->title)?>Filter" id="<?=$this->escapeHtmlAttr($this->title)?>Filter"> + <form class="facet-range-form" name="<?=$this->escapeHtmlAttr($this->title)?>Filter" id="<?=$this->escapeHtmlAttr($this->title)?>Filter"> <?=$results->getUrlQuery()->asHiddenFields(['page' => "/./", 'filter' => "/^{$this->title}:.*/"])?> <input type="hidden" name="<?=$this->escapeHtmlAttr($this->facet['type'])?>range[]" value="<?=$this->escapeHtmlAttr($this->title)?>"/> <div class="date-fields"> diff --git a/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml b/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml index 39707d78bd200a65ded034b97ad6003d7f1a8fa7..74ff2e0700b1f17ba397eb1a3576df53fe049b8f 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacets/single-facet.phtml @@ -12,6 +12,12 @@ if ($this->facet['isApplied']) { $classList[] = 'active'; } + if ($this->facet['operator'] == 'OR') { + $classList[] = 'facetOR'; + } + if ($this->facet['operator'] == 'AND') { + $classList[] = 'facetAND'; + } $displayText = '-'; if (!empty($this->facet['displayText'])) { @@ -19,6 +25,7 @@ } elseif (!empty($this->facet['value'])) { $displayText = $this->escapeHtml($this->facet['value']); } + $displayText = '<span class="facet-value">' . $displayText . '</span>'; if ($this->facet['operator'] == 'OR') { $displayText = @@ -27,7 +34,7 @@ } ?> <?php if ($hasSubLinks): ?> - <li class="<?=implode(' ', $classList) ?>"> + <div class="<?=implode(' ', $classList) ?>"> <?php else: ?> <a href="<?=$toggleUrl ?>" class="<?=implode(' ', $classList) ?>" data-title="<?=$this->escapeHtmlAttr($this->facet['displayText']) ?>" data-count="<?=$this->facet['count'] ?>"<?php if($this->facet['isApplied']): ?> title="<?=$this->transEsc('applied_filter') ?>"<?php endif;?> data-lightbox-ignore> <?php endif; ?> @@ -56,4 +63,4 @@ </a> <?php endif; ?> -<?=$hasSubLinks ? '</li>' : '</a>'; ?> +<?=$hasSubLinks ? '</div>' : '</a>'; ?> diff --git a/themes/bootstrap3/templates/Recommend/SideFacetsDeferred.phtml b/themes/bootstrap3/templates/Recommend/SideFacetsDeferred.phtml index 4e82bd5a93c28686230b5d556e4b19dcdac88335..3d7e3f310def2d20be1b5f9ca82413c46ccdc4e4 100644 --- a/themes/bootstrap3/templates/Recommend/SideFacetsDeferred.phtml +++ b/themes/bootstrap3/templates/Recommend/SideFacetsDeferred.phtml @@ -5,6 +5,20 @@ $results = $this->recommend->getResults(); $activeFacets = $this->recommend->getActiveFacets(); $rangeFacets = $this->recommend->getAllRangeFacets(); + $collapsedFacets = $this->recommend->getCollapsedFacets(); + $forceUncollapsedFacets = []; + + // Make sure facets with active selections are not collapsed: + $filterList = $results->getParams()->getFilterList(true); + foreach ($filterList as $field => $filters) { + foreach ($filters as $filter) { + $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false; + if ($index !== false) { + unset($collapsedFacets[$index]); // Open if we have a match + $forceUncollapsedFacets[] = $filter['field']; + } + } + } foreach ($activeFacets as $field => $facetName) { if (isset($rangeFacets[$field]) && 'date' === $rangeFacets[$field]['type']) { @@ -18,11 +32,11 @@ <h2><?=$this->transEsc(isset($this->overrideSideFacetCaption) ? $this->overrideSideFacetCaption : 'Narrow Search')?></h2> <div class="side-facets-container-ajax" data-search-class-id="<?=$this->escapeHtmlAttr($this->searchClassId) ?>" data-location="<?=$this->escapeHtmlAttr($this->location) ?>" data-config-index="<?=$this->escapeHtmlAttr($this->configIndex) ?>"> <?php endif; ?> -<?php $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?> +<?php $checkboxFilters = $this->recommend->getCheckboxFacetSet(); ?> <?php $checkboxesShown = false; ?> <?php if (count($checkboxFilters) > 0): foreach ($checkboxFilters as $current) { - if ($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible']) { + if ($results->getResultTotal() > 0 || $current['alwaysVisible']) { $checkboxesShown = true; break; } @@ -34,16 +48,6 @@ </div> <?php endif; ?> <?php endif; ?> -<?php $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?> -<?php $collapsedFacets = $this->recommend->getCollapsedFacets() ?> -<?php $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?> -<?php if (!empty($filterList)): ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/filter-list.phtml', [ - 'collapsedFacets' => $collapsedFacets, - 'extraFilters' => $extraFilters, - 'filterList' => $filterList, - ]); ?> -<?php endif; ?> <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> <?php $sideFacetSet = $this->recommend->getFacetSet(); ?> <?php $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?> @@ -55,7 +59,7 @@ <button class="title<?php if (in_array($field, $collapsedFacets)): ?> collapsed<?php endif ?>" data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($field) ?>" > <?=$this->transEsc($facetName)?> </button> - <div id="side-collapse-<?=$this->escapeHtmlAttr($field) ?>" class="collapse<?php if (!in_array($field, $collapsedFacets)): ?> in<?php endif ?>" data-facet="<?=$this->escapeHtmlAttr($field) ?>"> + <div id="side-collapse-<?=$this->escapeHtmlAttr($field) ?>" class="collapse<?php if (!in_array($field, $collapsedFacets)): ?> in<?php endif ?>" data-facet="<?=$this->escapeHtmlAttr($field) ?>"<?php if (in_array($field, $forceUncollapsedFacets)): ?> data-force-in="1"<?php endif ?>> <span class="facet-load-indicator hidden"> <span class="text"> <i class="fa fa-spinner fa-spin"></i> <?=$this->transEsc('Loading')?>... diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml index 0e65032a1510215d4204086714daa9da49c1009b..b115d2d0193263d1222867e357b5bccde1231dac 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml @@ -1,9 +1,11 @@ +<?php $this->metadata()->generateMetatags($this->driver);?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> <?php $QRCode = $this->record($this->driver)->getQRCode("core"); $coverDetails = $this->record($this->driver)->getCoverDetails('core', 'medium', $this->record($this->driver)->getThumbnail('large')); $cover = $coverDetails['html']; - $preview = $this->record($this->driver)->getPreviews(); + $preview = ($this->previewOverride ?? false) + ? $this->previewOverride : $this->record($this->driver)->getPreviews(); ?> <?php if ($QRCode || $cover || $preview): ?> <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"> @@ -34,6 +36,11 @@ <h1 property="name"><?=$this->escapeHtml($this->driver->getShortTitle() . ' ' . $this->driver->getSubtitle() . ' ' . $this->driver->getTitleSection())?></h1> + <?php if(!empty($this->extraControls)): ?> + <?=$this->extraControls['actionControls'] ?? ''?> + <?=$this->extraControls['availabilityInfo'] ?? ''?> + <?php endif; ?> + <?php $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> <?php if ($summary): ?> <p><?=$this->truncate($summary, 300)?></p> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml index 782054e0da9d041d16c1e985a6cb7e7ab0f71741..ffb32a631671124255920040fc13b852d18713b6 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml @@ -8,7 +8,7 @@ <?php endif; ?> </span> <?php $pubDate = $field->getDate(); if (!empty($pubDate)): ?> - <span property="publicationDate"><?=$this->escapeHtml($pubDate)?></span> + <span property="datePublished"><?=$this->escapeHtml($pubDate)?></span> <?php endif; ?> <br/> <?php endforeach; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-collection.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-collection.phtml new file mode 100644 index 0000000000000000000000000000000000000000..014fe5261f889d21ce208c8efe4414ea90cb1d8c --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/link-collection.phtml @@ -0,0 +1 @@ +<?=$this->url('collection', ['id' => $this->lookfor])?>?recordID=<?=urlencode($this->driver->getUniqueID())?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml index 98d94ee07e44e062090ec66f796480db52021d9e..e854591202ecef0324de03adaf2a756c4822c890 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/list-entry.phtml @@ -63,7 +63,7 @@ <?php foreach ($summInCollection as $collId => $collText): ?> <div> <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->record($this->driver)->getLink('collection', $collid)?>"> <?=$this->escapeHtml($collText)?> </a> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml index c97910034a87922bfc51e4ee819996690cac8d53..b44fa08be2b061296f429a439177cdd84f41ff5e 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/result-list.phtml @@ -53,7 +53,7 @@ <?php foreach ($summInCollection as $collId => $collText): ?> <div> <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->record($this->driver)->getLink('collection', $collId)?>"> <?=$this->escapeHtml($collText)?> </a> </div> @@ -65,7 +65,7 @@ <?php if(!$this->driver->isCollection()): ?> <?php if ($snippet = $this->driver->getHighlightedSnippet()): ?> <?php if (!empty($snippet['caption'])): ?> - <strong><?=$this->transEsc($snippet['caption']) ?>:</strong> '; + <strong><?=$this->transEsc($snippet['caption']) ?>:</strong> <?php endif; ?> <?php if (!empty($snippet['snippet'])): ?> <span class="quotestart">“</span>...<?=$this->highlight($snippet['snippet']) ?>...<span class="quoteend">”</span><br/> @@ -181,8 +181,10 @@ <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> <?php /* Add to favorites */ ?> - <i class="result-link-icon fa fa-fw fa-star" aria-hidden="true"></i> - <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" data-lightbox class="save-record result-link-label" data-id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId()) ?>"><?=$this->transEsc('Add to favorites')?></a><br/> + <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" data-lightbox class="save-record" data-id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId()) ?>"> + <i class="result-link-icon fa fa-fw fa-star" aria-hidden="true"></i> + <span class="result-link-label"><?=$this->transEsc('Add to favorites')?></span> + </a><br/> <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> <?=$block?> <?php endif; ?> @@ -198,7 +200,7 @@ <div class="hierarchyTreeLink"> <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> <i class="result-link-icon fa fa-fw fa-sitemap" aria-hidden="true"></i> - <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab')?>?hierarchy=<?=urlencode($hierarchyID)?>" data-lightbox-post="tab=hierarchytree"> + <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchyID])?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab', ['hierarchy' => $hierarchyID])?>" data-lightbox-post="tab=hierarchytree"> <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml index b22a16307a98746349297601cfda4df0d218a51f..0d764666ce39c965c48faa1562f0cd820734579f 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/toolbar.phtml @@ -17,7 +17,7 @@ <?php $exportFormats = $this->export()->getFormatsForRecord($this->driver); ?> <?php if(count($exportFormats) > 0): ?> <li role="none" class="dropdown"> - <a class="export-toggle dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>" rel="nofollow" aria-expanded="false" aria-controls="export-options"><i class="fa fa-list-alt" aria-hidden="true"></i> <?=$this->transEsc('Export Record') ?></a> + <a class="export-toggle toolbar-btn btn-type-export dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>" rel="nofollow" aria-expanded="false" aria-controls="export-options"><?=$this->transEsc('Export Record') ?></a> <ul class="dropdown-menu" id="export-options" role="menu"> <?php foreach ($exportFormats as $exportFormat): ?> <li role="none"> @@ -33,7 +33,7 @@ <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <li role="none"> <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> - <a class="save-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" rel="nofollow"><i class="fa fa-star" aria-hidden="true"></i> <?=$this->transEsc('Add to favorites')?></a> + <a class="save-record toolbar-btn btn-type-save" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" rel="nofollow"><?=$this->transEsc('Add to favorites')?></a> <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> <?=$block?> <?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml b/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml index 9c4de23a4d0017b91ee9069b70165bede11c90a7..4297ed524828882c6fa473c24f251ff5ab35925e 100644 --- a/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/EDS/core.phtml @@ -38,7 +38,7 @@ <?php endif; ?> <?php if ($this->driver->hasHTMLFullTextAvailable()): ?> <span> - <a href="<?=$this->recordLink()->getUrl($this->driver, 'fulltext')?>#html" class="icon html fulltext"> + <a href="<?=$this->recordLink()->getUrl($this->driver)?>#html" class="icon html fulltext"> <?=$this->transEsc('HTML Full Text')?> </a> </span><br /> @@ -105,6 +105,10 @@ </span> <?php endforeach; ?> <?php endif; ?> + + <?php $doi = $this->doi($this->driver, 'record'); if ($doi->isActive()): ?> + <div><?=$doi->renderTemplate()?></div> + <?php endif; ?> </div> </div> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml index 2eaabf714ee6a9ced44e5b3e19082ec3425dd482..d42c646c30fe5dacbbaaf941780849af6650bef8 100644 --- a/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml +++ b/themes/bootstrap3/templates/RecordDriver/EDS/result-list.phtml @@ -77,17 +77,22 @@ </div> <?php if ($this->driver->hasHTMLFullTextAvailable()): ?> - <a href="<?= $this->recordLink()->getUrl($this->driver, 'fulltext') ?>#html" class="icon html fulltext _record_link" target="_blank"> + <a href="<?= $this->recordLink()->getUrl($this->driver) ?>#html" class="icon html fulltext _record_link" target="_blank"> <?=$this->transEsc('HTML Full Text')?> </a> <?php endif; ?> <?php if ($this->driver->hasPdfAvailable()): ?> - <a href="<?= $this->recordLink()->getUrl($this->driver) . '/PDF'; ?>" class="icon pdf fulltext" target="_blank"> + <a href="<?= $this->recordLink()->getTabUrl($this->driver, 'PDF'); ?>" class="icon pdf fulltext" target="_blank"> <?=$this->transEsc('PDF Full Text')?> </a> <?php endif; ?> + + <?php /* Links from DOI linker */ ?> + <?php $doi = $this->doi($this->driver, 'results'); if ($doi->isActive()):?> + <?=$doi->renderTemplate()?> + <?php endif; ?> </div> <div class="result-links hidden-print"> <?php /* Display qrcode if appropriate: */ ?> @@ -126,7 +131,7 @@ <div class="hierarchyTreeLink"> <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> <i class="fa fa-fw fa-sitemap" aria-hidden="true"></i> - <a class="hierarchyTreeLinkText" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> + <a class="hierarchyTreeLinkText" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchyID])?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> </div> diff --git a/themes/bootstrap3/templates/RecordDriver/Search2Default/link-collection.phtml b/themes/bootstrap3/templates/RecordDriver/Search2Default/link-collection.phtml new file mode 100644 index 0000000000000000000000000000000000000000..249c56d6d2da48b1a51890f98fa90642390bd3f2 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/Search2Default/link-collection.phtml @@ -0,0 +1 @@ +<?=$this->url('search2collection', ['id' => $this->lookfor])?>?recordID=<?=urlencode($this->driver->getUniqueID())?> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/core.phtml b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/core.phtml new file mode 100644 index 0000000000000000000000000000000000000000..1a5b66ca037d49cf205f7ea7ff03964cc23af317 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/core.phtml @@ -0,0 +1,170 @@ +<?php +$user = $this->auth()->isLoggedIn(); +$avail = $this->driver->getOverdriveAvailability(); +$previews = $this->driver->getPreviewLinks(); +$od_id = $this->driver->getOverdriveID(); +$rec_id = $this->driver->getUniqueID(); +$hold_url = $this->url('overdrive-hold'); + +$this->previewOverride = false; +$notification = ""; +$message = false; +$alert_level = ""; +$button = false; +$button2 = false; + +//if we didn't get availability, we can't show the proper action buttons +if ($avail->code == 'od_code_login_for_avail'): + $message = $this->transEsc('od_code_login_for_avail'); + $alert_level = "info"; +elseif ($avail->code == 'od_account_noaccess'): + $message = $this->transEsc('od_code_contentnotavail'); + $alert_level = "info"; +elseif ($avail->data): + $checkedOut = $isOnHold = false; + $checkedOutResult = $this->driver->isCheckedOut(); + if (!$checkedOutResult->status && $user): + if ($checkedOutResult->code == 'od_account_noaccess'): + $message = $this->translate("od_code_contentnotavail"); + $alert_level = "warning"; + + elseif ($checkedOutResult->code == 'od_account_problem'): + $link = "<a href=\"" . $this->url( + 'overdrive-mycontent', [], ['query' => ['redirect' => 0]] + ) . "\">" . $this->transEsc("Your Account") . "</a>"; + $message = $this->translate( + "od_account_problem", ["%%message%%" => $message] + ); + $alert_level = "warning"; + + else: + //if the result of the check is false and the user is logged in, that + //probably means that there was a connection failure + $message = $this->transEsc("od_code_connection_failed"); + $alert_level = "warning"; + + endif; + else: + $checkedOut = $checkedOutResult->data; + if (!$checkedOut): + $hold = $this->driver->isHeld($user); + endif; + + //this title is checked out + if ($checkedOut): + $message = $this->transEsc( + "od_is_checkedout", ["%%due_date%%" => $checkedOut->expires] + ); + $alert_level = "info"; + + if ($checkedOut->isReturnable): + //early return button + $button = new stdClass(); + $button->class = "returnTitle"; + $button->url = "$hold_url?od_id=$od_id&rec_id=$rec_id&action=returnTitleConfirm"; + $button->icon = "sign-in"; + $button->text = $this->transEsc('od_but_return'); + endif; + + //download button + $button2 = new stdClass(); + $button2->class = "getTitle"; + $button2->url = "$hold_url?od_id=$od_id&rec_id=$rec_id&action=getTitleConfirm"; + $button2->icon = "download"; + $button2->text = $this->transEsc('od_but_gettitle'); + + //this title is on hold + elseif ($hold): + $message = $this->transEsc('od_is_on_hold'); + $alert_level = "info"; + + if ($hold->holdReadyForCheckout): + $message .= $this->transEsc( + "od_hold_now_avail", + ["%%expireDate%%" => $hold->holdExpires] + ); + else: + $message .= $this->transEsc( + "od_hold_queue", [ + "%%holdPosition%%" => $hold->holdListPosition, + "%%numberOfHolds%%" => $hold->numberOfHolds + ] + ); + endif; + + $button = new stdClass(); + $button->class = "cancelHold"; + $button->url = "$hold_url?od_id=$od_id&rec_id=$rec_id&action=cancelHoldConfirm"; + $button->icon = "flag"; + $button->text = $this->transEsc('od_but_cancel_hold'); + + else: + //user does not already have it on hold or checked out. + /*NOTE: its possible to have no copies owned which means that the + library needs to add it back to the collection */ + if ($avail->data->copiesAvailable): + //Checkout button + $button = new stdClass(); + $button->class = "checkout"; + $button->url = "$hold_url?od_id=$od_id&rec_id=$rec_id&action=checkoutConfirm"; + $button->icon = "arrow-left"; + $button->text = $this->transEsc('od_but_checkout'); + + else: + //Place a request button + $button = new stdClass(); + $button->class = "placehold"; + $button->url = "$hold_url?od_id=$od_id&rec_id=$rec_id&action=holdConfirm"; + $button->icon = "flag"; + $button->text = $this->transEsc('od_but_hold'); + endif; + endif; //end if checked out + endif; //end checkedout result + + if($message): + $notification = "<div class=\"alert alert-$alert_level\">$message</div>"; + endif; + + if($button): + $button1Markup = <<<BTN1MARKUP + <a class="btn btn-primary {$button->class}" + data-lightbox title="{$button->text}" + href="{$button->url}"><i class="fa fa-{$button->icon}" aria-hidden="true"></i> + {$button->text}</a> +BTN1MARKUP; + endif; + + if($button2): + $button2Markup = <<<BTN2MARKUP + <a class="btn btn-primary {$button2->class}" + data-lightbox title="{$button2->text}" + href="{$button2->url}"><i class="fa fa-{$button2->icon}" aria-hidden="true"></i> + {$button2->text}</a> +BTN2MARKUP; + endif; + + $extraControls['actionControls'] = <<<ACTIONMARKUP + <div class="pull-right"> + $notification + $button1Markup $button2Markup + </div> +ACTIONMARKUP; + + $extraControls['availabilityInfo'] = " + <div class=\"availability\"> + <div class=\"odbrand pull-left media-left\"> + <img src=\"https://developerportaldev.blob.core.windows.net/media/Default/images/newLogos/OverDrive_Logo_42x42_rgb.jpg\"> + </div> + + <div class=\"copies\"><strong>{$this->transEsc("od_avail_total")}</strong> {$avail->data->copiesOwned}</div> + <div class=\"avail\"><strong>{$this->transEsc("od_avail_avail")}</strong> {$avail->data->copiesAvailable}</div> + <div class=\"holds\"><strong>{$this->transEsc("od_avail_holds")}</strong> {$avail->data->numberOfHolds}</div> + </div>"; + + $this->extraControls = $extraControls; +endif; //end if avail + +if ($previews->url) { + $this->previewOverride = "<a href=\"{$previews->url}\" target=\"_blank\" class=\"btn btn-primary\">Sample</a>"; +} +echo $this->render('RecordDriver/DefaultRecord/core.phtml'); ?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/hold.phtml b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/hold.phtml new file mode 100644 index 0000000000000000000000000000000000000000..998dddeffab6ff9c91315e0126d4295359fb6a58 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/hold.phtml @@ -0,0 +1,235 @@ +<div class="odaction"> + <div class="media clearfix"> + <div class="pull-right <?= $this->escapeHtmlAttr($coverDetails['size'])?> img-col"> + <?php if($cover): ?> + <img alt="Cover Image" class="recordcover" src="<?= $cover?>"> + <?php endif; ?> + </div> + <div class="media-body"> + <h2> + <div class="odbrand pull-left"> + <img class="media-left" src="https://developerportaldev.blob.core.windows.net/media/Default/images/newLogos/OverDrive_Logo_28x28_rgb.jpg"> + </div> + <div> + <?= $this->transEsc($actionTitleCode); ?> + </div> + </h2> + + + <h3 class="title"><?= $this->escapeHtml($title)?></h3> + + <?php if (!empty($listAuthors)): ?> + <div class="authors"> + <?= $this->transEsc('by')?>: + <?= $this->escapeHtml($listAuthors[0])?> + <?php if (count($listAuthors) > 1): ?>, <?= $this->transEsc('more_authors_abbrev')?><?php endif; ?> + </div> + <?php endif; ?> + </div> + + <div class="action <?= $action ?>"> + + <?php if($action == "checkoutConfirm"): ?> + <?php if($result->status):?> + <div class="odformats"> + <h3><?= $this->transEsc("od_dl_formats"); ?></h3> + <?php foreach($formats as $key => $format):?> + <ul><li><?= $this->translate($format->name) ?></li></ul> + <?php endforeach;?> + </div> + <form class="form-record-hold" method="POST" name="placeHold" data-lightbox-onclose="VuFind.refreshPage"> + <div class="pull-right"> + + <input name="doAction" value="doCheckout" type="hidden"> + <input class="btn btn-primary" name="doCheckout" value="<?= $this->transEsc("od_but_checkout_s"); ?>" type="submit"> + </div> + </form> + <?php elseif($result->code == "OD_CODE_ALREADY_CHECKED_OUT"): ?> + <div class="alert alert-warning"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"> + <?= $this->transEsc("od_is_checkedout", ["%%due_date%%" => $result->data->checkout->expires]); ?> + </div> + </div> + <?php elseif($result->code == "OD_CODE_ALREADY_ON_HOLD"): ?> + <div class="alert alert-warning"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"> + <?= $this->transEsc("od_is_on_hold"); + if($hold->holdReadyForCheckout): + echo $this->transEsc("od_hold_now_avail", ["%%expireDate%%" => $result->data->hold->holdExpires]); + else: + echo $this->transEsc("od_hold_queue", + ["%%holdPosition%%" => $result->data->hold->holdListPosition, + "%%numberOfHolds%%" => $result->data->hold->numberOfHolds]); + endif;?> + </div> + </div> + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"><?= $result->msg?></div> + </div> + + <?php endif;?> + <?php elseif($action == "holdConfirm"): ?> + + <?php if($result->status):?> + <form class="form-record-hold" method="POST" name="placeHold" data-lightbox-onclose="VuFind.refreshPage"> + <div class="form-group hold-required-by"> + <label class="control-label"><?= $this->transEsc("Email Address"); ?></label> + <input id="email" name="email" value="" size="" class="form-control" type="text"> + </div> + <div class="form-group pull-right"> + <input name="doAction" value="placeHold" type="hidden"> + <input class="btn btn-primary" name="placeHold" value="<?= $this->transEsc("od_but_hold_s"); ?>" type="submit"> + </div> + </form> + <?php elseif($result->code == "OD_CODE_ALREADY_CHECKED_OUT"): ?> + <div class="alert alert-warning"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"> + <?= $this->transEsc("od_is_checkedout", ["%%due_date%%" => $result->data->checkout->expires]); ?> + </div> + </div> + <?php elseif($result->code == "OD_CODE_ALREADY_ON_HOLD"): ?> + <div class="alert alert-warning"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"> + <?= $this->transEsc("od_is_on_hold"); + if($hold->holdReadyForCheckout): + echo $this->transEsc("od_hold_now_avail", ["%%expireDate%%" => $result->data->hold->holdExpires]); + else: + echo $this->transEsc("od_hold_queue", + ["%%holdPosition%%" => $result->data->hold->holdListPosition, + "%%numberOfHolds%%" => $result->data->hold->numberOfHolds]); + endif;?> + </div> + </div> + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <div class="moreInfo"><?= $result->msg?></div> + </div> + + <?php endif;?> + + <?php elseif($action == "cancelHoldConfirm"): ?> + <form class="" method="POST" name="cancelHold" data-lightbox-onclose="VuFind.refreshPage"> + + <div class="form-group pull-right"> + <input name="doAction" value="cancelHold" type="hidden"> + <input class="btn btn-primary" name="cancelHold" value="<?= $this->transEsc("od_but_cancel_hold"); ?>" type="submit"> + </div> + </form> + + + <?php elseif($action == "returnTitleConfirm"): ?> + <form class="" method="POST" name="returnTitle" data-lightbox-onclose="VuFind.refreshPage"> + <div class="pull-right"> + + <input name="doAction" value="returnTitle" type="hidden"> + <input class="btn btn-primary" name="returnTitle" value="<?= $this->transEsc("od_but_return"); ?>" type="submit"> + </div> + </form> + + <?php elseif($action == "getTitleConfirm"): ?> + + <div class="pull-right"> + + <input name="doAction" value="getTitle" type="hidden"> + <div class="odformats"> + <h3><?= $this->transEsc("od_dl_formats"); ?></h3> + <?php foreach($formats as $key => $format):?> + <a data-lightbox-ignore class="btn btn-primary" target="_blank" + href="<?= $this->url('overdrive-hold') . "?od_id=$od_id&rec_id=$rec_id&action=getTitle&getTitleFormat=$key"?>"> + <?= $this->translate($format) ?></a> + <?php endforeach;?> + </div> + </div> + <form class="" method="POST" name="getTitle" data-lightbox-onclose="VuFind.refreshPage"></form> + + <?php elseif($action == 'placeHold'): ?> + <?php if($result->status):?> + <div class="alert alert-success"><i class='fa fa-check'></i> + <?= $this->transEsc("od_hold_place_success", ["%%holdListPosition%%" => $result->data->holdListPosition]); ?> + </div> + + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <?= $this->transEsc("od_hold_place_failure") ?> + <div class="moreInfo"><?= $result->msg?></div> + </div> + + <?php endif;?> + <div class="pull-right"> + <button type="button" class="btn btn-primary" data-dismiss="modal" aria-hidden="true"><?= $this->transEsc("close"); ?></button> + </div> + + <?php elseif($action == 'cancelHold'): ?> + <?php if($result->status):?> + <div class="alert alert-success"><i class='fa fa-check'></i> + <?= $this->transEsc("od_hold_cancel_success"); ?> + </div> + + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <?= $this->transEsc("od_hold_cancel_failure") ?> + <div class="moreInfo"><?= $result->msg?></div> + </div> + <?php endif;?> + <div class="pull-right"> + <button type="button" class="btn btn-primary" data-dismiss="modal" aria-hidden="true"><?= $this->transEsc("close"); ?></button> + </div> + + <?php elseif($action == "doCheckout"): ?> + <?php if($result->status):?> + <div class="alert alert-success"><i class='fa fa-check'></i> + <?= $this->transEsc("od_docheckout_success", ['%%expireDate%%' => $result->data->expires]); ?> + </div> + + <div class="odformats"> + <h3><?= $this->transEsc("od_dl_formats"); ?></h3> + <?php foreach($formats as $key => $format): ?> + <a data-lightbox-ignore class="btn btn-primary" target="_blank" + href="<?= $this->url('overdrive-hold') . "?od_id=$od_id&rec_id=$rec_id&action=getTitle&getTitleFormat=$key"?>"> + <?= $this->translate($format->name) ?></a> + <?php endforeach;?> + </div> + + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <?= $this->transEsc("od_docheckout_failure"); ?> + <div class="moreInfo"><?= $result->msg?></div> + </div> + <?php endif;?> + <div class="pull-right"> + <button type="button" class="btn btn-primary" data-dismiss="modal" aria-hidden="true"><?= $this->transEsc("close"); ?></button> + </div> + + <?php elseif($action == "returnTitle"): ?> + <?php if($result->status):?> + <div class="alert alert-success"><i class='fa fa-check'></i> + <?= $this->transEsc("od_return_success"); ?> + </div> + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <?= $this->transEsc("od_return_failure"); ?> + <div class="moreInfo"><?= $result->msg ?></div> + </div> + <?php endif;?> + + <?php elseif($action == "getTitle"): ?> + <?php if($result->status):?> + <div class="alert alert-success"><i class='fa fa-check'></i> + <?= $this->transEsc("od_return_success"); ?> + </div> + <?php else: ?> + <div class="alert alert-danger"><i class='fa fa-exclamation-triangle'></i> + <?= $this->transEsc("od_gettitle_failure"); ?> + <div class="moreInfo"><?= $result->msg ?></div> + </div> + + <?php endif;?> + <div class="pull-right"> + <button type="button" class="btn btn-primary" data-dismiss="modal" aria-hidden="true"><?= $this->transEsc("close"); ?></button> + </div> + <?php endif; ?> + + </div><!--end .action--> + </div> <!--end .media--> +</div><!--end .odaction--> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/list-entry.phtml b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/list-entry.phtml new file mode 100644 index 0000000000000000000000000000000000000000..ec1ed3c4b6833bda4e69911d7346dc988b42977c --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/list-entry.phtml @@ -0,0 +1,197 @@ +<?php + // Set up some convenience variables: + $id = $this->driver->getUniqueId(); + $source = $this->driver->getSourceIdentifier(); + if (isset($this->list) && is_object($this->list)) { + $list_id = $this->list->id; + $user_id = $this->list->user_id; + } else { + $list_id = null; + $user_id = $this->user ? $this->user->id : null; + } + // Thumbnail + $coverDetails = $this->record($this->driver)->getCoverDetails('list-entry', 'medium', $this->recordLink()->getUrl($this->driver)); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list'); + if ($cover): + ob_start(); ?> + <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> + <?=$cover ?> + </div> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> +<!--findme--> +<div class="result<?php if($this->driver->supportsAjaxStatus()): ?> ajaxItem<?php endif ?>"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($id) ?>" class="hiddenId"/> + <input type="hidden" value="<?=$this->escapeHtmlAttr($source) ?>" class="hiddenSource"/> + <?=$this->record($this->driver)->getCheckbox()?> + <div class="media"> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?=$thumbnail ?> + <?php endif; ?> + <div class="media-body"> + <div class="result-body"> + <div class="resultItemLine1"> + <?php $missing = $this->driver instanceof \VuFind\RecordDriver\Missing; ?> + <?php if (!$missing): ?><a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"><?php endif; ?> + <span class="title"><?=$this->record($this->driver)->getTitleHtml()?></span> + <?php if (!$missing): ?></a><?php endif; ?> + </div> + + <div class="resultItemLine2"> + <?php if($this->driver->isCollection()): ?> + <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?> + <?php else: ?> + <?php $summAuthors = $this->driver->getPrimaryAuthors(); if (!empty($summAuthors)): ?> + <?=$this->transEsc('by')?> + <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> + <a href="<?=$this->record($this->driver)->getLink('author', $summAuthor)?>"><?=$this->escapeHtml($summAuthor)?></a><?=($i + 1 < $authorCount ? ';' : '') ?> + <?php endforeach; ?> + <?php endif; ?> + + <?php $journalTitle = $this->driver->getContainerTitle(); $summDate = $this->driver->getPublicationDates(); ?> + <?php 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(['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $journalTitle)) . '">' . $this->highlight($journalTitle) . '</a>';?> + <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate[0]) . ')' : ''?> + <?php elseif (!empty($summDate)): ?> + <?=!empty($summAuthor) ? '<br/>' : ''?> + <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate[0])?> + <?php endif; ?> + <?php $summInCollection = $this->driver->getContainingCollections(); if (false && !empty($summInCollection)): ?> + <?php foreach ($summInCollection as $collId => $collText): ?> + <div> + <b><?=$this->transEsc("in_collection_label")?></b> + <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <?=$this->escapeHtml($collText)?> + </a> + </div> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> + </div> + + <div class="last"> + <?php if(!$this->driver->isCollection()) { + if ($snippet = $this->driver->getHighlightedSnippet()) { + if (!empty($snippet['caption'])) { + echo '<strong>' . $this->transEsc($snippet['caption']) . ':</strong> '; + } + if (!empty($snippet['snippet'])) { + echo '<span class="quotestart">“</span>...' . $this->highlight($snippet['snippet']) . '...<span class="quoteend">”</span><br/>'; + } + } + } ?> + + <?php $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags( + null === $list_id ? true : $list_id, // get tags for all lists if no single list is selected + $user_id, 'tag' + ) : []; + ?> + <?php if (count($listTags) > 0): ?> + <strong><?=$this->transEsc('Your Tags')?>:</strong> + <?php foreach ($listTags as $tag): ?> + <a href="<?=$this->currentPath() . $results->getUrlQuery()->addFacet('tags', $tag->tag)?>"><?=$this->escapeHtml($tag->tag)?></a> + <?php endforeach; ?> + <br/> + <?php endif; ?> + <?php $listNotes = $this->driver->getListNotes($list_id, $user_id); ?> + <?php if (count($listNotes) > 0): ?> + <strong><?=$this->transEsc('Notes')?>:</strong> + <?php if (count($listNotes) > 1): ?><br/><?php endif; ?> + <?php foreach ($listNotes as $note): ?> + <?=$this->escapeHtml($note)?><br/> + <?php endforeach; ?> + <?php endif; ?> + + <?php if (count($this->lists) > 0): ?> + <strong><?=$this->transEsc('Saved in')?>:</strong> + <?php $i = 0;foreach($this->lists as $current): ?> + <a href="<?=$this->url('userList', ['id' => $current->id])?>"><?=$this->escapeHtml($current->title)?></a><?php if($i++ < count($this->lists) - 1): ?>,<?php endif; ?> + <?php endforeach; ?> + <br/> + <?php endif; ?> + + <div class="callnumAndLocation ajax-availability hidden"> + <?php 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> + <?php else: ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> + <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> + <?php endif; ?> + <?php endif; ?> + </div> + + <?php /* 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->openUrl($this->driver, 'results'); + $openUrlActive = $openUrl->isActive(); + // Account for replace_other_urls setting + $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); + + if ($openUrlActive || !empty($urls)): + ?> + <?php if ($openUrlActive): ?> + <br/> + <?=$openUrl->renderTemplate()?> + <?php endif;?> + + <?php if (!is_array($urls)) { $urls = []; } + if(!$this->driver->isCollection()): + foreach ($urls as $current): ?> + <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; ?> + <br/> + + <?=$this->record($this->driver)->getFormatList() ?> + + <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> + <span class="status ajax-availability hidden"><?=$this->transEsc('Loading')?>...</span> + <br/><br/> + <?php endif; ?> + <?=$this->record($this->driver)->getPreviews()?> + </div> + </div> + + <div class="result-links hidden-print"> + <i class="fa fa-fw fa-edit" aria-hidden="true"></i> <a href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&source=<?=urlencode($source)?><?php if (null !== $list_id):?>&list_id=<?=urlencode($list_id)?><?php endif; ?>" class="edit tool"><?=$this->transEsc('Edit')?></a><br/> + <?php /* Use a different delete URL if we're removing from a specific list or the overall favorites: */ + $deleteUrl = null === $list_id + ? $this->url('myresearch-favorites') + : $this->url('userList', ['id' => $list_id]); + $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&source=' . urlencode($source); + + $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id); + ?> + <div class="dropdown"> + <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> <a class="dropdown-toggle" id="<?=$dLabel ?>" role="button" data-toggle="dropdown" href="<?=$deleteUrlGet ?>"> + <?=$this->transEsc('Delete') ?> + </a> + <ul class="dropdown-menu" role="menu" aria-labelledby="<?=$dLabel ?>"> + <li><a onClick="$.post('<?=$deleteUrl?>', {'delete':'<?=$this->escapeJs($id) ?>','source':'<?=$this->escapeJs($source) ?>','confirm':true},function(){location.reload(true)})" title="<?=$this->transEsc('confirm_delete_brief')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> + <li><a><?=$this->transEsc('confirm_dialog_no')?></a></li> + </ul> + </div> + + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> + </div> + </div> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?=$thumbnail ?> + <?php endif; ?> + </div> +</div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/result-list.phtml b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/result-list.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4d007474c913bb6a5a903bb121e9d3108f7373a4 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/result-list.phtml @@ -0,0 +1,185 @@ +<?php + $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'medium', $this->recordLink()->getUrl($this->driver)); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result'); + if ($cover): + ob_start(); ?> + <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> + <?=$cover ?> + </div> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> +<?php endif; ?> +<input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getOverdriveID())?>" class="hiddenId" /> +<input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> +<input type="hidden" value="overdrive" class="handler-name" /> +<div class="media" data-handler-name="overdrive"> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?=$thumbnail ?> + <?php endif ?> + <div class="media-body"> + <div class="result-body"> + <div> + <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"> + <?=$this->record($this->driver)->getTitleHtml()?> + </a> + + + </div> + + <div> + <?php if($this->driver->isCollection()): ?> + <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?> + <?php else: ?> + <?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> + <?=$this->transEsc('by')?> + <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> + <a href="<?=$this->record($this->driver)->getLink('author', $this->highlight($summAuthor, null, true, false))?>"><?=$this->highlight($summAuthor)?></a><?=$i + 1 < $authorCount ? ',' : ''?> + <?php endforeach; ?> + <?php endif; ?> +<?php endif; ?> + + </div> + + <?php if(!$this->driver->isCollection()): ?> + <?php if ($snippet = $this->driver->getHighlightedSnippet()): ?> + <?php if (!empty($snippet['caption'])): ?> + <strong><?=$this->transEsc($snippet['caption']) ?>:</strong> '; + <?php endif; ?> + <?php if (!empty($snippet['snippet'])): ?> + <span class="quotestart">“</span>...<?=$this->highlight($snippet['snippet']) ?>...<span class="quoteend">”</span><br/> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> + + <?php + /* Display information on duplicate records if available */ + if ($dedupData = $this->driver->getDedupData()): ?> + <div class="dedupInformation"> + <?php + $i = 0; + foreach ($dedupData as $source => $current) { + if (++$i == 1) { + ?><span class="currentSource"><a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc("source_$source", [], $source)?></a></span><?php + } else { + if ($i == 2) { + ?> <span class="otherSources">(<?=$this->transEsc('Other Sources')?>: <?php + } else { + ?>, <?php + } + ?><a href="<?=$this->recordLink()->getUrl($current['id'])?>"><?=$this->transEsc("source_$source", [], $source)?></a><?php + } + } + if ($i > 1) { + ?>)</span><?php + }?> + </div> + <?php endif; ?> + + <div class="callnumAndLocation ajax-availability hidden"> + <?php if($this->driver->supportsAjaxStatus()):?> + <div class="callnum ajax-availability hidden status"> + <?=$this->transEsc('Loading')?>...<br/> + </div> + + <?php elseif($avail = $this->driver->getOverdriveAvailability()): ?> + <div class="availability"> + <div class='copies'><strong><?= $this->transEsc("od_avail_total")?></strong> <?=$avail->copiesOwned ?></div> + <div class='avail'><strong><?= $this->transEsc("od_avail_avail")?></strong> <?=$avail->copiesAvailable ?></div> + <div class='holds'><strong><?= $this->transEsc("od_avail_holds")?></strong> <?=$avail->numberOfHolds ?></div> + </div> + + <?php endif; ?> + </div> + + <div class="result-formats"> + <?=$this->record($this->driver)->getFormatList() ?> + </div> + + + + <?php /* 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->openUrl($this->driver, 'results'); + $openUrlActive = $openUrl->isActive(); + // Account for replace_other_urls setting + $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); + + if ($openUrlActive || !empty($urls)): ?> + <?php if ($openUrlActive): ?> + <br/> + <?=$openUrl->renderTemplate()?> + <?php endif; ?> + <?php if (!is_array($urls)) $urls = []; + if(!$this->driver->isCollection()): + foreach ($urls as $current): ?> + <a href="<?=$this->escapeHtmlAttr($this->proxyUrl($current['url']))?>" class="fulltext" target="new"><i class="fa fa-external-link" aria-hidden="true"></i> <?=($current['url'] == $current['desc']) ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])?></a><br/> + <?php endforeach; ?> + <?php endif; ?> + <?php endif; + ?> + + + + <div class="result-previews"> + <?=$this->record($this->driver)->getPreviews()?> + </div> + </div> + <div class="result-links hidden-print"> + <?php /* Display qrcode if appropriate: */ ?> + <?php if ($QRCode = $this->record($this->driver)->getQRCode("results")): ?> + <?php + // Add JS Variables for QrCode + $this->jsTranslations()->addStrings(['qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show']); + ?> + <span class="hidden-xs"> + <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> + <div class="qrcode hidden"> + <script type="text/template" class="qrCodeImgTag"> + <img alt="<?=$this->transEsc('QR Code')?>" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> + </script> + </div><br/> + </span> + <?php endif; ?> + + <?php if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> + <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?><br/> + <?php endif; ?> + + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> + <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> + <?php /* Add to favorites */ ?> + <i class="result-link-icon fa fa-fw fa-star" aria-hidden="true"></i> + <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" data-lightbox class="save-record result-link-label" data-id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId()) ?>"><?=$this->transEsc('Add to favorites')?></a><br/> + <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> + <?=$block?> + <?php endif; ?> + <?php /* Saved lists */ ?> + <div class="savedLists"> + <strong><?=$this->transEsc("Saved in")?>:</strong> + </div> + <?php endif; ?> + + <?php /* Hierarchy tree link */ ?> + <?php $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> + <?php foreach ($trees as $hierarchyID => $hierarchyTitle): ?> + <div class="hierarchyTreeLink"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> + <i class="result-link-icon fa fa-fw fa-sitemap" aria-hidden="true"></i> + <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab')?>?hierarchy=<?=urlencode($hierarchyID)?>" data-lightbox-post="tab=hierarchytree"> + <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> + </a> + </div> + <?php endforeach; ?> + <?php endif; ?> + + <?php //$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':'' ?> + </div> + </div> + <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> + <?=$thumbnail ?> + <?php endif ?> +</div> diff --git a/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/status-full.phtml b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/status-full.phtml new file mode 100644 index 0000000000000000000000000000000000000000..403f35e103f6806b790986ed9ae720174b4637d1 --- /dev/null +++ b/themes/bootstrap3/templates/RecordDriver/SolrOverdrive/status-full.phtml @@ -0,0 +1,46 @@ +<?php +//json response. array of html strings +//todo check result first +$jsonResult = new stdClass; +$data = []; +$isError = false; +$loginForAvail = false; +$noshow = false; +error_log(print_r($this->result, true)); + +if($this->result->code == 'od_code_login_for_avail'){ + $loginForAvail = true; +}elseif($this->result->code == 'od_account_noaccess'){ + $noshow = true; +}elseif (!$this->result->status) { + //we must have had a connection error. + $isError = true; +} + +foreach($ids as $id): + $current['status'] = "OK"; + $current['id'] = $id; + $avail = $this->result->data[$id]; + + if($noshow) { + $current['full_status'] = ""; + } elseif($isError || $avail->code == 'od_code_resource_not_found') { + $current['error'] = $this->transEsc('status_unknown_message'); + $current['full_status'] = ""; + } elseif ($loginForAvail || $avail->code == 'od_code_login_for_avail') { + $current['full_status'] + = "<p class='alert-info'>Login for availability</p>"; + } else { + $current['full_status'] + = "<p><div class='copies'><strong>Total Copies</strong> " + . $avail->copiesOwned . "</div> + <div class='avail'><strong>Available</strong> " + . $avail->copiesAvailable . "</div> + <div class='holds'><strong>Holds</strong> " + . $avail->numberOfHolds . "</div></p>"; + } + $data[] = $current; +endforeach; +$jsonResult->data->statuses = $data; +echo json_encode($jsonResult); +?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordTab/collectionlist.phtml b/themes/bootstrap3/templates/RecordTab/collectionlist.phtml index 67721ad320f44d26288fc201600eb72f06763916..69ffa2b2fba5057dd5e526214f450b313b2ba00f 100644 --- a/themes/bootstrap3/templates/RecordTab/collectionlist.phtml +++ b/themes/bootstrap3/templates/RecordTab/collectionlist.phtml @@ -6,36 +6,91 @@ $results = $this->tab->getResults(); $params = $this->tab->getParams(); $searchDetails = ['results' => $results, 'params' => $params, 'indexStart' => 1]; + + $filterList = $params->getFilterList(true); + $checkboxFilters = $params->getCheckboxFacets(); ?> -<?php if (($recordTotal = $results->getResultTotal()) > 0): // only display these at very top if we have results ?> - <?php foreach ($results->getRecommendations('top') as $current): ?> - <?=$this->recommend($current)?> - <?php endforeach; ?> - <div class="clearfix hidden-print"> - <div class="pull-left flip"> - <?php - $transParams = [ - '%%start%%' => $this->localizedNumber($results->getStartRecord()), - '%%end%%' => $this->localizedNumber($results->getEndRecord()), - '%%total%%' => $this->localizedNumber($recordTotal) - ]; - ?> - <?php if (!isset($this->skipTotalCount)): ?> - <?=$this->translate('showing_items_of_html', $transParams); ?> - <?php else: ?> - <?=$this->translate('showing_items_html', $transParams); ?> - <?php endif; ?> +<div class="<?=$this->layoutClass('mainbody')?>"> + <?php if (($recordTotal = $results->getResultTotal()) > 0): // only display these at very top if we have results ?> + <?php foreach ($results->getRecommendations('top') as $current): ?> + <?=$this->recommend($current)?> + <?php endforeach; ?> + <?php endif; ?> + + <div class="collection-list-controls"> + <div class="collection-control"> + <form class="form-inline" role="form" method="get" name="keywordFilterForm" id="keywordFilterForm"> + <div class="input-group"> + <input id="keywordFilter_lookfor" type="text" name="lookfor" placeholder="<?=$this->transEsc('Search within collection')?>" value="<?=$params->getDisplayQuery()?>" class="form-control"> + <?php foreach ($filterList as $field => $filters): ?> + <?php foreach ($filters as $filter): ?> + <input type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($filter['field'])?>:"<?=$this->escapeHtmlAttr($filter['value'])?>"" /> + <?php endforeach; ?> + <?php endforeach; ?> + <input type="hidden" name="limit" value="<?=$params->getLimit() ?>" /> + <input type="hidden" name="sort" value="<?=$params->getSort() ?>" /> + <span class="input-group-btn"> + <button class="btn btn-primary" type="submit" name="submit"> + <span class="sr-only"><?=$this->transEsc('Search')?></span><i class="fa fa-search"></i> + </button> + </span> + </div> + </form> </div> - <div class="pull-right flip"> + <div class="collection-control"> <?=$this->render('search/controls/limit.phtml', $searchDetails)?> + </div> + <div class="collection-control"> <?=$this->render('search/controls/sort.phtml', $searchDetails)?> </div> </div> - <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>"> - <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', $searchDetails + ['idPrefix' => ''])?> - <?=$this->render('search/list-' . $results->getParams()->getView() . '.phtml', $searchDetails)?> - <?=$this->paginationControl($results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $results])?> - </form> -<?php else: ?> - <?=$this->transEsc('collection_empty')?> -<?php endif; ?> + + + <?php if ($filterList || $checkboxFilters): ?> + <?=$this->render('search/filters.phtml', + [ + 'urlQuery' => $results->getUrlQuery(), + 'filterList' => $filterList, + 'checkboxFilters' => $checkboxFilters, + 'searchClassId' => $this->searchClassId, + 'searchType' => 'basic', + ] + ); ?> + <?php endif; ?> + + <div class="collection-list-results"> + <?php if ($recordTotal > 0): ?> + <div class="clearfix hidden-print"> + <div class="pull-left flip"> + <?php + $transParams = [ + '%%start%%' => $this->localizedNumber($results->getStartRecord()), + '%%end%%' => $this->localizedNumber($results->getEndRecord()), + '%%total%%' => $this->localizedNumber($recordTotal) + ]; + ?> + <?php if (!isset($this->skipTotalCount)): ?> + <?=$this->translate('showing_items_of_html', $transParams); ?> + <?php else: ?> + <?=$this->translate('showing_items_html', $transParams); ?> + <?php endif; ?> + </div> + </div> + <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>"> + <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', $searchDetails + ['idPrefix' => ''])?> + <?=$this->render('search/list-' . $results->getParams()->getView() . '.phtml', $searchDetails)?> + <?=$this->paginationControl($results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $results])?> + </form> + <?php else: ?> + <h4><?=$this->transEsc($params->getDisplayQuery() || ($filterCount ?? 0) > 0 ? 'nohit_heading' : 'collection_empty')?></h4> + <div class="clearfix"> + </div> + <?php endif; ?> + </div> +</div> + +<div class="<?=$this->layoutClass('sidebar')?>"> + <?php foreach ($results->getRecommendations('side') as $current): ?> + <?=$this->recommend($current)?> + <?php endforeach; ?> +</div> diff --git a/themes/bootstrap3/templates/RecordTab/componentparts.phtml b/themes/bootstrap3/templates/RecordTab/componentparts.phtml new file mode 100644 index 0000000000000000000000000000000000000000..9fee3a06f5c1700ffd246f0d5baa2a16d46c7a55 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/componentparts.phtml @@ -0,0 +1,20 @@ +<?php $max = $this->tab->getMaxResults(); $showMore = false; ?> +<?php foreach ($this->tab->getResults() as $i => $current): ?> + <?php + if ($max == $i) { + $showMore = true; + break; + } + ?> + <div> + <?=($i + 1)?>. + <a href="<?=$this->recordLink()->getUrl($current)?>"> + <?=$this->record($current)->getTitleHtml()?> + </a> + </div> +<?php endforeach; ?> +<?php if ($showMore): ?> + <a href="<?=$this->recordLink()->getChildRecordSearchUrl($this->driver)?>"> + <?=$this->transEsc('see all')?> + </a> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/formats.phtml b/themes/bootstrap3/templates/RecordTab/formats.phtml new file mode 100644 index 0000000000000000000000000000000000000000..b7eb6351efca482f89657596f80c5f7a9b215f82 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/formats.phtml @@ -0,0 +1,17 @@ +<?php + // Set page title. + $this->headTitle('Formats' . ': ' . $this->driver->getBreadcrumb()); +?> +<table class="table table-striped"> + <?php foreach ($this->driver->getFormattedDigitalFormats() as $formatkey => $format): ?> + <tr> + <th colspan="2"><?= $this->translate($formatkey)?></th> + </tr> + <?php foreach ((array)$format as $key => $value): ?> + <tr> + <td><?=$key ?></td><td><?=$value ?></td> + </tr> + <?php endforeach; ?> + <?php endforeach; ?> +</table> +<?php ?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml b/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml index 630d4be321e1b4059a8d2380fb9a694afdc5427c..bb72715d50cd5418423dc6a1e23c1ed12f8417e2 100644 --- a/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml +++ b/themes/bootstrap3/templates/RecordTab/hierarchytree.phtml @@ -26,7 +26,7 @@ <?php if($activeTree == $hierarchy): ?> <i class="fa fa-sitemap" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?> <?php else: ?> - <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a> + <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchy])?>"><?=$this->escapeHtml($hierarchyTitle)?></a> <?php endif; ?> <?php endforeach; ?> </div> @@ -50,6 +50,7 @@ <div id="hierarchyTree" class="hierarchy-tree"> <input type="hidden" value="<?=$this->escapeHtml($this->driver->getUniqueId())?>" class="hiddenRecordId" /> <input type="hidden" value="<?=$this->escapeHtml($activeTree)?>" class="hiddenHierarchyId" /> + <input type="hidden" value="<?=$this->escapeHtml($this->driver->getSourceIdentifier())?>" class="hiddenHierarchySource" /> <input type="hidden" value="<?=isset($this->treeContext) ? $this->treeContext : 'Record'?>" class="hiddenContext" /> <?php if ($this->layout()->getTemplate() != 'layout/lightbox'): ?> <noscript> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml index fefcb15662def1bca294bca16bd2b42edc00e539..55cd0d28d1b3efd562b2ee854310b8eaccb30798 100644 --- a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml +++ b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml @@ -12,7 +12,13 @@ try { $holdings = $this->driver->getRealTimeHoldings(); } catch (\VuFind\Exception\ILS $e) { - $holdings = ['holdings' => []]; + $holdings = [ + 'holdings' => [], + 'electronic_holdings' => [], + 'total' => 0, + 'page' => 0, + 'itemLimit' => 0 + ]; $offlineMode = 'ils-offline'; } // Set page title. @@ -28,15 +34,16 @@ <?php endif; ?> <?=($offlineMode == "ils-offline") ? $this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message']) : ''?> + <?php if (($this->ils()->getHoldsMode() == 'driver' && !empty($holdings['holdings'])) || $this->ils()->getTitleHoldsMode() == 'driver'): ?> <?php if ($account->loginEnabled() && $offlineMode != 'ils-offline'): ?> <?php if (!$user): ?> <div class="alert alert-info"> - <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'Holdings')?>?login=true&catalogLogin=true" data-lightbox><?=$this->transEsc("hold_login")?></a> + <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'Holdings', ['login' => 'true', 'catalogLogin' => 'true'])?>" data-lightbox><?=$this->transEsc("hold_login")?></a> </div> <?php elseif (!$user->cat_username): ?> <div class="alert alert-info"> - <?=$this->translate("hold_profile_html", ['%%url%%' => $this->recordLink()->getTabUrl($this->driver, 'Holdings') . '?catalogLogin=true'])?> + <?=$this->translate("hold_profile_html", ['%%url%%' => $this->recordLink()->getTabUrl($this->driver, 'Holdings', ['catalogLogin' => 'true'])])?> </div> <?php endif; ?> <?php endif; ?> @@ -54,10 +61,18 @@ <?php if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><?php endif; ?> <?php if ($doiActive): ?><?=$doi->renderTemplate()?><?php endif; ?> <?php endif; ?> + +<?php if (!empty($holdings['electronic_holdings'])): ?> + <?=$this->context($this)->renderInContext( + 'RecordTab/holdingsils/electronic.phtml', + ['holdings' => $holdings['electronic_holdings']] + );?> +<?php endif; ?> + <?php foreach ($holdings['holdings'] ?? [] as $holding): ?> <h3> - <?php $locationText = $this->transEsc('location_' . $holding['location'], [], $holding['location']); ?> - <?php if (isset($holding['locationhref']) && $holding['locationhref']): ?> + <?php $locationText = $this->transEscWithPrefix('location_', $holding['location']); ?> + <?php if ($holding['locationhref'] ?? false): ?> <a href="<?=$holding['locationhref']?>" target="_blank"><?=$locationText?></a> <?php else: ?> <?=$locationText?> @@ -80,17 +95,17 @@ </td> </tr> <?php endif; ?> - <?php if (isset($holding['textfields'])): foreach ($holding['textfields'] as $textFieldName => $textFields): ?> + <?php foreach ($holding['textfields'] ?? [] as $textFieldName => $textFields): ?> <tr> <?php // Translation for summary is a special case for backwards-compatibility ?> <th><?=$textFieldName == 'summary' ? $this->transEsc("Volume Holdings") : $this->transEsc(ucfirst($textFieldName))?>:</th> <td> <?php foreach ($textFields as $current): ?> - <?=$this->escapeHtml($current)?><br/> + <?=$this->linkify($this->escapeHtml($current))?><br/> <?php endforeach; ?> </td> </tr> - <?php endforeach; endif; ?> + <?php endforeach; ?> <?php foreach ($holding['items'] as $row): ?> <?php try { @@ -118,7 +133,9 @@ <?php endif; ?> </table> <?php endforeach; ?> - +<?php if (!empty($holdings['total']) && $paginator = $this->tab->getPaginator($holdings['total'], $holdings['page'], $holdings['itemLimit'])): ?> + <?=$this->paginationControl($paginator, 'Sliding', 'Helpers/pagination.phtml', ['page' => $holdings['page']])?> +<?php endif; ?> <?php $history = $this->driver->getRealTimeHistory(); ?> <?php if (is_array($history) && !empty($history)): ?> <h3><?=$this->transEsc("Most Recent Received Issues")?></h3> diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4accf033e601a99820b1d39cb21558cd8a4b2ca1 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml @@ -0,0 +1,34 @@ +<h3><?=$this->transEsc('Electronic')?></h3> +<table class="table table-striped"> + <?php foreach ($this->holdings as $item): ?> + <tr> + <td class="fullLocation"> + <?php $locationText = $this->transEscWithPrefix('location_', $item['location']); ?> + <?php if ($item['locationhref'] ?? false): ?> + <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a> + <?php else: ?> + <?=$locationText?> + <?php endif; ?> + </td> + <td class="fullAvailability"> + <?php if ($item['use_unknown_message'] ?? false): ?> + <span><?=$this->transEsc('status_unknown_message')?></span> + <?php elseif ($item['availability']): ?> + <span class="text-success"><?=!empty($item['status']) ? $this->transEsc($item['status']) : $this->transEsc('Available')?></span> + <?php else: ?> + <span class="text-danger"><?=$this->transEsc($item['status'])?></span> + <?php endif; ?> + <?php if (isset($item['item_notes'])): ?> + <div class="item-notes"> + <b><?=$this->transEsc("Item Notes")?>:</b> + <ul> + <?php foreach ($item['item_notes'] as $item_note): ?> + <li><?=$this->escapeHtml($item_note) ?></li> + <?php endforeach; ?> + </ul> + </div> + <?php endif; ?> + </td> + </tr> + <?php endforeach; ?> +</table> diff --git a/themes/bootstrap3/templates/RecordTab/reviews.phtml b/themes/bootstrap3/templates/RecordTab/reviews.phtml index d95e06775e8737a016c06b5be94e2a752370845b..6d6a876660fd832673e874ed9e1ae07f891d2ed4 100644 --- a/themes/bootstrap3/templates/RecordTab/reviews.phtml +++ b/themes/bootstrap3/templates/RecordTab/reviews.phtml @@ -8,7 +8,7 @@ <?php if (count($reviews) > 0): ?> <?php foreach ($reviews as $provider => $list): ?> <?php foreach ($list as $review): ?> - <?php if (isset($review['Summary']) && !empty($review['Summary'])): ?> + <?php if (!empty($review['Summary'])): ?> <p> <?php if (isset($review['Rating'])): ?> <img src="<?=$this->imageLink($review['Rating'] . '.gif')?>" alt="<?=$review['Rating']?>/5 Stars"/> @@ -19,7 +19,7 @@ <?php if (isset($review['Source'])): ?><strong><?=$this->transEsc('Review by')?> <?=$review['Source']?></strong><?php endif; ?> <p class="summary"> <?=$review['Content'] ?? ''?> - <?php if ((!isset($review['Content']) || empty($review['Content'])) && isset($review['ReviewURL'])): ?> + <?php if (empty($review['Content']) && isset($review['ReviewURL'])): ?> <a target="new" href="<?=$this->escapeHtmlAttr($review['ReviewURL'])?>"><?=$this->transEsc('Read the full review online...')?></a> <?php endif; ?> </p> diff --git a/themes/bootstrap3/templates/RecordTab/search2collectionlist.phtml b/themes/bootstrap3/templates/RecordTab/search2collectionlist.phtml new file mode 100644 index 0000000000000000000000000000000000000000..6c2bbd3dc2ba94e9e3c4cec871febe226cc27ed1 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/search2collectionlist.phtml @@ -0,0 +1 @@ +<?=$this->render('RecordTab/collectionlist.phtml')?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/RecordTab/staffviewoverdrive.phtml b/themes/bootstrap3/templates/RecordTab/staffviewoverdrive.phtml new file mode 100644 index 0000000000000000000000000000000000000000..cc371be1f10f081047dbb86cb683d7ea3e057860 --- /dev/null +++ b/themes/bootstrap3/templates/RecordTab/staffviewoverdrive.phtml @@ -0,0 +1,42 @@ +<?php +// Set page title. +$this->headTitle( + $this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb() +); +if($this->driver->getIsMarc()): + echo \VuFind\XSLT\Processor::process('record-marc.xsl', $this->driver->getXML('marc21')); +else: +?> +<table class="citation table table-striped staffviewod"> + <?php foreach ($this->driver->getRawData() as $field => $values): ?> + <tr> + <th><?= $this->escapeHtml($field) ?></th> + <td> + <?php foreach ((array)$values as $value): ?> + <?php if (is_array($value)): ?> + <ul class="list-group"> + <?php foreach ($value as $key => $val): ?> + <li class="list-group-item"> + <strong><?php echo $key; ?>: </strong> + <?php if (is_array($val)): ?> + <?php foreach ($val as $k => $v): ?> + <?php echo "<strong>$k</strong>: <pre>" + . $this->escapeHtml( + print_r($v, true) + ) . "</pre>"; ?><br/> + <?php endforeach; ?> + <?php else: ?> + <?php echo $this->escapeHtml($val); ?> + <?php endif; ?> + </li> + <?php endforeach; ?> + </ul> + <?php else: ?> + <?php echo $this->escapeHtml($value); ?> + <?php endif; ?> + <?php endforeach; ?> + </td> + </tr> + <?php endforeach; ?> +</table> +<?php endif;?> \ No newline at end of file diff --git a/themes/bootstrap3/templates/admin/home.phtml b/themes/bootstrap3/templates/admin/home.phtml index 36de2e5b41be303461e8efa270caaabb509fa293..1338d93efd6cc25ccb622f2c36830d1b0035e678 100644 --- a/themes/bootstrap3/templates/admin/home.phtml +++ b/themes/bootstrap3/templates/admin/home.phtml @@ -15,28 +15,37 @@ <?php $coreName = (string)$core['name']; ?> <?php $coreLabel = $coreLabels[$coreName] ?? ucwords($coreName) . ' Index'; ?> <h3><?=$this->transEsc($coreLabel)?></h3> - <table class="table table-striped"> - <tr> - <th><?=$this->transEsc('Record Count')?>: </th> - <?php $recordCount = $core->xpath('//lst[@name="' . $coreName . '"]/lst/int[@name="numDocs"]') ?> - <td><?=$this->escapeHtml((string)array_pop($recordCount))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Start Time')?>: </th> - <?php $startTime = $core->xpath('//lst[@name="' . $coreName . '"]/date[@name="startTime"]') ?> - <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($startTime))))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Last Modified')?>: </th> - <?php $lastModified = $core->xpath('//lst[@name="' . $coreName . '"]/lst/date[@name="lastModified"]') ?> - <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($lastModified))))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Uptime')?>: </th> - <?php $uptime = $core->xpath('//lst[@name="' . $coreName . '"]/long[@name="uptime"]') ?> - <td><?=$this->printms((string)array_pop($uptime))?></td> - </tr> - </table> + <?php + // If a core is offline or hasn't been initialized yet, it won't have valid + // uptime data; use this knowledge to display an appropriate message. + $uptimeData = $core->xpath('//lst[@name="' . $coreName . '"]/long[@name="uptime"]'); + $uptime = (string)array_pop($uptimeData); + ?> + <?php if (!is_numeric($uptime)): ?> + <p><i><?=$this->transEsc('System Unavailable')?></i></p> + <?php else: ?> + <table class="table table-striped"> + <tr> + <th><?=$this->transEsc('Record Count')?>: </th> + <?php $recordCount = $core->xpath('//lst[@name="' . $coreName . '"]/lst/int[@name="numDocs"]') ?> + <td><?=$this->escapeHtml((string)array_pop($recordCount))?></td> + </tr> + <tr> + <th><?=$this->transEsc('Start Time')?>: </th> + <?php $startTime = $core->xpath('//lst[@name="' . $coreName . '"]/date[@name="startTime"]') ?> + <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($startTime))))?></td> + </tr> + <tr> + <th><?=$this->transEsc('Last Modified')?>: </th> + <?php $lastModified = $core->xpath('//lst[@name="' . $coreName . '"]/lst/date[@name="lastModified"]') ?> + <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($lastModified))))?></td> + </tr> + <tr> + <th><?=$this->transEsc('Uptime')?>: </th> + <td><?=$this->printms($uptime)?></td> + </tr> + </table> + <?php endif; ?> <?php endforeach; ?> </div> diff --git a/themes/bootstrap3/templates/admin/menu.phtml b/themes/bootstrap3/templates/admin/menu.phtml index ec41087884865e7840504e064d461c7d9e3f0818..3b2f3d27742cfd453d289657e10ca5ec51b9bee2 100644 --- a/themes/bootstrap3/templates/admin/menu.phtml +++ b/themes/bootstrap3/templates/admin/menu.phtml @@ -4,4 +4,7 @@ <a href="<?=$this->url('admin/config')?>" class="list-group-item<?=strtolower($this->layout()->templateName) == "config" ? ' active' : ''?>"><?=$this->transEsc('Configuration')?></a> <a href="<?=$this->url('admin/maintenance')?>" class="list-group-item<?=strtolower($this->layout()->templateName) == "maintenance" ? ' active' : ''?>"><?=$this->transEsc('System Maintenance')?></a> <a href="<?=$this->url('admin/tags')?>" class="list-group-item<?=strtolower($this->layout()->templateName) == "tags" ? ' active' : ''?>"><?=$this->transEsc('Tag Maintenance')?></a> + <?php if ($this->overdrive()->showOverdriveAdminLink()):?> + <a href="<?=$this->url('admin/overdrive')?>" class="list-group-item<?=strtolower($this->layout()->templateName) == "overdrive" ? ' active' : ''?>"><?=$this->transEsc('Overdrive API')?></a> + <?php endif;?> </div> diff --git a/themes/bootstrap3/templates/admin/overdrive/home.phtml b/themes/bootstrap3/templates/admin/overdrive/home.phtml new file mode 100644 index 0000000000000000000000000000000000000000..6135f1f329aa61de6d1545f51fbb625da0cfa0ca --- /dev/null +++ b/themes/bootstrap3/templates/admin/overdrive/home.phtml @@ -0,0 +1,52 @@ +<?php + // Set page title. + $this->headTitle($this->translate('VuFind Administration - Overdrive API')); +?> +<div class="<?=$this->layoutClass('mainbody')?>"> + + <?=$this->flashmessages()?> + + <h2><?=$this->transEsc('Overdrive Status')?></h2> + <ul class="list-group"> + + <li class="list-group-item"> <?php echo "<strong>Current API Token:</strong> " . $this->token;?></li> + </ul> + + <h2><?=$this->transEsc('Overdrive Library Information')?></h2> + <ul class="list-group"> + <li class="list-group-item"><?php echo "<strong>Product Key:</strong> " . $this->productsKey;?></li> + + </ul> + + <h2><?=$this->transEsc('Overdrive Configuration')?></h2> + + <ul class="list-group"> + <li class="list-group-item"><?php echo "<strong>Mode: </strong>"; + echo $this->overdriveConfig->productionMode?"Production":"Integration";?></li> + <li class="list-group-item"> <?php echo "<strong>Client Key:</strong> " . $this->overdriveConfig->clientKey;?></li> + <li class="list-group-item"> <?php echo "<strong>Library ID:</strong> " . $this->overdriveConfig->libraryID;?></li> + <li class="list-group-item"> <?php echo "<strong>Website ID:</strong> " . $this->overdriveConfig->websiteID;?></li> + <li class="list-group-item"> <?php echo "<strong>ILS Name:</strong> " . $this->overdriveConfig->ILSname;?></li> + <li class="list-group-item"> <?php echo "<strong>Records in Marc Format: </strong>"; + echo $this->overdriveConfig->isMarc?"Yes":"No"?></li> + </ul> + + <h2><?=$this->transEsc('User Access')?></h2> + <ul class="list-group"> + <?php if($this->hasAccess->status):?> + <li class="list-group-item"><?php echo "<strong>Current User Has Access?:</strong> Yes"?></li> + <li class="list-group-item"><?php echo "<strong>Current User API Token?:</strong> " . $this->hasAccess->status;?></li> + <?php else:?> + <li class="list-group-item"><?php echo "<strong>Current User Has Access?:</strong> No"?></li> + <?php endif;?> + <li class="list-group-item"><?php echo "<strong>Test User:</strong>"?> + <div class="alert alert-info"><i class='fa fa-exclamation-triangle'></i> + Feature not ready yet + </div> + </li> + </ul> + +</div> +<div class="<?=$this->layoutClass('sidebar')?>"> + <?=$this->render("admin/menu.phtml")?> +</div> diff --git a/themes/bootstrap3/templates/ajax/resolverLink.phtml b/themes/bootstrap3/templates/ajax/resolverLink.phtml new file mode 100644 index 0000000000000000000000000000000000000000..f84a2cd4eee2ffbabb071094472cec49f3b5072b --- /dev/null +++ b/themes/bootstrap3/templates/ajax/resolverLink.phtml @@ -0,0 +1,22 @@ +<?php if (!empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=$this->transEsc($link['service_type'] ?? '')?>"<?=!empty($link['access']) ? ' class="access-' . $link['access'] . '"':''?>> + <?=$this->escapeHtml($link['title'] ?? '')?> + </a> +<?php else: ?> + <?=$this->escapeHtml($link['title'] ?? '')?> +<?php endif; ?> +<?php if (!empty($link['coverage'])): ?> + <span class="openurl-coverage"> + <?=$this->escapeHtml($link['coverage'])?> + </span> +<?php endif; ?> +<?php if (!empty($link['notes'])): ?> + <span class="openurl-notes"> + <?=$this->escapeHtml($link['notes'])?> + </span> +<?php endif; ?> +<?php if (!empty($link['authentication'])): ?> + <span class="openurl-authentication-notes"> + <?=$this->escapeHtml($link['authentication'])?> + </span> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/ajax/resolverLinks.phtml b/themes/bootstrap3/templates/ajax/resolverLinks.phtml index 44992801ee405d5edecbcbff9a7bb5faecd5b73b..5f757148e234baa7e0df6d0716e3ff1052702528 100644 --- a/themes/bootstrap3/templates/ajax/resolverLinks.phtml +++ b/themes/bootstrap3/templates/ajax/resolverLinks.phtml @@ -5,11 +5,7 @@ <ul> <?php foreach ($this->electronic as $link): ?> <li> - <?php if (!empty($link['href'])): ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php else: ?> - <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php endif; ?> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'electronic']); ?> </li> <?php endforeach; ?> </ul> @@ -21,24 +17,22 @@ <ul> <?php foreach ($this->print as $link): ?> <li> - <?php if (!empty($link['href'])): ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php else: ?> - <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php endif; ?> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'print']); ?> </li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <div class="openurls"> - <?php if (!empty($this->moreOptionsLink)): ?><strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong><?php endif; ?> + <?php if (!empty($this->moreOptionsLink)): ?> + <strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong> + <?php endif; ?> <?php if (!empty($this->services)): ?> <ul> <?php foreach ($this->services as $link): ?> <?php if (!empty($link['href'])): ?> <li> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-' . $link['access'] . '"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'service']); ?> </li> <?php endif; ?> <?php endforeach; ?> diff --git a/themes/bootstrap3/templates/ajax/status-full.phtml b/themes/bootstrap3/templates/ajax/status-full.phtml index 0c0a4588df1b5e03029a377aa95ad6d6b240a861..19a7bcee09b9dfd7da16686b7955ec01387cb057 100644 --- a/themes/bootstrap3/templates/ajax/status-full.phtml +++ b/themes/bootstrap3/templates/ajax/status-full.phtml @@ -8,8 +8,8 @@ <?php if (++$i == 5) break; // Show no more than 5 items ?> <tr> <td class="fullLocation"> - <?php $locationText = $this->transEsc('location_' . $item['location'], [], $item['location']); ?> - <?php if (isset($item['locationhref']) && $item['locationhref']): ?> + <?php $locationText = $this->transEscWithPrefix('location_', $item['location']); ?> + <?php if ($item['locationhref'] ?? false): ?> <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a> <?php else: ?> <?=$locationText?> @@ -23,7 +23,7 @@ <?php endif; ?> </td> <td class="fullAvailability"> - <?php if (isset($item['use_unknown_message']) && $item['use_unknown_message']): ?> + <?php if ($item['use_unknown_message'] ?? false): ?> <span><?=$this->transEsc("status_unknown_message")?></span> <?php elseif ($item['availability']): ?> <span class="text-success"><?=($item['reserve'] == 'Y') ? $this->transEsc("On Reserve") : $this->transEsc("Available")?></span> diff --git a/themes/bootstrap3/templates/authority/record.phtml b/themes/bootstrap3/templates/authority/record.phtml index 2d4f4b71b06cbdf5e7815d494f999907ccb57146..f505f4a1a9e63b58071962747ac69f63138956a0 100644 --- a/themes/bootstrap3/templates/authority/record.phtml +++ b/themes/bootstrap3/templates/authority/record.phtml @@ -1,2 +1,7 @@ -<?php $this->layout()->breadcrumbs = false; ?> -<?=$this->record($this->driver)->getTab($this->tabs['Details'])?> +<?php + $this->layout()->breadcrumbs = false; + // Pick a tab to display -- Details if available, otherwise first option (if any): + $tab = $this->tabs['Details'] ?? current($this->tabs) ?? null; +?> +<h1><?=$this->escapeHtml($this->driver->getBreadcrumb())?></h1> +<p><?=empty($tab) ? $this->transEsc('no_description') : $this->record($this->driver)->getTab($tab)?></p> diff --git a/themes/bootstrap3/templates/breadcrumbs/default.phtml b/themes/bootstrap3/templates/breadcrumbs/default.phtml index baf8e259b68cf6635d11efbab8571d99af3137b9..73cc825aa08474e66394dc066e5442204ad0996e 100644 --- a/themes/bootstrap3/templates/breadcrumbs/default.phtml +++ b/themes/bootstrap3/templates/breadcrumbs/default.phtml @@ -1,7 +1,7 @@ <li><a href="<?=$this->url('home')?>"><?=$this->transEsc('Home')?></a></li> <?php $current = $this->layout()->breadcrumbs; $current = current($current); ?> <?php foreach($current as $id => $parent): ?> - <li><a href="<?=$this->url('collection', ['id' => $id]) ?>"><?=$parent ?></a></li> + <li><a href="<?=$this->record($this->driver)->getLink('collection', $id)?>"><?=$parent ?></a></li> <?php endforeach; ?> <?php if(isset($this->layout()->end)): ?> <li title="<?=$this->layout()->title ?>"><?=$this->truncate($this->layout()->title, 100) ?></li> diff --git a/themes/bootstrap3/templates/cart/cart.phtml b/themes/bootstrap3/templates/cart/cart.phtml index 31090b61f518a6b9a853dd0586e507df18cbdd81..d81bd52c547d81485376d74f0b71667125fd4bef 100644 --- a/themes/bootstrap3/templates/cart/cart.phtml +++ b/themes/bootstrap3/templates/cart/cart.phtml @@ -18,28 +18,23 @@ </label> </div> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> - <button type="submit" class="btn btn-default" name="saveCart" title="<?=$this->transEsc('bookbag_save')?>" value="1"> - <i class="fa fa-save" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-save" name="saveCart" value="1" title="<?=$this->transEsc('bookbag_save')?>" value="1"> <?=$this->transEsc('Save')?> </button> <?php endif; ?> - <button type="submit" class="btn btn-default" name="email" title="<?=$this->transEsc('bookbag_email')?>" value="1"> - <i class="fa fa-envelope-o" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-email" name="email" value="1" title="<?=$this->transEsc('bookbag_email')?>" value="1"> <?=$this->transEsc('Email')?> </button> <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> - <button type="submit" class="btn btn-default" name="export" title="<?=$this->transEsc('bookbag_export')?>" value="1"> - <i class="fa fa-list-alt" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-export" name="export" value="1" title="<?=$this->transEsc('bookbag_export')?>" value="1"> <?=$this->transEsc('Export')?> </button> <?php endif; ?> - <button type="submit" class="btn btn-default dropdown-toggle" name="print" title="<?=$this->transEsc('print_selected')?>" value="1"> - <i class="fa fa-printer" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-print dropdown-toggle" name="print" value="1" title="<?=$this->transEsc('print_selected')?>" value="1"> <?=$this->transEsc('Print')?> </button> <div class="btn-group" id="cartDelete"> - <button type="submit" name="delete" class="btn btn-default dropdown-toggle" data-toggle="dropdown" id="cart-delete-label" value="1"> - <i class="fa fa-trash" aria-hidden="true"></i> + <button type="submit" name="delete" value="1" class="toolbar-btn btn-type-delete dropdown-toggle" data-toggle="dropdown" id="cart-delete-label" value="1"> <?=$this->transEsc('Delete')?> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="cart-delete-label"> @@ -48,8 +43,7 @@ </ul> </div> <div class="btn-group"> - <button type="submit" class="btn btn-default dropdown-toggle" name="empty" data-toggle="dropdown" id="cart-empty-label" value="1"> - <i class="fa fa-close" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-empty dropdown-toggle" name="empty" value="1" data-toggle="dropdown" id="cart-empty-label" value="1"> <?=$this->transEsc('Empty Book Bag')?> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="cart-empty-label"> diff --git a/themes/bootstrap3/templates/collection/view.phtml b/themes/bootstrap3/templates/collection/view.phtml index d4f9c63195a7aed7ba49cf9a44317b2759d4c366..11a24592088a8d7729e1e65b5ef2270588692994 100644 --- a/themes/bootstrap3/templates/collection/view.phtml +++ b/themes/bootstrap3/templates/collection/view.phtml @@ -31,7 +31,7 @@ <?=$this->record($this->driver)->getToolbar()?> <div class="record"> - <div<?php if (!$tree): /* in tree mode, do not constrain width with a class */ ?> class="<?=$this->layoutClass('mainbody') ?>"<?php endif; ?>> + <div<?php if (!$tree): /* in tree mode, do not constrain width with a class */ ?> class="<?=$this->layoutClass('mainbody') ?> solo"<?php endif; ?>> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" id="record_id" /> <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier())?>" class="hiddenSource" /> <?=$this->flashmessages()?> @@ -74,13 +74,5 @@ <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>':''?> </div> - - <?php if (isset($activeTabObj) && is_callable([$activeTabObj, 'getSideRecommendations'])): ?> - <div class="<?=$this->layoutClass('sidebar')?>"> - <?php foreach ($activeTabObj->getSideRecommendations() as $current): ?> - <?=$this->recommend($current)?> - <?php endforeach; ?> - </div> - <?php endif; ?> </div> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$(document).ready(recordDocReady);', 'SET'); ?> diff --git a/themes/bootstrap3/templates/combined/results-ajax.phtml b/themes/bootstrap3/templates/combined/results-ajax.phtml index f6a80e5f6d51f29638b6ca4532ead33a6f261cc7..d60e5c7cec110d3820d0db56525d34644925c933 100644 --- a/themes/bootstrap3/templates/combined/results-ajax.phtml +++ b/themes/bootstrap3/templates/combined/results-ajax.phtml @@ -11,9 +11,12 @@ $searchClassIdEncoded = urlencode($searchClassId); $targetIdHtmlEscaped = $this->escapeHtml('#' . $currentSearch['domId']); $lookforEncoded = urlencode($lookfor); + $typeEncoded = urlencode($params->getSearchHandler()); $loadJs = <<<JS $(document).ready(function(){ - var url = VuFind.path + '/Combined/Result?id=$searchClassIdEncoded&lookfor=$lookforEncoded'; + var url = VuFind.path + + '/Combined/Result?id=$searchClassIdEncoded&lookfor=$lookforEncoded' + + '&type=$typeEncoded'; var container = $('$targetIdHtmlEscaped'); VuFind.combinedSearch.init(container, url); }); diff --git a/themes/bootstrap3/templates/combined/results.phtml b/themes/bootstrap3/templates/combined/results.phtml index 0a552a622f9d99bea7036bf4166c2a2320ac8114..9bdc276a54f6737aff33abbfa3fb0cb915493357 100644 --- a/themes/bootstrap3/templates/combined/results.phtml +++ b/themes/bootstrap3/templates/combined/results.phtml @@ -17,7 +17,7 @@ 'searchId' => $this->results->getSearchId(), 'searchClassId' => $this->params->getSearchClassId(), 'checkboxFilters' => $this->params->getCheckboxFacets(), - 'filterList' => $this->params->getFilters(), + 'filterList' => $this->params->getFilterList(true), 'hasDefaultsApplied' => $this->params->hasDefaultsApplied(), 'selectedShards' => $this->params->getSelectedShards() ] diff --git a/themes/bootstrap3/templates/feedback/form.phtml b/themes/bootstrap3/templates/feedback/form.phtml index f4246bec3daeb40b1a74f8d9244fdb51153e13dc..2878cc3ffd27c8e9e161891eb493771ab3b31e6e 100644 --- a/themes/bootstrap3/templates/feedback/form.phtml +++ b/themes/bootstrap3/templates/feedback/form.phtml @@ -16,63 +16,68 @@ $helpPre = isset($help['pre']) ? $this->translate($help['pre']) : null; $helpPost = isset($help['post']) ? $this->translate($help['post']) : null; ?> -<div class="feedback-content"> +<?php if (!$this->inLightbox): ?><div class="feedback-content"><?php endif; ?> <?php if ($title): ?> <?php $this->headTitle($title); ?> - <h1><?=$title?></h1> + <?php $headTag = $this->inLightbox ? 'h2' : 'h1'; ?> + <<?=$headTag?>><?=$title?></<?=$headTag?>> <?php endif; ?> <?=$this->flashmessages()?> - <?php if ($form->showOnlyForLoggedUsers() && !$this->user): ?> - <div class="form-group"> - <p><?=$this->translate('feedback_login_required')?></p> - <a href="<?=$this->url('myresearch-userlogin') ?>" class="btn btn-primary" data-lightbox title="Login"><i class="fa fa-sign-in" aria-hidden="true" data-lightbox-onclose="window.location.href='<?=$this->escapeHtmlAttr($formUrl) ?>'"></i> <?=$this->transEsc("Login") ?></a> - </div> - <?php else: ?> - <?= $this->form()->openTag($form); ?> - <?php if ($helpPre): ?> - <div class="form-group"> - <div class="form-info pre"> - <?=$helpPre?> - </div> + <?= $this->form()->openTag($form); ?> + <?php if ($helpPre): ?> + <div class="form-group"> + <div class="form-info pre"> + <?=$helpPre?> </div> - <?php endif ?> + </div> + <?php endif ?> - <?php $currentGroup = null; ?> - <?php foreach($form->getElements() as $el): ?> - <?php - $formElement = $form->get($el['name']); + <?php $currentGroup = null; ?> + <?php foreach($form->getElements() as $el): ?> + <?php + $formElement = $form->get($el['name']); - // Group form elements into field sets - $handleGroup = $group = null; - if (isset($el['group']) && !empty($el['group'])) { - $group = $el['group']; - } - if ($group && $currentGroup === null) { - $handleGroup = 'open'; - $currentGroup = $group; - } elseif ($currentGroup && !$group) { - $handleGroup = 'close'; - $currentGroup = null; - } elseif ($currentGroup !== $group) { - $handleGroup = 'openAndClose'; - $currentGroup = $group; - } - ?> + // Group form elements into field sets + $handleGroup = $group = null; + if (isset($el['group']) && !empty($el['group'])) { + $group = $el['group']; + } + if ($group && $currentGroup === null) { + $handleGroup = 'open'; + $currentGroup = $group; + } elseif ($currentGroup && !$group) { + $handleGroup = 'close'; + $currentGroup = null; + } elseif ($currentGroup !== $group) { + $handleGroup = 'openAndClose'; + $currentGroup = $group; + } + ?> <?php if (in_array($handleGroup, ['close', 'openAndClose'])): ?> </div> <?php endif ?> <?php if (in_array($handleGroup, ['open', 'openAndClose'])): ?> - <div class="field-set"> + <?php if (in_array($el['type'], ['checkbox', 'radio'])): ?> + <div class="field-set" role="group"<?= !empty($el['label']) ? ' aria-labelledby="' . $this->escapeHtmlAttr($el['name']) . '"' : ''?>> + <?php else: ?> + <div class="field-set"> + <?php endif ?> <?php endif ?> - <div class="form-group"> + <div class="form-group <?= $el['type'] ?>"> <?php if (!empty($el['help'])): ?> - <p class="info"><?= $this->transEsc($el['help']) ?></p> + <p class="info"><?= substr($el['help'], -5) === '_html' ? $this->translate($el['help']) : $this->transEsc($el['help']) ?></p> <?php endif ?> <?php if ($el['type'] !== 'submit'): ?> - <label for="<?=$this->escapeHtmlAttr($el['name'])?>" class="control-label<?=!empty($el['required']) ? ' required' : ''?>"><?=$this->transEsc($el['label'])?>:</label> + <?php if ($el['label']): ?> + <?php if (in_array($el['type'], ['checkbox', 'radio'])): ?> + <p id="<?=$this->escapeHtmlAttr($el['name'])?>" class="control-label radio-label<?=!empty($el['required']) ? ' required' : ''?>"><?=$this->transEsc($el['label'])?>:</p> + <?php else: ?> + <label for="<?=$this->escapeHtmlAttr($el['name'])?>" class="control-label<?=!empty($el['required']) ? ' required' : ''?>"><?=$this->transEsc($el['label'])?>:</label> + <?php endif ?> + <?php endif ?> <?php else: ?> <?php if ($helpPost): ?> <div class="form-info post"> @@ -85,5 +90,4 @@ </div> <?php endforeach ?> <?= $this->form()->closeTag() ?> - <?php endif ?> -</div> +<?php if (!$this->inLightbox): ?></div><?php endif; ?> diff --git a/themes/bootstrap3/templates/header.phtml b/themes/bootstrap3/templates/header.phtml index 838b40a33ac1560523f26e281e54d2bf1aeb52ca..fdb62ccc66767fe7524da7ef8fc38018a428885e 100644 --- a/themes/bootstrap3/templates/header.phtml +++ b/themes/bootstrap3/templates/header.phtml @@ -49,13 +49,14 @@ <?php if (isset($this->layout()->themeOptions) && count($this->layout()->themeOptions) > 1): ?> <li class="theme dropdown"> - <form method="post" name="themeForm" id="themeForm"> - <input type="hidden" name="ui"/> - </form> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Theme")?> <b class="caret"></b></a> <ul class="dropdown-menu"> <?php foreach ($this->layout()->themeOptions as $current): ?> - <li<?=$current['selected'] ? ' class="active"' : ''?>><a href="#" onClick="document.themeForm.ui.value='<?=$this->escapeHtmlAttr($current['name'])?>';document.themeForm.submit()"><?=$this->transEsc($current['desc'])?></a></li> + <li<?=$current['selected'] ? ' class="active"' : ''?>> + <a href="<?=$this->escapeHtmlAttr($this->url()->addQueryParameters(['ui' => $current['name']])) ?>" rel="nofollow"> + <?=$this->transEsc($current['desc']) ?> + </a> + </li> <?php endforeach; ?> </ul> </li> @@ -63,13 +64,14 @@ <?php if (isset($this->layout()->allLangs) && count($this->layout()->allLangs) > 1): ?> <li class="language dropdown"> - <form method="post" name="langForm" id="langForm"> - <input type="hidden" name="mylang"/> - </form> <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Language")?> <b class="caret"></b></a> <ul class="dropdown-menu"> <?php foreach ($this->layout()->allLangs as $langCode => $langName): ?> - <li<?=$this->layout()->userLang == $langCode ? ' class="active"' : ''?>><a href="#" onClick="document.langForm.mylang.value='<?=$langCode?>';document.langForm.submit()"><?=$this->displayLanguageOption($langName)?></a></li> + <li<?=$this->layout()->userLang == $langCode ? ' class="active"' : ''?>> + <a href="<?=$this->escapeHtmlAttr($this->url()->addQueryParameters(['lng' => $langCode])) ?>" rel="nofollow"> + <?=$this->displayLanguageOption($langName) ?> + </a> + </li> <?php endforeach; ?> </ul> </li> diff --git a/themes/bootstrap3/templates/librarycards/editcard.phtml b/themes/bootstrap3/templates/librarycards/editcard.phtml index 1e241c594c59d15b963c18971ebd60e6cd3635dc..7d8e40684fe2a84ddb7818ea7087fc6550392515 100644 --- a/themes/bootstrap3/templates/librarycards/editcard.phtml +++ b/themes/bootstrap3/templates/librarycards/editcard.phtml @@ -1,6 +1,6 @@ <?php // Set up page title: - $pageTitle = empty($this->card->id) ? 'Add a Library Card' : "Edit Library Card"; + $pageTitle = empty($this->card->id) ? 'Add a Library Card' : 'Edit Library Card'; $this->headTitle($this->translate($pageTitle)); // Set up breadcrumbs: @@ -30,14 +30,31 @@ </div> <?php endif; ?> <div class="form-group"> - <label class="control-label" for="login_username"><?=$this->transEsc('Username')?>:</label> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <label class="control-label password-login" for="login_username"><?=$this->transEsc('Username')?>:</label> + <?php endif; ?> + <?php if (null === $this->loginMethod || 'email' === $this->loginMethod): ?> + <label class="control-label email-login<?php if (null === $this->loginMethod): ?> hidden<?php endif; ?>" for="login_username"><?=$this->transEsc('Email')?>:</label> + <?php endif; ?> <input id="login_username" type="text" name="username" value="<?=$this->escapeHtmlAttr($this->username)?>" class="form-control"/> </div> - <div class="form-group"> - <label class="control-label" for="login_password"><?=$this->transEsc('Password')?>:</label> - <input id="login_password" type="password" name="password" value="" placeholder="<?=!empty($this->card->id) ? $this->escapeHtmlAttr($this->translate('library_card_edit_password_placeholder')) : ''?>" class="form-control"/> - </div> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <div class="form-group"> + <label class="control-label" for="login_password"><?=$this->transEsc('Password')?>:</label> + <input id="login_password" type="password" name="password" value="" placeholder="<?=!empty($this->card->id) ? $this->escapeHtmlAttr($this->translate('library_card_edit_password_placeholder')) : ''?>" class="form-control"/> + </div> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>"/> </div> </form> + +<?php + if (null !== $targets) { + $methods = json_encode($this->loginMethods); + $script = "setupMultiILSLoginFields($methods, 'login_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + } +?> diff --git a/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml b/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml index c45b65be814f752315ead128e55978daa25fcac0..620a90e3945d1ab26e475ebcf139b2c7f884c008 100644 --- a/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml +++ b/themes/bootstrap3/templates/myresearch/bulk-action-buttons.phtml @@ -3,22 +3,22 @@ <input type="hidden" name="listName" value="<?=$this->escapeHtmlAttr($list->title)?>" /> <?php endif; ?> <?php $user = $this->auth()->isLoggedIn(); ?> -<div class="bulkActionButtons"> +<nav class="bulkActionButtons"> <div class="bulk-checkbox"> <input type="checkbox" name="selectAll" class="checkbox-select-all" id="myresearchCheckAll"/> <label for="myresearchCheckAll"><?=$this->transEsc('select_page')?> | <?=$this->transEsc('with_selected')?>:</label> </div> - <div class="btn-group"> - <input class="btn btn-default" type="submit" name="email" value="<?=$this->transEsc('Email')?>" title="<?=$this->transEsc('email_selected')?>"/> + <ul class="action-toolbar"> + <li><button class="toolbar-btn btn-type-email" type="submit" name="email" value="1" title="<?=$this->transEsc('email_selected')?>"><?=$this->transEsc('Email') ?></button></li> <?php if ((null !== $this->list && $this->list->editAllowed($user)) || null === $this->list && $user): ?> - <input class="btn btn-default" id="<?=$this->idPrefix?>delete_list_items_<?=null !== $this->list ? $this->escapeHtmlAttr($this->list->id) : ''?>" type="submit" name="delete" value="<?=$this->transEsc('Delete')?>" title="<?=$this->transEsc('delete_selected')?>"/> + <li><button class="toolbar-btn btn-type-delete" id="<?=$this->idPrefix?>delete_list_items_<?=null !== $this->list ? $this->escapeHtmlAttr($this->list->id) : ''?>" type="submit" name="delete" value="1" title="<?=$this->transEsc('delete_selected')?>"><?=$this->transEsc('Delete') ?></button></li> <?php endif; ?> <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> - <input class="btn btn-default" type="submit" name="export" value="<?=$this->transEsc('Export')?>" title="<?=$this->transEsc('export_selected')?>"/> + <li><button class="toolbar-btn btn-type-export" type="submit" name="export" value="1" title="<?=$this->transEsc('export_selected')?>"><?=$this->transEsc('Export')?></button></li> <?php endif; ?> - <input class="btn btn-default" type="submit" name="print" value="<?=$this->transEsc('Print')?>" title="<?=$this->transEsc('print_selected')?>" data-lightbox-ignore/> + <li><button class="toolbar-btn btn-type-print" type="submit" name="print" value="1" title="<?=$this->transEsc('print_selected')?>" data-lightbox-ignore><?=$this->transEsc('Print')?></button></li> <?php if ($this->cart()->isActive()): ?> - <input class="btn btn-default" id="<?=$this->idPrefix?>updateCart" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/> + <li><button class="toolbar-btn btn-type-add" id="<?=$this->idPrefix?>updateCart" type="submit" name="add" value="1"><?=$this->transEsc('Add to Book Bag')?></button></li> <?php endif; ?> - </div> -</div> + </ul> +</nav> diff --git a/themes/bootstrap3/templates/myresearch/cataloglogin.phtml b/themes/bootstrap3/templates/myresearch/cataloglogin.phtml index 0cb7afbe7fa2ec9d08a1853ae61ec1cd7b4ee0e5..3a2eb713ef26bfabe7755bd998028d4f90002879 100644 --- a/themes/bootstrap3/templates/myresearch/cataloglogin.phtml +++ b/themes/bootstrap3/templates/myresearch/cataloglogin.phtml @@ -20,24 +20,43 @@ <form method="post" action="<?=$this->serverUrl(true)?>" class="form-catalog-login"> <?php if ($this->targets !== null): ?> <div class="form-group"> - <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> - <select id="login_target" name="target" class="form-control"> + <label class="control-label" for="profile_cat_target"><?=$this->transEsc('login_target')?>:</label> + <select id="profile_cat_target" name="target" class="form-control"> <?php foreach ($this->targets as $target): ?> - <option value="<?=$this->escapeHtmlAttr($target)?>"><?=$this->transEsc("source_$target", null, $target)?></option> + <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target === $this->defaultTarget ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> <?php endforeach; ?> </select> </div> <?php endif; ?> <div class="form-group"> - <label class="control-label" for="profile_cat_username"><?=$this->transEsc('Library Catalog Username')?>:</label> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <label class="control-label password-login" for="profile_cat_username"><?=$this->transEsc('Library Catalog Username')?>:</label> + <?php endif; ?> + <?php if (null === $this->loginMethod || 'email' === $this->loginMethod): ?> + <label class="control-label email-login<?php if (null === $this->loginMethod): ?> hidden<?php endif; ?>" for="profile_cat_username"><?=$this->transEsc('Email')?>:</label> + <?php endif; ?> <input id="profile_cat_username" type="text" name="cat_username" value="" class="form-control"/> </div> - <div class="form-group"> - <label class="control-label" for="profile_cat_password"><?=$this->transEsc('Library Catalog Password')?>:</label> - <input id="profile_cat_password" type="password" name="cat_password" value="" class="form-control"/> - </div> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <div class="form-group"> + <label class="control-label" for="profile_cat_password"><?=$this->transEsc('Library Catalog Password')?>:</label> + <input id="profile_cat_password" type="password" name="cat_password" value="" class="form-control"/> + </div> + <?php else: ?> + <input type="hidden" name="cat_password" value="****"> + <?php endif; ?> <div class="form-group"> <input class="btn btn-primary" type="submit" name="processLogin" value="<?=$this->transEsc('Login')?>"> </div> </form> + + <?php + if (null !== $this->targets) { + $methods = json_encode($this->loginMethods); + $script = "setupMultiILSLoginFields($methods, 'profile_cat_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + } + ?> <?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/changeemail.phtml b/themes/bootstrap3/templates/myresearch/changeemail.phtml new file mode 100644 index 0000000000000000000000000000000000000000..356699c90a298e0479f0002d7009d822ffa1074c --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/changeemail.phtml @@ -0,0 +1,32 @@ +<?php + // Set up page title: + $this->headTitle($this->translate('Change Email Address')); + + // Set up breadcrumbs: + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>' + . '<li class="active">' . $this->transEsc('Change Email Address') . '</li>'; +?> +<div class="<?=$this->layoutClass('mainbody')?>"> + + <h2><?=$this->transEsc('Change Email Address') ?></h2> + <?=$this->flashmessages() ?> + + <?php if (!$this->auth()->getManager()->supportsEmailChange($this->auth_method)): ?> + <div class="error"><?=$this->transEsc('change_email_disabled') ?></div> + <?php else: ?> + <form id="newemail" class="form-new-email" action="<?=$this->url('myresearch-changeemail') ?>" method="post" data-toggle="validator" role="form"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->auth()->getManager()->getCsrfHash())?>" name="csrf"/> + <div class="form-group"> + <label class="control-label"><?=$this->transEsc('Email Address') ?>:</label> + <input type="text" name="email" class="form-control" value="<?=$this->escapeHtmlAttr($this->email)?>" /> + </div> + <?=$this->recaptcha()->html($this->useRecaptcha) ?> + <div class="form-group"> + <input class="btn btn-primary" name="submit" type="submit" value="<?=$this->transEsc('Submit')?>" /> + </div> + </form> + <?php endif; ?> +</div> +<div class="<?=$this->layoutClass('sidebar')?>"> + <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'newpassword'])?> +</div> diff --git a/themes/bootstrap3/templates/myresearch/checkedout.phtml b/themes/bootstrap3/templates/myresearch/checkedout.phtml index 9dff2c8a96d26ab7ee90e46dfa0ca528fa99756a..149145eeb18d90bb6acbb0a000c83a23de0f860e 100644 --- a/themes/bootstrap3/templates/myresearch/checkedout.phtml +++ b/themes/bootstrap3/templates/myresearch/checkedout.phtml @@ -42,7 +42,7 @@ <?php endif; ?> </nav> <?php if ($this->renewForm): ?> - <form name="renewals" method="post" id="renewals"> + <form name="renewals" method="post" id="renewals" data-clear-account-cache="checkedOut"> <div class="toolbar"> <div class="checkbox"> <label> @@ -148,12 +148,12 @@ <?php endif; ?> <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> + <strong><?=$this->transEscWithPrefix('location_', $ilsDetails['institution_name'])?></strong> <br /> <?php endif; ?> <?php if (!empty($ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> + <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEscWithPrefix('location_', $ilsDetails['borrowingLocation'])?> <br /> <?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/emailnotverified.phtml b/themes/bootstrap3/templates/myresearch/emailnotverified.phtml new file mode 100644 index 0000000000000000000000000000000000000000..a949b54b3241f2fa2368740b4015645bf4860012 --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/emailnotverified.phtml @@ -0,0 +1,2 @@ +<h2><?=$this->transEsc('User Account')?></h2> +<?=$this->flashmessages()?> diff --git a/themes/bootstrap3/templates/myresearch/fines.phtml b/themes/bootstrap3/templates/myresearch/fines.phtml index 40f940532f879c99dd0fca139400cfd3016503be..08685e2e37f75141bfb9bb20275df876cac38212 100644 --- a/themes/bootstrap3/templates/myresearch/fines.phtml +++ b/themes/bootstrap3/templates/myresearch/fines.phtml @@ -38,7 +38,7 @@ $tableData['Title'][] = $title; $tableData['Checked Out'][] = $this->escapeHtml($record['checkout'] ?? ''); $tableData['Due Date'][] = $this->escapeHtml($record['duedate'] ?? ''); - $tableData['Fine'][] = $this->escapeHtml($record['fine'] ?? ''); + $tableData['Fine'][] = $this->transEsc($record['fine'] ?? ''); $tableData['Fine Date'][] = $this->escapeHtml($record['createdate'] ?? ''); $tableData['Fee'][] = isset($record['amount']) ? $this->safeMoneyFormat($record['amount'] / 100.00) : ''; diff --git a/themes/bootstrap3/templates/myresearch/historicloans.phtml b/themes/bootstrap3/templates/myresearch/historicloans.phtml index a2b31aaca596fc10d35d9d7aef4a1f494ae17a08..39ca314dadac0346cc2a37dbb23d97eac4da4102 100644 --- a/themes/bootstrap3/templates/myresearch/historicloans.phtml +++ b/themes/bootstrap3/templates/myresearch/historicloans.phtml @@ -94,12 +94,12 @@ <?php endif; ?> <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> + <strong><?=$this->transEscWithPrefix('location_', $ilsDetails['institution_name'])?></strong> <br /> <?php endif; ?> <?php if (!empty($ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> + <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEscWithPrefix('location_', $ilsDetails['borrowingLocation'])?> <br /> <?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/holds.phtml b/themes/bootstrap3/templates/myresearch/holds.phtml index 4ff14f88ca8e00d279ccb213f1b4899e2f65ff62..ce4c4e834bdab9dd567646824e67f9de78d20b25 100644 --- a/themes/bootstrap3/templates/myresearch/holds.phtml +++ b/themes/bootstrap3/templates/myresearch/holds.phtml @@ -19,7 +19,7 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelHold"> + <form name="cancelForm" class="inline" method="post" id="cancelHold" data-clear-account-cache="holds"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> @@ -131,7 +131,7 @@ <?php endif; ?> <?php if (!empty($pickupDisplay)): ?> <strong><?=$this->transEsc('pick_up_location') ?>:</strong> - <?=$pickupTranslate ? $this->transEsc('location_' . $pickupDisplay, null, $pickupDisplay) : $this->escapeHtml($pickupDisplay)?> + <?=$pickupTranslate ? $this->transEscWithPrefix('location_', $pickupDisplay) : $this->escapeHtml($pickupDisplay)?> <br /> <?php endif; ?> diff --git a/themes/bootstrap3/templates/myresearch/illrequests.phtml b/themes/bootstrap3/templates/myresearch/illrequests.phtml index 8094311a8d526e5e6bf3703a038f4696577fc163..f70651c3a4b8c11bed5b38991c07745141761430 100644 --- a/themes/bootstrap3/templates/myresearch/illrequests.phtml +++ b/themes/bootstrap3/templates/myresearch/illrequests.phtml @@ -20,7 +20,7 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelILLRequest"> + <form name="cancelForm" class="inline" method="post" id="cancelILLRequest" data-clear-account-cache="illRequests"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> diff --git a/themes/bootstrap3/templates/myresearch/menu.phtml b/themes/bootstrap3/templates/myresearch/menu.phtml index 9f84f744c334accfc6809a0362ca76c46ffae465..8b6eadea1b2d11d036c90fd7391ce560a537a696 100644 --- a/themes/bootstrap3/templates/myresearch/menu.phtml +++ b/themes/bootstrap3/templates/myresearch/menu.phtml @@ -2,6 +2,7 @@ $user = $this->auth()->isLoggedIn(); $patron = $user ? $this->auth()->getILSPatron() : false; $capabilityParams = $patron ? ['patron' => $patron] : []; + $ilsOnline = ('ils-none' !== $this->ils()->getOfflineMode()); ?> <button class="close-offcanvas btn btn-link" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button> <h3><?=$this->transEsc('Your Account')?></h3> @@ -11,50 +12,53 @@ <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Favorites')?> </a> <?php endif; ?> - <?php if ('ils-none' !== $this->ils()->getOfflineMode()): ?> - <?php if ($this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-checkedout')?>" class="flex checkedout<?=$this->active == 'checkedout' ? ' active' : ''?>"> - <span class="flex-col"><i class="fa fa-fw fa-book" aria-hidden="true"></i> <?=$this->transEsc('Checked Out Items')?></span> - <span class="checkedout-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - </a> - <?php endif; ?> - <?php if ($this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-history" aria-hidden="true"></i> <?=$this->transEsc('Loan History')?> - </a> - <?php endif; ?> - <?php if ($this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-holds')?>" class="flex<?=$this->active == 'holds' ? ' active' : ''?>"> - <span class="flex-col"><i class="fa fa-fw fa-flag" aria-hidden="true"></i> <?=$this->transEsc('Holds and Recalls')?></span> - <span class="holds-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - </a> - <?php endif; ?> - <?php if ($this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-storageretrievalrequests')?>" class="flex<?=$this->active == 'storageRetrievalRequests' ? ' active' : ''?>"> - <span class="flex-col"><i class="fa fa-fw fa-archive" aria-hidden="true"></i> <?=$this->transEsc('Storage Retrieval Requests')?></span> - <span class="storageretrievalrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - </a> - <?php endif; ?> - <?php if ($this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-illrequests')?>" class="flex<?=$this->active == 'ILLRequests' ? ' active' : ''?>"> - <span class="flex-col"><i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?></span> - <span class="illrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - </a> - <?php endif; ?> - <?php if ($this->ils()->checkCapability('getMyFines', $capabilityParams)): ?> - <a href="<?=$this->url('myresearch-fines')?>" class="flex<?=$this->active == 'fines' ? ' active' : ''?>"> - <span class="flex-col"><i class="fa fa-fw fa-usd" aria-hidden="true"></i> <?=$this->transEsc('Fines')?></span> - <span class="fines-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - </a> - <?php endif; ?> - <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-user" aria-hidden="true"></i> <?=$this->transEsc('Profile')?> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-checkedout')?>" class="flex checkedout<?=$this->active == 'checkedout' ? ' active' : ''?>"> + <span class="flex-col"><i class="fa fa-fw fa-book" aria-hidden="true"></i> <?=$this->transEsc('Checked Out Items')?></span> + <span class="checkedout-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + </a> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-history" aria-hidden="true"></i> <?=$this->transEsc('Loan History')?> + </a> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-holds')?>" class="flex<?=$this->active == 'holds' ? ' active' : ''?>"> + <span class="flex-col"><i class="fa fa-fw fa-flag" aria-hidden="true"></i> <?=$this->transEsc('Holds and Recalls')?></span> + <span class="holds-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + </a> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-storageretrievalrequests')?>" class="flex<?=$this->active == 'storageRetrievalRequests' ? ' active' : ''?>"> + <span class="flex-col"><i class="fa fa-fw fa-archive" aria-hidden="true"></i> <?=$this->transEsc('Storage Retrieval Requests')?></span> + <span class="storageretrievalrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + </a> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-illrequests')?>" class="flex<?=$this->active == 'ILLRequests' ? ' active' : ''?>"> + <span class="flex-col"><i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?></span> + <span class="illrequests-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + </a> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyFines', $capabilityParams)): ?> + <a href="<?=$this->url('myresearch-fines')?>" class="flex<?=$this->active == 'fines' ? ' active' : ''?>"> + <span class="flex-col"><i class="fa fa-fw fa-usd" aria-hidden="true"></i> <?=$this->transEsc('Fines')?></span> + <span class="fines-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + </a> + <?php endif; ?> + <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-user" aria-hidden="true"></i> <?=$this->transEsc('Profile')?> + </a> + <?php if ($ilsOnline && $user && $user->libraryCardsEnabled()): ?> + <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-barcode" aria-hidden="true"></i> <?=$this->transEsc('Library Cards')?> + </a> + <?php endif; ?> + <?php if ($this->overdrive()->showMyContentLink()):?> + <a href="<?=$this->url('overdrive-mycontent')?>"<?=$this->active == 'dgcontent' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-download" aria-hidden="true"></i> <?=$this->transEsc('Overdrive Content')?> </a> - <?php if ($user && $user->libraryCardsEnabled()): ?> - <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active"' : ''?>> - <i class="fa fa-fw fa-barcode" aria-hidden="true"></i> <?=$this->transEsc('Library Cards')?> - </a> - <?php endif; ?> <?php endif; ?> <?php if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> <a href="<?=$this->url('search-history')?>?require_login"<?=$this->active == 'history' ? ' class="active"' : ''?>> diff --git a/themes/bootstrap3/templates/myresearch/odmycontent.phtml b/themes/bootstrap3/templates/myresearch/odmycontent.phtml new file mode 100644 index 0000000000000000000000000000000000000000..04399909d2a83df8048ece82ddf5b7f8c9caa7dc --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/odmycontent.phtml @@ -0,0 +1,152 @@ +<?php + // Set up page title: + $this->headTitle($this->translate('od_content')); + + // Set up breadcrumbs: + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('od_content') . '</li>'; +?> + +<div class="<?= $this->layoutClass('mainbody')?>"> + <h2> + <div class="odbrand pull-left"> + <img class="media-left" src="https://developerportaldev.blob.core.windows.net/media/Default/images/newLogos/OverDrive_Logo_28x28_rgb.jpg"> + </div> + <?= $this->transEsc('od_content')?></h2> + <p class="od_help"><i class="fa fa-fw fa-question-circle" aria-hidden="true"></i> + <?= $this->translate('od_mycontent_help', ['%%url%%' => $this->config()->get('Overdrive')->Overdrive->ODHelpLink]); ?></p> + + <?= $this->flashmessages()?> + + <h3><?= $this->transEsc('od_loans')?></h3> + + <?php if($this->checkoutsUnavailable): ?> + <div class="well"> <?= $this->transEsc('od_info_unavail')?></div> + <?php elseif(count($this->checkouts) == 0): ?> + <div class="well"> <?= $this->transEsc('od_none_found')?></div> + <?php else: + $i = 0; foreach ($this->checkouts as $resource): + $od_id = $resource['record']->getOverdriveID(); + $rec_id = $resource['record']->getUniqueID(); + $coverDetails = $this->record($resource['record'])->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource['record'])); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($resource['record'])->getThumbnailAlignment('account'); + ?> + <div id="record<?=$rec_id?>" class="result"> + <?php + if ($cover): + ob_start(); ?> + <div class="media-<?= $thumbnailAlignment ?> <?= $this->escapeHtmlAttr($coverDetails['size'])?>"> + <?= $cover ?> + </div> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> + <div class="media"> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?= $thumbnail ?> + <?php endif ?> + <div class="media-body"> + + <h4 property="name"> + <a href="<?= $this->recordLink()->getUrl($resource['record'])?>" class="title"> + <?= $this->escapeHtml($resource['record']->getTitle() . ' ' . $resource['record']->getSubtitle() . ' ' . $resource['record']->getTitleSection())?> + </a> + </h4> + <?php $listAuthors = $resource['record']->getPrimaryAuthors(); + if (!empty($listAuthors)): ?> + <?= $this->transEsc('by')?>: + <a href="<?= $this->record($resource['record'])->getLink('author', $listAuthors[0])?>"><?= $this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?= $this->transEsc('more_authors_abbrev')?> + <?php endif; ?><br/> + <?php endif; ?> + <div><?= $this->transEsc('od_expires_on', ['%%due_date%%' => $resource['checkout']->expires])?></div> + + <div class="pull-right"> + <?php if($resource['checkout']->isReturnable): ?> + <a class="btn btn-primary returnTitle" + data-lightbox title="<?= $this->transEsc('od_but_return')?>" + href="<?= $this->url('overdrive-hold') . "?od_id=$od_id&rec_id=$rec_id&action=returnTitleConfirm"; ?>"><i class="fa fa arrow-to-right " aria-hidden="true"></i> + <?= $this->transEsc('od_but_return')?></a> + <?php endif;?> + <a class="btn btn-primary getTitle" + data-lightbox title="<?= $this->transEsc('od_but_gettitle')?>" + href="<?= $this->url('overdrive-hold') . "?od_id=$od_id&rec_id=$rec_id&action=getTitleConfirm"; ?>"><i class="fa fa-fw fa-download" aria-hidden="true"></i> + <?= $this->transEsc('od_but_gettitle')?></a> + + </div> + </div> + </div> + + </div> + + <?php endforeach;?> + <?php endif;?> + + + <h3><?= $this->transEsc('od_holds')?></h3> + <?php if($this->holdsUnavailable): ?> + <div class="well"> <?= $this->transEsc('od_info_unavail')?></div> + <?php elseif(count($this->holds) == 0): ?> + <div class="well"> <?= $this->transEsc('od_none_found')?></div> + <?php else: + $i = 0; foreach ($this->holds as $resource): + $od_id = $resource['record']->getOverdriveID(); + $rec_id = $resource['record']->getUniqueID(); + $coverDetails = $this->record($resource['record'])->getCoverDetails('checkedout', 'small', $this->recordLink()->getUrl($resource['record'])); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($resource['record'])->getThumbnailAlignment('account'); + ?> + <div id="record<?=$rec_id?>" class="result"> + <?php + if ($cover): + ob_start(); ?> + <div class="media-<?= $thumbnailAlignment ?> <?= $this->escapeHtmlAttr($coverDetails['size'])?>"> + <?= $cover ?> + </div> + <?php $thumbnail = ob_get_contents(); ?> + <?php ob_end_clean(); ?> + <?php endif; ?> + <div class="media"> + <?php if ($thumbnail && $thumbnailAlignment == 'left'): ?> + <?= $thumbnail ?> + <?php endif ?> + <div class="media-body"> + <h4 property="name"> + <a href="<?= $this->recordLink()->getUrl($resource['record'])?>" class="title"> + <?= $this->escapeHtml($resource['record']->getTitle() . ' ' . $resource['record']->getSubtitle() . ' ' . $resource['record']->getTitleSection())?> + </a> + </h4> + <?php $listAuthors = $resource['record']->getPrimaryAuthors(); + if (!empty($listAuthors)): ?> + <?= $this->transEsc('by')?>: + <a href="<?= $this->record($resource['record'])->getLink('author', $listAuthors[0])?>"><?= $this->escapeHtml($listAuthors[0])?></a><?php if (count($listAuthors) > 1): ?>, <?= $this->transEsc('more_authors_abbrev')?> + <?php endif; ?><br/> + <?php endif; ?> + + <?= $this->transEsc('od_hold_placed_on', ['%%holdPlacedDate%%' => $resource['hold']->holdPlacedDate]);?> + <?= $this->transEsc('od_hold_queue', ['%%holdPosition%%' => $resource['hold']->holdListPosition, '%%numberOfHolds%%' => $resource['hold']->numberOfHolds]);?> + <?= $this->transEsc('od_hold_email', ['%%holdEmailAddress%%' => $resource['hold']->emailAddress]);?> + + <div class="pull-right"> + <a class="btn btn-primary placehold" + data-lightbox title="<?= $this->transEsc('request_place_text')?>" + href="<?= $this->url('overdrive-hold') . "?od_id=$od_id&rec_id=$rec_id&action=cancelHoldConfirm"; ?>"><i class="fa fa-minus-circle" aria-hidden="true"></i> + <?= $this->transEsc('od_but_cancel_hold')?></a> + </div> + </div> + </div> + </div> + <?php endforeach;?> + <?php endif;?> +<!-- + <h3><?= $this->transEsc('od_history')?></h3> + <div class="alert alert-info"><i class='fa fa-exclamation-triangle'></i> + Coming Soon. + </div> +--> +</div> + +<div class="<?= $this->layoutClass('sidebar')?>"> + <?= $this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'dgcontent'])?> +</div> diff --git a/themes/bootstrap3/templates/myresearch/profile.phtml b/themes/bootstrap3/templates/myresearch/profile.phtml index e34a4d6c2726ab8a7728cd554549db06cfa46676..01d60b7a0cc6782f4bed7f1c56e3386d0d4fb331 100644 --- a/themes/bootstrap3/templates/myresearch/profile.phtml +++ b/themes/bootstrap3/templates/myresearch/profile.phtml @@ -9,7 +9,7 @@ $arrTemplate = '<tr><th>%%LABEL%%:</th><td> %%VALUE%%</td></tr>'; ?> -<a class="search-filter-toggle visible-xs" href="#account-sidebar" data-toggle="offcanvas" title="Expand Sidebar"> +<a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" title="Expand Sidebar"> <?=$this->transEsc('Your Account') ?> </a> @@ -26,26 +26,39 @@ $this->transEsc('Email') => 'email', ] )?> - <?php if (count($this->pickup ?? []) > 1): // Skip form if only one location: ?> - <tr><th><?=$this->transEsc('Preferred Library')?>:</th> - <?php - $selected = (strlen($this->profile['home_library'] ?? '') > 0) - ? $this->profile['home_library'] : $this->defaultPickupLocation - ?> - <td> - <form id="profile_form" class="form-inline" method="post"> - <select id="home_library" name="home_library" class="form-control"> - <?php foreach ($this->pickup as $lib): ?> - <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID'])?' selected="selected"':''?>><?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?></option> - <?php endforeach; ?> - </select> - <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Save')?>" /> - </form> - </td> + <?php /* this section renders under two distinct circumstances; see if/else below: */ ?> + <?php if (count($this->pickup ?? []) > 1 || !empty($this->preferredLibraryDisplay)): ?> + <tr> + <th><?=$this->transEsc('Preferred Library')?>:</th> + <td> + <?php if (count($this->pickup ?? []) > 1): // case 1: set home library allowed ?> + <?php + $selected = (strlen($this->profile['home_library'] ?? '') > 0) + ? $this->profile['home_library'] : $this->defaultPickupLocation + ?> + <form id="profile_form" class="form-inline" method="post"> + <select id="home_library" name="home_library" class="form-control"> + <?php foreach ($this->pickup as $lib): ?> + <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID'])?' selected="selected"':''?>><?=$this->transEscWithPrefix('location_', $lib['locationDisplay'])?></option> + <?php endforeach; ?> + </select> + <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Save')?>" /> + </form> + <?php else: // case 2: set home library disallowed, but default provided by ILS ?> + <?=$this->transEscWithPrefix('location_', $this->preferredLibraryDisplay)?> + <?php endif; ?> + </td> + </tr> <?php endif; ?> </table> <div id="account-actions"> + <?php if ($this->auth()->getManager()->supportsEmailChange()): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-changeemail') ?>"> + <i class="fa fa-fw fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Change Email Address') ?> + </a> + <?php endif; ?> + <?php if ($this->auth()->getManager()->supportsPasswordChange()): ?> <a class="btn btn-default" href="<?=$this->url('myresearch-changepassword') ?>"> <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?=$this->transEsc('Change Password') ?> diff --git a/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml b/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml index 513dc4d5e81f632ae99ba8585bea629a2cd3aa1c..c90ece34e4011a3d3d5e3479fe494ae613bd646b 100644 --- a/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml +++ b/themes/bootstrap3/templates/myresearch/storageretrievalrequests.phtml @@ -19,7 +19,7 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest"> + <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest" data-clear-account-cache="storageRetrievalRequests"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> diff --git a/themes/bootstrap3/templates/myresearch/unsubscribe.phtml b/themes/bootstrap3/templates/myresearch/unsubscribe.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d0516f8c4bea33e9e57c0d2ff65365700205ed00 --- /dev/null +++ b/themes/bootstrap3/templates/myresearch/unsubscribe.phtml @@ -0,0 +1,17 @@ +<?php + // Set up page title: + $this->headTitle($this->translate('unsubscribe_confirmation')); +?> +<div class="<?=$this->layoutClass('mainbody')?>"> + <div class="content unsubscribe"> + <?php if ($this->success == true): ?> + <p><?=$this->translate('unsubscribe_successful'); ?></p> + <?php else: ?> + <p><?= $this->translate('unsubscribe_confirmation'); ?></p> + <p> + <a class="btn btn-primary" href="<?= $this->escapeHtml($this->unsubscribeUrl); ?>"><?= $this->translate('confirm_dialog_yes'); ?></a> + <a class="btn btn-primary" href="<?= $this->url('home'); ?>"><?= $this->translate('confirm_dialog_no'); ?></a> + </p> + <?php endif; ?> + </div> +</div> \ No newline at end of file diff --git a/themes/bootstrap3/templates/record/cart-buttons.phtml b/themes/bootstrap3/templates/record/cart-buttons.phtml index b153271bcefdb872b8edf304059444315f71c44c..81826f741d05e0f9bcf7fe5fc12340b7dde12359 100644 --- a/themes/bootstrap3/templates/record/cart-buttons.phtml +++ b/themes/bootstrap3/templates/record/cart-buttons.phtml @@ -3,12 +3,10 @@ <?php $cartId = $this->source . '|' . $this->id; ?> <span class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>"> <a href="#" class="cart-add hidden<?php if(!$cart->contains($cartId)): ?> correct<?php endif ?>"> - <i class="cart-link-icon fa fa-plus" title="<?=$this->transEsc('Add to Book Bag') ?>"></i><!-- - --><span class="cart-link-label"><?=$this->transEsc('Add to Book Bag') ?></span><!-- - --></a> + <span class="cart-link-label btn-type-add"><?=$this->transEsc('Add to Book Bag') ?></span> + </a> <a href="#" class="cart-remove hidden<?php if($cart->contains($cartId)): ?> correct<?php endif ?>"> - <i class="cart-link-icon fa fa-minus-circle" title="<?=$this->transEsc('Remove from Book Bag') ?>"></i><!-- - --><span class="cart-link-label"><?=$this->transEsc('Remove from Book Bag') ?></span> + <span class="cart-link-label btn-type-minus"><?=$this->transEsc('Remove from Book Bag') ?></span> </a> <noscript> <form method="post" name="addForm" action="<?=$this->url('cart-processor')?>"> diff --git a/themes/bootstrap3/templates/record/hold.phtml b/themes/bootstrap3/templates/record/hold.phtml index e47ba5a96e8222100a518265a05d61ffc00e7df3..d803cad42402aad3ba3a852442cba749a793d21a 100644 --- a/themes/bootstrap3/templates/record/hold.phtml +++ b/themes/bootstrap3/templates/record/hold.phtml @@ -12,7 +12,7 @@ <p class="helptext"><?=$this->helpText?></p> <?php endif; ?> -<form class="form-record-hold" method="post" name="placeHold"> +<form class="form-record-hold" method="post" name="placeHold" data-clear-account-cache="holds"> <?=$this->flashmessages()?> <label class="control-label"><?=$this->transEsc('Title')?></label> <p class="form-control-static"><?=$this->driver->getBreadcrumb() ?></p> @@ -92,7 +92,7 @@ <?php endif; ?> <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID']) ? ' selected="selected"' : ''?>> - <?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?> + <?=$this->transEscWithPrefix('location_', $lib['locationDisplay'])?> </option> <?php endforeach; ?> </select> diff --git a/themes/bootstrap3/templates/record/illrequest.phtml b/themes/bootstrap3/templates/record/illrequest.phtml index 9fac505e2561f6f192c2ded2fc5acba5d92b808c..0108f11266e69145f1907b5271017d77eef037c1 100644 --- a/themes/bootstrap3/templates/record/illrequest.phtml +++ b/themes/bootstrap3/templates/record/illrequest.phtml @@ -12,7 +12,7 @@ <p class="help-block"><?=$this->helpText?></p> <?php endif; ?> -<form id="ILLRequestForm" name="placeILLRequest" class="form-ill-retrieval-request" method="post"> +<form id="ILLRequestForm" name="placeILLRequest" class="form-ill-retrieval-request" method="post" data-clear-account-cache="illRequests"> <?=$this->flashmessages()?> <?php if (in_array("itemId", $this->extraFields)): ?> <div class="form-group"> diff --git a/themes/bootstrap3/templates/record/storageretrievalrequest.phtml b/themes/bootstrap3/templates/record/storageretrievalrequest.phtml index f6aee9a841d8303b35626c8f288885eb16842de5..c4543b993c328797bcaeea2c14e4f67f940efb9b 100644 --- a/themes/bootstrap3/templates/record/storageretrievalrequest.phtml +++ b/themes/bootstrap3/templates/record/storageretrievalrequest.phtml @@ -12,7 +12,7 @@ <p class="helptext"><?=$this->helpText?></p> <?php endif; ?> -<form name="placeStorageRetrievalRequest" class="form-storage-retrieval-request" method="post"> +<form name="placeStorageRetrievalRequest" class="form-storage-retrieval-request" method="post" data-clear-account-cache="storageRetrievalRequests"> <?=$this->flashmessages()?> <?php if (in_array("item-issue", $this->extraFields)): ?> <div class="form-group controls"> @@ -71,7 +71,7 @@ <?php endif; ?> <?php foreach ($this->pickup as $lib): ?> <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID']) ? ' selected="selected"' : ''?>> - <?=$this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay'])?> + <?=$this->transEscWithPrefix('location_', $lib['locationDisplay'])?> </option> <?php endforeach; ?> </select> diff --git a/themes/bootstrap3/templates/record/view.phtml b/themes/bootstrap3/templates/record/view.phtml index 9d23d1a282f2dc2f763049040931b9fd14bbb264..efaf45381562549597bdb0e28ee366ae6eccfd4d 100644 --- a/themes/bootstrap3/templates/record/view.phtml +++ b/themes/bootstrap3/templates/record/view.phtml @@ -52,8 +52,8 @@ if (!$obj->isVisible()) { $tabClasses[] = 'hidden'; } if (!$obj->supportsAjax()) { $tabClasses[] = 'noajax'; } ?> - <li class="<?=implode(' ', $tabClasses)?>" data-tab="<?=$tabName?>"> - <a href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>><?=$this->transEsc($desc)?></a> + <li class="<?=implode(' ', $tabClasses)?>" data-tab="<?=$tabName?>"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>> + <a href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"><?=$this->transEsc($desc)?></a> </li> <?php endforeach; ?> </ul> diff --git a/themes/bootstrap3/templates/search/advanced/ranges.phtml b/themes/bootstrap3/templates/search/advanced/ranges.phtml index 7f51cb4226234625d646db30d61487101544606b..8e3de488cb05f6c60271862f7e1f6e042f31f62c 100644 --- a/themes/bootstrap3/templates/search/advanced/ranges.phtml +++ b/themes/bootstrap3/templates/search/advanced/ranges.phtml @@ -1,5 +1,5 @@ <?php if (isset($this->ranges) && !empty($this->ranges)): ?> - <?php $params = $this->searchParams($this->searchClassId); $params->activateAllFacets(); ?> + <?php $params = $this->searchParams($this->searchClassId); ?> <?php foreach ($this->ranges as $current): $escField = $this->escapeHtmlAttr($current['field']); ?> <?php $extraInputAttribs = ($current['type'] == 'date') ? 'maxlength="4" ' : ''; ?> <fieldset class="range"> diff --git a/themes/bootstrap3/templates/search/bulk-action-buttons.phtml b/themes/bootstrap3/templates/search/bulk-action-buttons.phtml index 5955b7d278267de0c6bc04b5d0f34a6ed4d26da2..1cab4b444edf1cc47e97c6cb7daa17262a8722b8 100644 --- a/themes/bootstrap3/templates/search/bulk-action-buttons.phtml +++ b/themes/bootstrap3/templates/search/bulk-action-buttons.phtml @@ -1,5 +1,5 @@ <?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> - <div class="bulkActionButtons hidden-print"> + <nav class="bulkActionButtons"> <div class="bulk-checkbox"> <input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> <label for="<?=$this->idPrefix?>addFormCheckboxSelectAll"> @@ -7,20 +7,20 @@ | <?=$this->transEsc('with_selected')?>: </label> </div> - <div class="btn-group"> + <ul class="action-toolbar"> <?php if (isset($this->showBulkOptions) && $this->showBulkOptions): ?> - <input id="ribbon-email" class="btn btn-default" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> - <?php $exportOptions = $this->export()->getBulkOptions(); if (count($exportOptions) > 0): ?> - <input id="ribbon-export" class="btn btn-default" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li><button id="ribbon-email" class="toolbar-btn btn-type-email" type="submit" name="email" value="1" title="<?=$this->transEsc('bookbag_email_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Email')?></button></li> + <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> + <li><button id="ribbon-export" class="toolbar-btn btn-type-export" type="submit" name="export" value="1" title="<?=$this->transEsc('bookbag_export_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Export')?></button></li> <?php endif; ?> - <input id="ribbon-print" class="btn btn-default" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li><button id="ribbon-print" class="toolbar-btn btn-type-print" type="submit" name="print" value="1" title="<?=$this->transEsc('bookbag_print_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Print')?></button></li> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> - <input id="ribbon-save" class="btn btn-default" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li><button id="ribbon-save" class="toolbar-btn btn-type-save" type="submit" name="saveCart" value="1" title="<?=$this->transEsc('bookbag_save_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Save')?></button></li> <?php endif; ?> <?php endif; ?> <?php if (isset($this->showCartControls) && $this->showCartControls): ?> - <input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-default" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li><button id="<?=$this->idPrefix?>updateCart" type="submit" class="toolbar-btn btn-type-add" name="add" value="1"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Add to Book Bag')?></button></li> <?php endif; ?> - </div> - </div> + </ul> + </nav> <?php endif; ?> diff --git a/themes/bootstrap3/templates/search/controls/limit.phtml b/themes/bootstrap3/templates/search/controls/limit.phtml index b322fbac571f6592be8ea5f3d90f9253085740d9..7e4a01191a8224f0c0100bc3d96fc3f9ccb409c9 100644 --- a/themes/bootstrap3/templates/search/controls/limit.phtml +++ b/themes/bootstrap3/templates/search/controls/limit.phtml @@ -1,6 +1,6 @@ <?php $limitList = $this->params->getLimitList(); ?> <?php if (count($limitList) > 1): ?> - <form class="form-inline" action="<?=$this->currentPath() . $this->results->getUrlQuery()->setLimit(null)?>" method="post"> + <form class="form-inline search-result-limit" action="<?=$this->currentPath() . $this->results->getUrlQuery()->setLimit(null)?>" method="post"> <label for="limit"><?=$this->transEsc('Results per page')?></label> <select id="limit" name="limit" class="jumpMenu form-control"> <?php foreach ($limitList as $limitVal => $limitData): ?> diff --git a/themes/bootstrap3/templates/search/filters.phtml b/themes/bootstrap3/templates/search/filters.phtml new file mode 100644 index 0000000000000000000000000000000000000000..dad6016842355b4a8f49d81a362c14ed17be26be --- /dev/null +++ b/themes/bootstrap3/templates/search/filters.phtml @@ -0,0 +1,150 @@ +<?php + if (!isset($this->params)) { + // No current search, use last search in memory + $params = $this->searchMemory()->getLastSearchParams($this->searchClassId); + } else { + // clone params so that the manipulation doesn't cause trouble e.g. for facets + $params = clone $this->params; + } + + $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); + $options = $this->searchOptions($this->searchClassId); + $hasDefaultsApplied = $params->hasDefaultsApplied(); + $filterCount = $this->searchbox()->getFilterCount($checkboxFilters, $filterList); + + // Determine whether the only filters applied are the default ones; this controls + // when we display or hide the reset button: + $defaultFilters = $options->getDefaultFilters(); + $onlyDefaultsApplied = count($defaultFilters) == $filterCount; + foreach ($defaultFilters as $currentDefault) { + if (!$params->hasFilter($currentDefault)) { + $onlyDefaultsApplied = false; + break; + } + } + + $advancedSearch = $this->searchType === 'advanced'; +?> +<?php ob_start(); ?> + <?php foreach ($checkboxFilters as $filter): ?> + <?php if ($filter['selected']): ?> + <span class="filter-value"> + <?php + $removeLink = isset($urlQuery) + ? $urlQuery->removeFilter($filter['filter']) + : $this->searchMemory()->getEditLink( + $this->searchClassId, 'removeFilter', $filter['filter'] + ); + $desc = $this->translate($filter['desc']); + $ariaLabel = $this->translate('Remove filter') . ' ' . $this->escapeHtmlAttr($desc); + ?> + <?=$desc?> + <?php if($removeLink): ?> + <a class="search-filter-remove" aria-label="<?=$ariaLabel?>" href="<?=$removeLink?>"><!--icon from css --></a> + <?php endif; ?> + </span> + <?php endif ?> + <?php endforeach; ?> + + <?php foreach ($filterList as $field => $data): ?> + <div class="title-value-pair"> + <span class="filters-title"><?=$this->transEsc($field)?>:</span> + <?php if (count($data) > 3): ?> + <div class="search-filter-dropdown dropdown"> + <?php $dropdown = true; ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $field); ?> + <button id="dropdown-toggle-<?=$safeId?>" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"> + <?=$this->transEsc('filter_toggle_entries', ['%%count%%' => count($data)])?> + </button> + <ul class="dropdown-menu" role="menu" aria-labelledby="dropdown-toggle-<?=$safeId?>"> + <?php else: ?> + <?php $dropdown = false; ?> + <?php endif; ?> + <?php foreach ($data as $index => $value): ?> + <?php if ($dropdown): ?> + <li> + <?php endif; ?> + <?php + switch ($value['operator']) { + case 'NOT': + $operatorChar = '-'; + $join = $this->transEsc('NOT'); + break; + case 'OR': + $operatorChar = '~'; + $join = $this->transEsc('OR'); + break; + case 'AND': + $operatorChar = ''; + $join = $this->transEsc('AND'); + break; + default: + $operatorChar = $join = ''; + } + $operatorClass = $this->escapeHtmlAttr(strtolower($value['operator'])); + ?> + <span class="filters-term filters-<?=$operatorClass?>"> + <?=($index > 0 || 'NOT' === $value['operator']) ? $join : '' ?> + </span> + <span class="filter-value filters-<?=$operatorClass?>"> + <span class="text"> + <?=$this->escapeHtml($value['displayText'])?> + </span> + <?php + $removeLink = isset($this->urlQuery) + ? $urlQuery->removeFacet($value['field'], $value['value'], $value['operator']) + : $this->searchMemory()->getEditLink($this->searchClassId, 'removeFacet', $value); + ?> + <a class="search-filter-remove" aria-label="<?=$this->translate('Remove filter') ?>" href="<?=$removeLink?>"><!--icon from css --></a> + </span> + <?php if ($dropdown): ?> + </li> + <?php endif; ?> + <?php endforeach; ?> + <?php if ($dropdown): ?> + </ul> + </div> + <?php endif; ?> + </div> + <?php endforeach; ?> +<?php $filters = ob_get_contents(); ?> +<?php ob_end_clean(); ?> + +<?php if ($hasDefaultsApplied || $filterCount > 0): ?> + <?php + $resetLink = null; + if (!$onlyDefaultsApplied) { + $resetLink = isset($urlQuery) + ? $urlQuery->removeAllFilters()->resetDefaultFilters() + : $this->searchMemory()->getEditLink($this->searchClassId, 'removeAllFilters', 1); + } + ?> + <?php // Normal view ?> + <div class="active-filters hidden-xs"> + <?php if ($resetLink && $options->getRetainFilterSetting()): ?> + <a class="reset-filters-btn" href="<?=$resetLink?>"><?=$this->transEsc('reset_filters_button')?></a> + <?php elseif ($advancedSearch): ?> + <p class="adv_search_filters"><?=$this->transEsc('adv_search_filters')?>:</p> + <?php endif; ?> + <div class="filters"> + <?=$filters ?> + </div> + <div class="clearfix"></div> + </div> + <?php // Narrow view ?> + <div class="active-filters visible-xs"> + <div class="filters-toggle-bar"> + <?php if ($resetLink && $options->getRetainFilterSetting()): ?> + <a class="reset-filters-btn" href="<?=$resetLink?>"><?=$this->transEsc('reset_filters_button')?></a> + <?php endif; ?> + <div class="filters-toggle collapsed" data-toggle="collapse" data-target="#active-filters-mobile"> + <?=$this->transEsc('show_filters_html', ['%%count%%' => $filterCount])?> + </div> + <div class="clearfix"></div> + </div> + <div id="active-filters-mobile" class="filters filters-bar collapse"> + <?=$filters ?> + </div> + <div class="clearfix"></div> + </div> +<?php endif; ?> diff --git a/themes/bootstrap3/templates/search/history-table.phtml b/themes/bootstrap3/templates/search/history-table.phtml index fcb620133173d446cbbfbb605a867dcd4dfd75cd..39767f9e56dcb9bcd6a2a06e78033874d5a2bfe5 100644 --- a/themes/bootstrap3/templates/search/history-table.phtml +++ b/themes/bootstrap3/templates/search/history-table.phtml @@ -1,10 +1,14 @@ -<?php $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> -<table class="table table-striped"> +<?php + $scheduleSupported = !empty($this->schedule); + $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; +?> +<table class="search-history-table" id="<?=$this->showSaved ? 'saved-searches' : 'recent-searches'?>"> <tr> <th width="20%"><?=$this->transEsc("history_time")?></th> <th><?=$this->transEsc("history_search")?></th> <th><?=$this->transEsc("history_limits")?></th> <th><?=$this->transEsc("history_results")?></th> + <?php if ($scheduleSupported): ?><th class="search-schedule-header"><?=$this->transEsc('history_schedule')?></th><?php endif; ?> <?php if ($saveSupported): ?><th><?=$this->transEsc($this->showSaved ? "history_delete" : "history_save")?></th><?php endif; ?> </tr> <?php foreach (($this->showSaved ? array_reverse($this->saved) : array_reverse($this->unsaved)) as $iteration => $info): ?> @@ -18,7 +22,7 @@ ?></a> </td> <td> - <?php $info->getParams()->activateAllFacets(); foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> + <?php foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> <?php foreach ($filters as $i => $filter): ?> <?php if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?> <strong><?=$this->transEsc($field)?></strong>: <?=$this->escapeHtml($filter['displayText'])?><br/> @@ -31,10 +35,27 @@ <?php endforeach; ?> </td> <td><?=$this->escapeHtml($this->localizedNumber($info->getResultTotal()))?></td> + <?php if ($scheduleSupported): ?> + <td> + <?php if (isset($this->schedule[$info->getSearchId()])): ?> + <?php $schedule = $this->schedule[$info->getSearchId()]; ?> + <form class="form-inline jumpMenuForm" action="<?= $this->url('myresearch-savesearch')?>" method="get" name="schedule"> + <select name="schedule" class="jumpMenu form-control" aria-haspopup="true" title="<?=$this->transEsc("history_schedule")?>"> + <?php foreach ($scheduleOptions as $scheduleValue => $scheduleLabel): ?> + <option value="<?=$this->escapeHtmlAttr($scheduleValue)?>"<?=($schedule == $scheduleValue) ? (' selected') : ('')?>><?=$this->transEsc($scheduleLabel)?></option> + <?php endforeach; ?> + </select> + <input type="hidden" name="searchid" value="<?= $info->getSearchId(); ?>"/> + </form> + <?php else: ?> + <span class="disable"><?=$this->transEsc("cannot set")?></span> + <?php endif; ?> + </td> + <?php endif; ?> <?php if ($saveSupported): ?> <td> <?php if ($this->showSaved): ?> - <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($info->getSearchId())?>&mode=history"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_delete_link")?></a> + <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($info->getSearchId())?>&mode=history" class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_delete_link")?></a> <?php else: ?> <a href="<?=$this->url('myresearch-savesearch')?>?save=<?=urlencode($info->getSearchId())?>&mode=history"><i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc("history_save_link")?></a> <?php endif; ?> diff --git a/themes/bootstrap3/templates/search/history.phtml b/themes/bootstrap3/templates/search/history.phtml index a0ca86a36b06b8be3315546a6dcb5105a29e79e6..5c6f790a4cf950b9a0cb090414443161c15100f9 100644 --- a/themes/bootstrap3/templates/search/history.phtml +++ b/themes/bootstrap3/templates/search/history.phtml @@ -9,11 +9,23 @@ $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> -<a class="search-filter-toggle visible-xs" href="#account-sidebar" data-toggle="offcanvas" title="Expand Sidebar"> +<a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" title="Expand Sidebar"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php if (!empty($this->alertemail)): ?> + <div class="alert alert-info alert-email-notification"> + <?=$this->transEsc("alert_email_address") . ': ' . $this->alertemail ?> + <?php if ($this->auth()->getManager()->supportsEmailChange()): ?> + (<a href="<?=$this->url('myresearch-profile');?>"><?=$this->transEsc("edit");?></a>) + <?php endif; ?> + </div> + <?php elseif (!empty($this->schedule) && $this->auth()->isLoggedIn()): ?> + <div class="alert alert-danger alert-email-notification"> + <?=$this->transEsc("no_email_address") . ' ';?><a href="<?=$this->url('myresearch-profile');?>"><?=$this->transEsc("check_profile");?></a> + </div> + <?php endif; ?> <?=$this->flashmessages()?> <?php if ($saveSupported && !empty($this->saved)): ?> <h2><?=$this->transEsc("history_saved_searches")?></h2> @@ -39,3 +51,16 @@ ?> </div> <?php endif; ?> + +<?php + $explanation = $this->transEsc('schedule_explanation'); + $script = <<<JS +$(".search-schedule-header").popover({ + content: "$explanation", + placement: "top", + trigger: "hover", + container: "body", +}) +JS; +?> +<?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET') ?> diff --git a/themes/bootstrap3/templates/search/reservesresults.phtml b/themes/bootstrap3/templates/search/reservesresults.phtml index ed683a0c0d01ca80d2bb59d07c98ccd58bca1f9b..d007c4f582e3936f8a4f6b45f7c9d081128d96da 100644 --- a/themes/bootstrap3/templates/search/reservesresults.phtml +++ b/themes/bootstrap3/templates/search/reservesresults.phtml @@ -2,18 +2,15 @@ // Set some overrides, then call the standard search results action: $this->overrideTitle = $this->translate('Reserves Search Results'); $this->overrideSearchHeading = $this->transEsc('Reserves'); - if (isset($this->instructor) || isset($this->course)) { - $this->overrideSearchHeading .= ' ('; - if (isset($this->instructor)) { - $this->overrideSearchHeading .= $this->transEsc('Instructor') . ': <strong>' . $this->escapeHtml($this->instructor) . '</strong>'; - if (isset($this->course)) { - $this->overrideSearchHeading .= ', '; - } - } - if (isset($this->course)) { - $this->overrideSearchHeading .= $this->transEsc('Course') . ': <strong>' . $this->escapeHtml($this->course) . '</strong>'; - } - $this->overrideSearchHeading .= ')'; + $headingParts = []; + if (isset($this->instructor)) { + $headingParts[] = $this->transEsc('Instructor') . ': <strong>' . $this->escapeHtml($this->instructor) . '</strong>'; + } + if (isset($this->course)) { + $headingParts[] = $this->transEsc('Course') . ': <strong>' . $this->escapeHtml($this->course) . '</strong>'; + } + if (!empty($headingParts)) { + $this->overrideSearchHeading .= ' (' . implode(', ', $headingParts) . ')'; } $this->overrideEmptyMessage = $this->transEsc('course_reserves_empty_list'); echo $this->render('search/results.phtml'); diff --git a/themes/bootstrap3/templates/search/results.phtml b/themes/bootstrap3/templates/search/results.phtml index 5f6f4be0df1e59b0a4d3fd7181d2ed4a580c9244..bec5c3c6dbe116f086f0167e5ccb26c45e37bdc6 100644 --- a/themes/bootstrap3/templates/search/results.phtml +++ b/themes/bootstrap3/templates/search/results.phtml @@ -17,7 +17,7 @@ 'searchId' => $this->results->getSearchId(), 'searchClassId' => $this->params->getSearchClassId(), 'checkboxFilters' => $this->params->getCheckboxFacets(), - 'filterList' => $this->params->getFilters(), + 'filterList' => $this->params->getFilterList(true), 'hasDefaultsApplied' => $this->params->hasDefaultsApplied(), 'selectedShards' => $this->params->getSelectedShards(), 'ignoreHiddenFiltersInRequest' => isset($this->ignoreHiddenFiltersInRequest) ? $this->ignoreHiddenFiltersInRequest : false, diff --git a/themes/bootstrap3/templates/search/searchbox.phtml b/themes/bootstrap3/templates/search/searchbox.phtml index f0f9470c95c71cf6cf337b675a3728bc3d9055a6..1a03f5753dc5181447eb507cd61acc9a12dcf803 100644 --- a/themes/bootstrap3/templates/search/searchbox.phtml +++ b/themes/bootstrap3/templates/search/searchbox.phtml @@ -1,14 +1,14 @@ <?php // Set default value if necessary: if (!isset($this->searchClassId)) { - $this->searchClassId = 'Solr'; + $config = $this->config()->get('config'); + $this->searchClassId = $config->Site->defaultSearchBackend ?? 'Solr'; } // Load search actions and settings (if any): $options = $this->searchOptions($this->searchClassId); $handlers = $this->searchbox()->getHandlers( - $this->searchClassId, - isset($this->searchIndex) ? $this->searchIndex : null + $this->searchClassId, $this->searchIndex ?? null ); $handlerCount = count($handlers); $basicSearch = $this->searchbox()->combinedHandlersActive() ? 'combined-searchbox' : $options->getSearchAction(); @@ -16,8 +16,8 @@ $advSearch = $options->getAdvancedSearchAction(); $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); $lastLimit = $this->searchMemory()->getLastLimit($this->searchClassId); - $ignoreHiddenFilterMemory = isset($this->ignoreHiddenFilterMemory) && $this->ignoreHiddenFilterMemory; - $ignoreHiddenFiltersInRequest = isset($this->ignoreHiddenFiltersInRequest) && $this->ignoreHiddenFiltersInRequest; + $ignoreHiddenFilterMemory = $this->ignoreHiddenFilterMemory ?? false; + $ignoreHiddenFiltersInRequest = $this->ignoreHiddenFiltersInRequest ?? false; $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, $ignoreHiddenFilterMemory, $ignoreHiddenFiltersInRequest); if (empty($hiddenFilters) && !$ignoreHiddenFilterMemory) { $hiddenFilters = $this->searchMemory()->getLastHiddenFilters($this->searchClassId); @@ -26,6 +26,17 @@ } } $hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->searchClassId, $ignoreHiddenFilterMemory, '?'); + + if (!isset($this->filterList) || !isset($this->checkboxFilters)) { + $params = $this->searchMemory()->getLastSearchParams($this->searchClassId); + $filterList = $params->getFilterList(true); + $checkboxFilters = $params->getCheckboxFacets(); + } else { + $filterList = is_array($this->filterList) ? $this->filterList : []; + $checkboxFilters = is_array($this->checkboxFilters) ? $this->checkboxFilters : []; + } + $filterDetails = $this->searchbox()->getFilterDetails($filterList, $checkboxFilters); + $showFilters = $filterDetails && (isset($results) || $options->getRetainFilterSetting()); ?> <?php $tabConfig = $this->searchTabs()->getTabConfig($this->searchClassId, $this->lookfor, $this->searchIndex, $this->searchType, $hiddenFilters); ?> <?php if ($this->searchType == 'advanced'): ?> @@ -38,6 +49,17 @@ <a href="<?=$this->url($advSearch) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Advanced Search")?></a> | <a href="<?=$this->url($searchHome) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Basic Search")?></a> </p> + <?=$this->context($this)->renderInContext( + 'search/filters.phtml', + [ + 'params' => $params ?? null, + 'urlQuery' => isset($results) ? $results->getUrlQuery() : null, + 'filterList' => $showFilters ? $filterList : [], + 'checkboxFilters' => $showFilters ? $checkboxFilters : [], + 'searchClassId' => $this->searchClassId, + 'searchType' => $this->searchType, + ] + );?> <?php if (!empty($tabs)): ?></div><?php endif; ?> </div> <?php else: ?> @@ -47,16 +69,34 @@ <input id="searchForm_lookfor" class="searchForm_lookfor form-control search-query<?php if($this->searchbox()->autocompleteEnabled($this->searchClassId)):?> autocomplete searcher:<?=$this->escapeHtmlAttr($this->searchClassId) ?><?=$this->searchbox()->autocompleteAutoSubmit($this->searchClassId) ? ' ac-auto-submit' : '' ?><?php endif ?>" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($this->lookfor)?>"<?php if ($placeholder): ?> placeholder="<?=$this->transEsc($placeholder) ?>"<?php endif ?> aria-label="<?=$this->transEsc("search_terms")?>" /> <?php if ($handlerCount > 1): ?> <select id="searchForm_type" class="searchForm_type form-control" name="type" data-native-menu="false" aria-label="<?=$this->transEsc("Search type")?>"> + <?php $currentGroup = $insideGroup = false; ?> <?php foreach ($handlers as $handler): ?> + <?php + if ($currentGroup !== ($handler['group'] ?? false)) { + $currentGroup = $handler['group']; + if ($insideGroup) { + echo '</optgroup>'; + } + if ($currentGroup) { + echo '<optgroup label="' . $this->escapeHtmlAttr($currentGroup) . '">'; + $insideGroup = true; + } else { + $insideGroup = false; + } + } + ?> <option value="<?=$this->escapeHtmlAttr($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$handler['indent'] ? '-- ' : ''?><?=$this->transEsc($handler['label'])?></option> <?php endforeach; ?> + <?php if ($insideGroup): ?> + </optgroup> + <?php endif; ?> </select> <?php elseif ($handlerCount == 1): ?> <input type="hidden" name="type" value="<?=$this->escapeHtmlAttr($handlers[0]['value'])?>" /> <?php endif; ?> <button type="submit" class="btn btn-primary"><i class="fa fa-search" aria-hidden="true"></i> <?=$this->transEsc("Find")?></button> <?php if ($advSearch): ?> - <a href="<?=$this->url($advSearch) . ((isset($this->searchId) && $this->searchId) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams) ?>" class="btn btn-link" rel="nofollow"><?=$this->transEsc("Advanced")?></a> + <a href="<?=$this->url($advSearch) . (($this->searchId ?? false) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams) ?>" class="btn btn-link" rel="nofollow"><?=$this->transEsc("Advanced")?></a> <?php endif; ?> <?php if ($geoUrl = $this->geocoords()->getSearchUrl($options)) : ?> <a href="<?=$geoUrl ?>" class="btn btn-link"><?=$this->transEsc('Geographic Search')?></a> @@ -73,31 +113,15 @@ <input type="checkbox" <?=$isSelected ? 'checked="checked" ' : ''?>name="shard[]" value='<?=$this->escapeHtmlAttr($shard)?>' /> <?=$this->transEsc($shard)?> <?php endforeach; ?> <?php endif; ?> - <?php - $filterDetails = $this->searchbox()->getFilterDetails( - isset($this->filterList) && is_array($this->filterList) ? $this->filterList : [], - isset($this->checkboxFilters) && is_array($this->checkboxFilters) ? $this->checkboxFilters : [] - ); - ?> - <?php if ((isset($hasDefaultsApplied) && $hasDefaultsApplied) || !empty($filterDetails)): ?> - <?php $defaultFilterState = $options->getRetainFilterSetting() ? ' checked="checked"' : ''; ?> - <div class="checkbox"> - <label> - <input type="checkbox"<?=$defaultFilterState?> class="searchFormKeepFilters"/> - <?=$this->transEsc("basic_search_keep_filters")?> - </label> - </div> - <div class="hidden"> + <?php if (($hasDefaultsApplied ?? false) || !empty($filterDetails)): ?> + <?php if ($options->getRetainFilterSetting()): ?> <?php foreach ($filterDetails as $current): ?> - <input class="applied-filter" id="<?=$this->escapeHtmlAttr($current['id'])?>" type="checkbox"<?=$defaultFilterState?> name="filter[]" value="<?=$this->escapeHtmlAttr($current['value'])?>" /> - <label for="<?=$this->escapeHtmlAttr($current['id'])?>"><?=$this->escapeHtml($current['value'])?></label> + <input class="applied-filter" id="<?=$this->escapeHtmlAttr($current['id'])?>" type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($current['value'])?>" /> <?php endforeach; ?> - <?php if (isset($hasDefaultsApplied) && $hasDefaultsApplied): ?> - <!-- this is a hidden element that flags whether or not default filters have been applied; - it is intentionally unlabeled, as users are not meant to manipulate it directly. --> - <input class="applied-filter" id="dfApplied" type="checkbox" name="dfApplied" value="1"<?=$defaultFilterState?> /> + <?php if ($hasDefaultsApplied ?? false): ?> + <input class="applied-filter" id="dfApplied" type="hidden" name="dfApplied" value="1" /> <?php endif; ?> - </div> + <?php endif; ?> <?php endif; ?> <?php foreach ($hiddenFilters as $key => $filter): ?> <?php foreach ($filter as $value): ?> @@ -117,5 +141,17 @@ echo '<input type="hidden" name="sort" value="' . $this->escapeHtmlAttr($lastSort) . '" />'; } ?> + <?=$this->context($this)->renderInContext( + 'search/filters.phtml', + [ + 'params' => $params ?? null, + 'urlQuery' => isset($results) ? $results->getUrlQuery() : null, + 'filterList' => $showFilters ? $filterList : [], + 'checkboxFilters' => $showFilters ? $checkboxFilters : [], + 'searchClassId' => $this->searchClassId, + 'searchType' => $this->searchType, + ] + );?> </form> <?php endif; ?> + diff --git a/themes/finc-accessibility/js/record.js b/themes/finc-accessibility/js/record.js index 4061aac960994b15678094cb98e48c07a075ac3f..f6a80431ba6e9cfc63485d4bdfc2e0d679d3f18d 100644 --- a/themes/finc-accessibility/js/record.js +++ b/themes/finc-accessibility/js/record.js @@ -144,6 +144,7 @@ function registerTabEvents() { VuFind.lightbox.bind('.tab-pane.active'); } +// 'function removeHashFromLocation()' removed in #21555 to irritating prevent jump to content function ajaxLoadTab($newTab, tabid, setHash) { // Request the tab via AJAX: @@ -162,6 +163,7 @@ function ajaxLoadTab($newTab, tabid, setHash) { if (typeof syn_get_widget === "function") { syn_get_widget(); } + // 'if (typeof setHash == 'undefined' || setHash) ...' replaced in #21555 to irritating prevent jump to content $('.tab-pane.active').find(':focusable').eq(0).focus(); }); return false; @@ -220,6 +222,7 @@ function ajaxTagUpdate(_link, tag, _remove) { } function getNewRecordTab(tabid) { + // finc adds accessibility code #19938 return $('<div class="tab-pane ' + tabid + '-tab" id="' + tabid + '" role="tabpanel" tabindex="-1" aria-labelledby="' + tabid + '-tabselector"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i> ' + VuFind.translate('loading') + '...</div>'); } @@ -228,10 +231,11 @@ function backgroundLoadTab(tabid) { return; } var newTab = getNewRecordTab(tabid); - $('.nav-tabs a.' + tabid).closest('.result,.record').find('.tab-content').append(newTab); + $('[data-tab="' + tabid + '"]').closest('.result,.record').find('.tab-content').append(newTab); return ajaxLoadTab(newTab, tabid, false); } +// 'function applyRecordTabHash() ...' replaced in #21555 to irritating prevent jump to content function recordDocReady() { $('.record-tabs .nav-tabs a').click(function recordTabsClick() { var $li = $(this).parent(); @@ -258,12 +262,16 @@ function recordDocReady() { // now need to return -- just switch it back on. if ($li.hasClass('initiallyActive')) { $(this).tab('show'); + // finc reduce radically to get aria in tabs #19938 + remove hashes #21555 + // Fixme: Review during upgrades - CK return false; } // otherwise, we need to let the browser follow the link: return true; } $(this).tab('show'); + // finc changed to get aria in tabs #19938 + remove hashes #21555 + // Fixme: Review during upgrades - CK if ($top.find('.' + tabid + '-tab').length > 0) { $top.find('.' + tabid + '-tab').addClass('active'); $('.tab-pane.active').find(':focusable').eq(0).focus(); @@ -276,8 +284,9 @@ function recordDocReady() { }); $('[data-background]').each(function setupBackgroundTabs(index, el) { - backgroundLoadTab(el.className); + backgroundLoadTab(el.dataset.tab); }); registerTabEvents(); + // finc 'applyRecordTabHash()' removed to remove hashes #21555 } diff --git a/themes/finc-accessibility/scss/compiled.scss b/themes/finc-accessibility/scss/compiled.scss index 8f930d159bf8e12822d9efc884a244f2de7ca3c9..273b21b9120a8dde635c897a12acaa6b5e0cd3b6 100644 --- a/themes/finc-accessibility/scss/compiled.scss +++ b/themes/finc-accessibility/scss/compiled.scss @@ -17,6 +17,16 @@ // $brand-info: #a0d3e8 !default; // cornflower // $brand-success: #085218 !default; // sea-green // $brand-warning: #f08a24 !default; // carrot + +$black: #000 !default; +$white: #fff !default; +$red: #f00 !default; +$brand-primary: #204563 !default; +$brand-warning: #f08a24 !default; +$sidebar-item-active-hover-bg: transparentize($brand-primary, .1) !default; +$sidebar-facet-active-hover-color: $white !default; + + // ------------------------------------------------------- // 1.2 DO NOT define site-specific colours in finc - this will be done in site-specific themes // ------------------------------------------------------- @@ -37,14 +47,53 @@ // $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; // ------------------------------------------------------- // 3. import entire BS (finc) OR import (instances) finc compiled.scss to have variables and mixins defined above applied to it -@import '../../bootstrap3/scss/bootstrap'; -// @import '../../finc/scss/compiled.scss' + + +// Extra small screen / phone +//** Deprecated `$screen-xs` as of v3.0.1 +$screen-xs: 480px !default; +//** Deprecated `$screen-xs-min` as of v3.2.0 +$screen-xs-min: $screen-xs !default; +//** Deprecated `$screen-phone` as of v3.0.1 +$screen-phone: $screen-xs-min !default; + +// Small screen / tablet +//** Deprecated `$screen-sm` as of v3.0.1 +$screen-sm: 768px !default; +$screen-sm-min: $screen-sm !default; +//** Deprecated `$screen-tablet` as of v3.0.1 +$screen-tablet: $screen-sm-min !default; + +// Medium screen / desktop +//** Deprecated `$screen-md` as of v3.0.1 +$screen-md: 992px !default; +$screen-md-min: $screen-md !default; +//** Deprecated `$screen-desktop` as of v3.0.1 +$screen-desktop: $screen-md-min !default; + +// Large screen / wide desktop +//** Deprecated `$screen-lg` as of v3.0.1 +$screen-lg: 1200px !default; +$screen-lg-min: $screen-lg !default; +//** Deprecated `$screen-lg-desktop` as of v3.0.1 +$screen-lg-desktop: $screen-lg-min !default; + +// So media queries don't overlap when required, provide a maximum +$screen-xs-max: ($screen-sm-min - 1) !default; +$screen-sm-max: ($screen-md-min - 1) !default; +$screen-md-max: ($screen-lg-min - 1) !default; + +//@import '../../bootstrap3/scss/bootstrap'; +// @import '../../finc/scss/compiled.scss'; +@import '../../bootstrap3/scss/vendor/bootstrap/variables'; +@import '../../finc/scss/customVariables'; // ------------------------------------------------------- // 4. Customize further - this for BASIC SETTINGS ONLY - all colour customization will be done in in site-specific themes // ATTENTION: any change made here will affect ALL subsequent branches !!! //// Result List ol, ul that contains li.result or facet group / side facets that contains li.facet -.record-list, .facet-group > ul { +.record-list, +.facet-group > ul { list-style: none; margin: 0; padding: 0; @@ -64,14 +113,10 @@ } } -// remove filter links styling -a.remove-filter { - display: flex; - width: 100%; -} -.record-tab.active{ + +.record-tab.active { .load-tab-content { display: none; } -} \ No newline at end of file +} diff --git a/themes/finc-accessibility/templates/Auth/AbstractBase/login.phtml b/themes/finc-accessibility/templates/Auth/AbstractBase/login.phtml index 7a7764f9287be9baa6b4dc3f3b3c83ababcf7843..374f8f82b8912809b2ff02f86d857edc9534e4a7 100644 --- a/themes/finc-accessibility/templates/Auth/AbstractBase/login.phtml +++ b/themes/finc-accessibility/templates/Auth/AbstractBase/login.phtml @@ -7,6 +7,7 @@ <input type="hidden" name="auth_method" value="<?=$account->getAuthMethod()?>"> <input type="hidden" name="csrf" value="<?=$this->escapeHtmlAttr($account->getCsrfHash())?>" /> <div class="form-group"> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="processLogin" aria-label="<?= $this->transEsc("Login-to-account") ?>" value="<?=$this->transEsc('Login')?>"> <?php if ($account->supportsCreation()): ?> <a class="btn btn-link createAccountLink" href="<?=$this->url('myresearch-account') ?>?auth_method=<?=$account->getAuthMethod()?>"><?=$this->transEsc('Create New Account')?></a> diff --git a/themes/finc-accessibility/templates/Auth/AbstractBase/loginfields.phtml b/themes/finc-accessibility/templates/Auth/AbstractBase/loginfields.phtml index 7a1c5dc4252bf27fae32e000ecf2f85525207962..abf0b5817424d25224f30b297c344d0d55b3d91c 100644 --- a/themes/finc-accessibility/templates/Auth/AbstractBase/loginfields.phtml +++ b/themes/finc-accessibility/templates/Auth/AbstractBase/loginfields.phtml @@ -6,6 +6,7 @@ </div> <div class="form-group"> <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_password"><?=$this->transEsc('Password')?>:</label> + <?php /* finc adds 'autocomplete' for accessibility */ ?> <input type="password" name="password" id="login_<?=$this->escapeHtmlAttr($topClass)?>_password" class="form-control" autocomplete="current-password"/> </div> <!-- finc-accessibility: auth - abstractbase - loginfields.phtml - END --> diff --git a/themes/finc-accessibility/templates/Auth/AbstractBase/newpassword.phtml b/themes/finc-accessibility/templates/Auth/AbstractBase/newpassword.phtml index 37a20857e5d2718ae4d2362329bd7f7c8c8c7c77..f70192010cf76b2585436218c577f602b8c705ea 100644 --- a/themes/finc-accessibility/templates/Auth/AbstractBase/newpassword.phtml +++ b/themes/finc-accessibility/templates/Auth/AbstractBase/newpassword.phtml @@ -8,6 +8,7 @@ <?php endif; ?> <?php if (isset($this->verifyold) && $this->verifyold || isset($this->oldpwd)): ?> <div class="form-group"> + <?php /* finc adds 'for' + 'autocomplete' + 'aria-describedby' + 'required' for accessibility */ ?> <label for="current-password" class="control-label"><?=$this->transEsc('old_password') ?>:</label> <input id="current-password" type="password" name="oldpwd" class="form-control" aria-describedby="current-password-error" autocomplete="current-password" required aria-required="true" /> <div id="current-password-error" class="help-block with-errors"></div> @@ -26,6 +27,7 @@ } ?> <div class="form-group"> + <?php /* finc adds 'for' + 'autocomplete' + 'aria-describedby' for accessibility */ ?> <label for="password" class="control-label"><?=$this->transEsc('new_password') ?>:</label> <input type="password" id="password" name="password" class="form-control" required aria-required="true" <?=isset($this->passwordPolicy['minLength']) ? ' data-minlength="' . $this->passwordPolicy['minLength'] . '" data-minlength-error="' . $this->escapeHtmlAttr($this->translate('password_minimum_length', ['%%minlength%%' => $this->passwordPolicy['minLength']])) . '"' : '' ?> @@ -37,9 +39,11 @@ <?php if ($this->passwordPolicy['hint']): ?> <div class="help-block"><?=$this->transEsc($this->passwordPolicy['hint']) ?></div> <?php endif; ?> + <?php /* finc adds 'id' */ ?> <div id="password-error" class="help-block with-errors"></div> </div> <div class="form-group"> + <?php /* finc adds 'for' + 'ids' + 'autocomplete' + 'aria-describedby' for accessibility */ ?> <label for="password2" class="control-label"><?=$this->transEsc('confirm_new_password') ?>:</label> <input id="password2" type="password" name="password2" class="form-control" required aria-required="true" data-match="#password" data-match-error="<?=$this->escapeHtmlAttr($this->translate('Passwords do not match'))?>" autocomplete="new-password" aria-describedby="password2-error"/> <div id="password2-error" class="help-block with-errors"></div> diff --git a/themes/finc-accessibility/templates/Auth/Database/create.phtml b/themes/finc-accessibility/templates/Auth/Database/create.phtml index c4445398ee7af8ef43f1df8fec3003df0cb71767..c3f5c57373716ae7ec0fe5f7d838edc8a934500c 100644 --- a/themes/finc-accessibility/templates/Auth/Database/create.phtml +++ b/themes/finc-accessibility/templates/Auth/Database/create.phtml @@ -14,6 +14,7 @@ ?> <div class="form-group"> <label class="control-label" for="account_firstname"><?=$this->transEsc('First Name')?>:</label> + <?php /* finc adds 'autocomplete' for accessibility - also below! */ ?> <input id="account_firstname" type="text" name="firstname" value="<?=$this->escapeHtmlAttr($this->request->get('firstname'))?>" class="form-control" autocomplete="given-name"/> </div> <div class="form-group"> diff --git a/themes/finc-accessibility/templates/Auth/MultiILS/loginfields.phtml b/themes/finc-accessibility/templates/Auth/MultiILS/loginfields.phtml index f0c00fede0625753989a6a70d2c3dcc38185b3af..96043af54d5ea803878c47d63400234e7b0d5382 100644 --- a/themes/finc-accessibility/templates/Auth/MultiILS/loginfields.phtml +++ b/themes/finc-accessibility/templates/Auth/MultiILS/loginfields.phtml @@ -1,20 +1,37 @@ <!-- finc-accessibility: auth - multiils - loginfields.phtml --> <?php /* copied from /themes/bootstrap3/templates/Auth/MultiILS/loginfields.phtml to add autocomplete */ ?> +<?php $loginTargets = $this->auth()->getManager()->getLoginTargets(); ?> <div class="form-group"> <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> <?php $currentTarget = $this->request->get('target'); if (!$currentTarget) $currentTarget = $this->auth()->getManager()->getDefaultLoginTarget();?> - <select id="login_target" name="target" class="form-control"> - <?php foreach ($this->auth()->getManager()->getLoginTargets() as $target):?> + <select id="login_<?=$this->escapeHtmlAttr($topClass)?>_target" name="target" class="form-control"> + <?php foreach ($loginTargets as $target):?> <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target == $currentTarget ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> <?php endforeach ?> </select> </div> <div class="form-group"> - <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Username')?>:</label> + <label class="control-label password-login" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Username')?>:</label> + <label class="control-label email-login hidden" for="login_<?=$this->escapeHtmlAttr($topClass)?>_username"><?=$this->transEsc('Email')?>:</label> + <?php /* finc adds 'autofocus' for accessibility */ ?> <input id="login_<?=$this->escapeHtmlAttr($topClass)?>_username" type="text" name="username" value="<?=$this->escapeHtmlAttr($this->request->get('username'))?>" class="form-control" autofocus/> </div> <div class="form-group"> <label class="control-label" for="login_<?=$this->escapeHtmlAttr($topClass)?>_password"><?=$this->transEsc('Password')?>:</label> + <?php /* finc adds 'autocomplete' for accessibility */ ?> <input id="login_<?=$this->escapeHtmlAttr($topClass)?>_password" type="password" name="password" class="form-control" autocomplete="current-password"/> </div> -<!-- finc-accessibility: auth - multiils - loginfields.phtml - END --> \ No newline at end of file + +<?php + $methods = []; + $authManager = $this->auth()->getManager(); + foreach ($loginTargets as $target) { + $methods[$target] = $authManager->getILSLoginMethod($target); + } + $methods = json_encode($methods); + $script = "setupMultiILSLoginFields($methods, 'login_{$topClass}_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); +?> +<!-- finc-accessibility: auth - multiils - loginfields.phtml - END --> diff --git a/themes/finc-accessibility/templates/Recommend/AlphaBrowseLink.phtml b/themes/finc-accessibility/templates/Recommend/AlphaBrowseLink.phtml index 8d7fc91cc4c775c9d19045d2361d7e4c22b9ae69..5c4c5f24f3870944fc37f866b84fdf6b297eb7a6 100644 --- a/themes/finc-accessibility/templates/Recommend/AlphaBrowseLink.phtml +++ b/themes/finc-accessibility/templates/Recommend/AlphaBrowseLink.phtml @@ -1,5 +1,5 @@ <!-- finc-accessibility: Recommend - AlphaBrowseLink --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php $index = $this->recommend->getIndex(); @@ -14,5 +14,5 @@ ] ); ?> -<p class="alert alert-info" role="alert"><?=$link?></p> +<p class="alert alert-info" aria-live="polite"><?=$link?></p> <!-- finc-accessibility: Recommend - AlphaBrowseLink - END --> diff --git a/themes/finc-accessibility/templates/Recommend/Channels.phtml b/themes/finc-accessibility/templates/Recommend/Channels.phtml index 9ea450e3ed1056f39a5a9eebb1616a27140dcba3..20c1d4fb20ba5b2d27a8238d24c9fdf0d34be14a 100644 --- a/themes/finc-accessibility/templates/Recommend/Channels.phtml +++ b/themes/finc-accessibility/templates/Recommend/Channels.phtml @@ -1,5 +1,5 @@ <!-- finc-accessibility: Recommend - Channels --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php $results = $this->recommend->getResults(); @@ -7,7 +7,7 @@ . $results->getUrlQuery()->getParams(false) . '&source=' . urlencode($results->getParams()->getSearchClassId()); ?> -<p class="channels-alert alert alert-info" role="alert"> +<p class="channels-alert alert alert-info" aria-live="polite"> <a href="<?=$this->escapeHtmlAttr($link)?>"><?=$this->transEsc('channel_expand')?></a> </p> <!-- finc-accessibility: Recommend - Channels - END --> diff --git a/themes/finc-accessibility/templates/Recommend/DOI.phtml b/themes/finc-accessibility/templates/Recommend/DOI.phtml index d39d99a2e6cc32115663df33cc0dd5747cae4d80..4aadae42a178f4f0aa6473eede8e59cc04d6d001 100644 --- a/themes/finc-accessibility/templates/Recommend/DOI.phtml +++ b/themes/finc-accessibility/templates/Recommend/DOI.phtml @@ -1,9 +1,9 @@ <!-- finc-accessibility: Recommend - DOI --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php $doi = $this->recommend->getDOI(); if (!empty($doi)): ?> <?php $url = $this->recommend->getURL(); ?> - <p class="alert alert-info" role="alert"> + <p class="alert alert-info" aria-live="polite"> <?=$this->translate('doi_detected_html', ['%%url%%' => $url, '%%doi%%' => $doi])?> </p> <?php if ($this->recommend->isFullMatch()): ?> diff --git a/themes/finc-accessibility/templates/Recommend/RemoveFilters.phtml b/themes/finc-accessibility/templates/Recommend/RemoveFilters.phtml index 07f915a551d4bf3f16dec7ae90ec717809d2883c..9301a697aeee4b0b8d789b89f91f350903923c42 100644 --- a/themes/finc-accessibility/templates/Recommend/RemoveFilters.phtml +++ b/themes/finc-accessibility/templates/Recommend/RemoveFilters.phtml @@ -1,8 +1,8 @@ <!-- finc-accessibility: Recommend - RemoveFilters --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php if ($this->recommend->hasFilters()): ?> - <p class="alert alert-info" role="alert"> + <p class="alert alert-info" aria-live="polite"> <?=$this->transEsc('nohit_active_filters')?> <a href="<?=$this->recommend->getFilterlessUrl()?>"><?=$this->transEsc('nohit_query_without_filters')?></a> </p> diff --git a/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml b/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml index 1b1f8adcbba51d11ecdae3e1785993b1e8cfff98..76fd394bd1e7617f21c5e9a1a892da70641fa06f 100644 --- a/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml +++ b/themes/finc-accessibility/templates/Recommend/SideFacets/cluster-list.phtml @@ -14,6 +14,7 @@ </a> <?php break; ?> <?php endif; ?> + <?php /* finc adds li for correct list entries and uses 'a class="text"' */ ?> <li <?=$idAndClass ?>> <a class="text" href="<?=$moreUrl ?>" onclick="event.stopImmediatePropagation(); return moreFacets('narrowGroupHidden-<?=$this->escapeHtmlAttr($this->title) ?>');" rel="nofollow"> <?=$this->transEsc('more')?> ... @@ -30,6 +31,7 @@ ]) ?> <?php endforeach; ?> <?php if (empty($this->cluster['list'])): ?> + <?php /* finc uses 'span' here */ ?> <span class="facet"><?=$this->transEsc('facet_list_empty')?></span> <?php endif; ?> @@ -42,6 +44,7 @@ $moreUrl .= '&baseUriExtra=' . urlencode($this->baseUriExtra); } ?> + <?php /* finc adds li for correct list entries and uses 'a class="text"' */ ?> <li> <a class="facet narrow-toggle <?=$moreClass ?>" data-lightbox href="<?=$moreUrl ?>" rel="nofollow"> <span class="text"><?=$this->transEsc('see all')?> ...</span> diff --git a/themes/finc-accessibility/templates/Recommend/SideFacets/facet.phtml b/themes/finc-accessibility/templates/Recommend/SideFacets/facet.phtml index aa6790b82c6929dfadb2a86c7e25cedfb00e29e7..6bbd232147abb52157005b00c1c5930a8596c48d 100644 --- a/themes/finc-accessibility/templates/Recommend/SideFacets/facet.phtml +++ b/themes/finc-accessibility/templates/Recommend/SideFacets/facet.phtml @@ -31,8 +31,8 @@ [ 'allowExclude' => $this->recommend->excludeAllowed($facet), 'title' => $facet, - 'sortOptions' => $hierarchicalFacetSortOptions[$facet] ?? '', - 'collapsedFacets' => $collapsedFacets + 'sortOptions' => $hierarchicalFacetSortOptions[$facet] ?? $hierarchicalFacetSortOptions['*'] ?? null, + 'collapsedFacets' => $this->collapsedFacets ] ); ?> <noscript> diff --git a/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml b/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml index a6b0fc61aef449d149113027d54e4de90e0524bb..72034180f17fa1dce59c670b6fa9a55d0aabde96 100644 --- a/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml +++ b/themes/finc-accessibility/templates/Recommend/SideFacets/filter-list.phtml @@ -1,5 +1,6 @@ <!-- finc-accessibility - Recommend - SideFacets - filter-list.phtml --> -<?php /* #18509 copied from bootstrap for adding language tags to displayText */ ?> +<?php /* #18509 copied from bootstrap for adding language tags to displayText */ +/* FIXME: Is this still in use in VF6? */ ?> <div class="facet-group active-filters"> <?php /* change div of Remove Filters to h3, #19406 - VE */ ?> <h3 class="title"><?=$this->transEsc('Remove Filters') ?> <span class="sr-only"><?=$this->transEsc('facet_deselect_hint') ?></span></h3> diff --git a/themes/finc-accessibility/templates/Recommend/SwitchType.phtml b/themes/finc-accessibility/templates/Recommend/SwitchType.phtml index 9276727ecdf680fa8c1b7cb6629c34f6ba34778e..32fc4f86a8a7abe298082e309970a12eb7205b7f 100644 --- a/themes/finc-accessibility/templates/Recommend/SwitchType.phtml +++ b/themes/finc-accessibility/templates/Recommend/SwitchType.phtml @@ -1,8 +1,8 @@ <!-- finc-accessibility: Recommend - SwitchType --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts */ ?> <?php if ($handler = $this->recommend->getNewHandler()): ?> - <p class="alert alert-info" role="alert" role="alert"> + <p class="alert alert-info" aria-live="polite"> <?=$this->transEsc('widen_prefix')?> <a href="<?=$this->recommend->getResults()->getUrlQuery()->setHandler($handler)?>"><?=$this->transEsc($this->recommend->getNewHandlerName())?></a>. </p> diff --git a/themes/finc-accessibility/templates/RecordDriver/EDS/result-list.phtml b/themes/finc-accessibility/templates/RecordDriver/EDS/result-list.phtml index 8f2d6712f935c7ff7f5234a46c7926242c3ffa02..e467a2bbf53d67febc38133fb2e8f1fcf34214a6 100644 --- a/themes/finc-accessibility/templates/RecordDriver/EDS/result-list.phtml +++ b/themes/finc-accessibility/templates/RecordDriver/EDS/result-list.phtml @@ -1,5 +1,5 @@ <!-- finc-accessibility: RecordDriver - EDS - result-list --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts + empty 'lang' for 'language undefined' */ ?> <?php $this->headLink()->appendStylesheet('EDS.css'); @@ -11,6 +11,7 @@ $thumbnail = false; $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result'); ob_start(); ?> + <?php /* finc adds aria and tabindex - Fixme: Re-evaluate during updates */ ?> <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtml($coverDetails['size'])?>" aria-hidden="true"> <?php if ($coverDetails['cover']): ?> <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="_record_link" tabindex="-1"> @@ -83,17 +84,22 @@ </div> <?php if ($this->driver->hasHTMLFullTextAvailable()): ?> - <a href="<?= $this->recordLink()->getUrl($this->driver, 'fulltext') ?>#html" class="icon html fulltext _record_link" target="_blank"> + <a href="<?= $this->recordLink()->getUrl($this->driver) ?>#html" class="icon html fulltext _record_link" target="_blank"> <?=$this->transEsc('HTML Full Text')?> </a> <?php endif; ?> <?php if ($this->driver->hasPdfAvailable()): ?> - <a href="<?= $this->recordLink()->getUrl($this->driver) . '/PDF'; ?>" class="icon pdf fulltext" target="_blank"> + <a href="<?= $this->recordLink()->getTabUrl($this->driver, 'PDF'); ?>" class="icon pdf fulltext" target="_blank"> <?=$this->transEsc('PDF Full Text')?> </a> <?php endif; ?> + + <?php /* Links from DOI linker */ ?> + <?php $doi = $this->doi($this->driver, 'results'); if ($doi->isActive()):?> + <?=$doi->renderTemplate()?> + <?php endif; ?> </div> <div class="result-links hidden-print"> <?php /* Display qrcode if appropriate: */ ?> @@ -105,7 +111,7 @@ <span class="hidden-xs"> <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> <div class="qrcode hidden"> - <script type="text/template" class="qrCodeImgTag"> + <script class="qrCodeImgTag"> <img alt="<?=$this->transEsc('QR Code')?>" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> </script> </div><br/> @@ -120,8 +126,8 @@ <?php /* Add to favorites */ ?> <i class="fa fa-fw fa-star" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" class="save-record" data-lightbox id="<?=$this->driver->getUniqueId() ?>" title="<?=$this->transEsc('Add to favorites')?>"><?=$this->transEsc('Add to favorites')?></a><br/> - <?php /* Saved lists */ ?> - <p class="savedLists alert alert-info hidden" role="alert"> + <?php /* Saved lists, finc uses aria-live */ ?> + <p class="savedLists" aria-live="polite"> <strong><?=$this->transEsc("Saved in")?>:</strong> </p> <?php endif; ?> @@ -132,7 +138,7 @@ <div class="hierarchyTreeLink"> <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> <i class="fa fa-fw fa-sitemap" aria-hidden="true"></i> - <a class="hierarchyTreeLinkText" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> + <a class="hierarchyTreeLinkText" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchyID])?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>"> <?=$this->transEsc('hierarchy_view_context')?><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> </div> diff --git a/themes/finc-accessibility/templates/RecordTab/similaritemscarousel.phtml b/themes/finc-accessibility/templates/RecordTab/similaritemscarousel.phtml index f112e618638e5d1d11e52ae77beee3bfc6380b46..a740f71d776f8a293da8744b5b411b7f1ce72e87 100644 --- a/themes/finc-accessibility/templates/RecordTab/similaritemscarousel.phtml +++ b/themes/finc-accessibility/templates/RecordTab/similaritemscarousel.phtml @@ -1,4 +1,5 @@ -<?php /* add language specific translations for carousel */ ?> +<!-- finc-accessibility - RecordTab - similaritemscarousel --> +<?php /* finc adds language-specific translations for carousel */ ?> <?php if (strcmp($this->layout()->userLang, 'de') == 0): ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'vendor/bootstrap-accessibility-de.min.js', 'SET');?> <?php endif; ?> @@ -73,3 +74,4 @@ $('#similar-items-carousel img').load(normalizeHeights); JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET') ?> +<!-- finc-accessibility - RecordTab - similaritemscarousel - END --> diff --git a/themes/finc-accessibility/templates/Related/Channels.phtml b/themes/finc-accessibility/templates/Related/Channels.phtml index 5b5abb0465820528c50343ab7aad9fa33b6526c6..d9936d97840e684d91c5c57dad7a2bdd49f9e0c7 100644 --- a/themes/finc-accessibility/templates/Related/Channels.phtml +++ b/themes/finc-accessibility/templates/Related/Channels.phtml @@ -1,5 +1,5 @@ <!-- finc-accessibility: Related - Channels --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php $driver = $this->related->getDriver(); @@ -7,7 +7,7 @@ . '?id=' . urlencode($driver->getUniqueId()) . '&source=' . urlencode($driver->getSourceIdentifier()); ?> -<p class="channels-alert alert alert-info" role="alert"> +<p class="channels-alert alert alert-info" aria-live="polite"> <a href="<?=$this->escapeHtmlAttr($link)?>"><?=$this->transEsc('channel_expand')?></a> </p> <!-- finc-accessibility: Related - Channels - END --> diff --git a/themes/finc-accessibility/templates/channels/channelList.phtml b/themes/finc-accessibility/templates/channels/channelList.phtml index 1224ee29b4180f3f85758c83d6180da2301c045b..db441a53c0187d7c9c738862d7358a7ddb12b5a5 100644 --- a/themes/finc-accessibility/templates/channels/channelList.phtml +++ b/themes/finc-accessibility/templates/channels/channelList.phtml @@ -29,6 +29,7 @@ <?php if (empty($token)): ?> <form action="<?=$this->url('channels-search')?>" class="channel-search form-inline"> <?=$this->transEsc('channel_searchbox_label')?> + <?php /* finc adds 'aria-labels' for accessibility */ ?> <input type="text" name="lookfor" class="form-control" value="<?=$this->escapeHtmlAttr($this->lookfor) ?>" aria-label="<?=$this->transEsc('search_terms') ?>"/> <input type="submit" value="<?=$this->escapeHtmlAttr($this->translate('Submit'))?>" class="btn btn-default" aria-label="<?=$this->transEsc('Submit') ?>" /> </form> @@ -47,6 +48,7 @@ <?php if (count($channel['relatedTokens'] ?? []) > 0): ?> <div class="channel-add-menu hidden" data-group="<?=$groupId ?>"> + <?php /* finc adds 'aria-label' for accessibility */ ?> <button type="button" class="add-btn btn btn-link"><?=$this->transEsc('channel_add_more') ?></button><!-- --><button type="button" class="btn btn-link dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="<?=$this->transEsc('toggle-dropdown') ?>"> <span class="caret"></span> @@ -72,3 +74,4 @@ </div> </div> <?php endforeach; ?> +<!-- finc-accessibility: channels - channelList - END --> \ No newline at end of file diff --git a/themes/finc-accessibility/templates/myresearch/delete.phtml b/themes/finc-accessibility/templates/myresearch/delete.phtml index aeb2c39b4b64b8759d3c88c1df86a5bd96292a15..65de8b04d3fdfe745bfa2033e6b7d6b4d622a1f0 100644 --- a/themes/finc-accessibility/templates/myresearch/delete.phtml +++ b/themes/finc-accessibility/templates/myresearch/delete.phtml @@ -1,13 +1,12 @@ <!-- finc-accessibility: myresearch - delete --> -<?php /* #17950 - use <p> for alerts - HR */ -/* #18535 - record listings - AP */ ?> +<?php /* finc uses h1 and h2 for headings hierarchy, and below uses <p> and aria for alerts; see #18535 for record listings als lists - AP */ ?> <h1><?=$this->transEsc('delete_selected_favorites')?></h1> <form action="<?=$this->url('myresearch-delete')?>" method="post" name="bulkDelete" data-lightbox-onclose="VuFind.refreshPage"> <div id="popupMessages"><?=$this->flashmessages()?></div> <div id="popupDetails"> <?php if (!$this->list): ?> - <p class="alert alert-info" role="alert"><?=$this->transEsc("fav_delete_warn") ?></p> + <p class="alert alert-info" role="alert" aria-live="assertive"><?=$this->transEsc("fav_delete_warn") ?></p> <?php else: ?> <h2><?=$this->transEsc("List") ?>: <?=$this->escapeHtml($this->list->title) ?></h2> <?php endif; ?> diff --git a/themes/finc-accessibility/templates/myresearch/login.phtml b/themes/finc-accessibility/templates/myresearch/login.phtml index f7d5f80479408229447b485f1b3b0056d30a18c3..1c3301deece342ba309eeddaf15209bc6fc6f613 100644 --- a/themes/finc-accessibility/templates/myresearch/login.phtml +++ b/themes/finc-accessibility/templates/myresearch/login.phtml @@ -1,5 +1,5 @@ <!-- finc-accessibility: myresearch - login --> -<?php /* #17950 - use <p> for alerts - HR */ ?> +<?php /* finc uses <p> and aria for alerts*/ ?> <?php // Set up page title: @@ -22,7 +22,7 @@ <?=$this->flashmessages()?> <?php if ($hideLogin): ?> - <p class="alert alert-danger" role="alert"><?=$this->transEsc('login_disabled')?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc('login_disabled')?></p> <?php else: ?> <?=$this->auth()->getLogin()?> <?php endif; ?> diff --git a/themes/finc-accessibility/templates/record/comments-list.phtml b/themes/finc-accessibility/templates/record/comments-list.phtml index fa892bbad12807b77f5528e057e0e19c6e5e8e69..255b60997fd59ca940608e649f086c4e057b73a6 100644 --- a/themes/finc-accessibility/templates/record/comments-list.phtml +++ b/themes/finc-accessibility/templates/record/comments-list.phtml @@ -1,7 +1,8 @@ <!-- finc-accessibility: record - comments-list --> <?php $comments = $this->driver->getComments(); ?> <?php if (empty($comments) || count($comments) == 0): ?> - <p class="alert alert-info" role="alert"><?=$this->transEsc('Be the first to leave a comment')?>!</p> +<?php /* finc uses aria-live */ ?> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc('Be the first to leave a comment')?>!</p> <?php else: ?> <?php foreach ($comments as $comment): ?> <div class="comment"> diff --git a/themes/finc-accessibility/templates/search/facet-list.phtml b/themes/finc-accessibility/templates/search/facet-list.phtml index 9312d4d68412be58d3a9ad5f1a7d8e03fa639049..3fdc40df2b577ba68470d8ad4cc79b99246094f5 100644 --- a/themes/finc-accessibility/templates/search/facet-list.phtml +++ b/themes/finc-accessibility/templates/search/facet-list.phtml @@ -28,6 +28,7 @@ <div class="lightbox-scroll full-facets"> <?php foreach ($this->sortOptions as $key => $sort): ?> <?php $active = $this->sort == $key; ?> + <?php /* finc uses ul structure */ ?> <ul class="full-facet-list facet-group<?php if(!$active): ?> hidden<?php endif; ?>" id="facet-list-<?=$this->escapeHtmlAttr($key) ?>"> <?php if ($active): ?> <?php if ($this->page > 1): ?> diff --git a/themes/finc-accessibility/templates/search/list-list.phtml b/themes/finc-accessibility/templates/search/list-list.phtml index b86711fdfea9516cf8b8023cd44571457fbb370f..a12c5512413156c355fdf42f5503af772985fc44 100644 --- a/themes/finc-accessibility/templates/search/list-list.phtml +++ b/themes/finc-accessibility/templates/search/list-list.phtml @@ -3,6 +3,7 @@ <?php if (!isset($this->indexStart)) $this->indexStart = 0; ?> <?php $i = $this->indexStart; ?> <?php $listStart = $this->results->getStartRecord() + $i - $this->indexStart; ?> +<?php /* finc uses ol list */ ?> <ol class="record-list" start="<?=$listStart?>"> <?php foreach ($this->results->getResults() as $current): ?> <?php $recordNumber = $this->results->getStartRecord() + $i - $this->indexStart; ?> diff --git a/themes/finc/js/account_ajax.js b/themes/finc/js/account_ajax.js index 7fb72e08c8b53dd401182a1bd40525a7b403825e..6f6d3687376ecba195ad5dbef8c6f68c97f55bda 100644 --- a/themes/finc/js/account_ajax.js +++ b/themes/finc/js/account_ajax.js @@ -184,17 +184,18 @@ $(document).ready(function registerAccountAjax() { var html = ''; var level = ICON_LEVELS.NONE; if (status.ok > 0) { - html += '<span class="badge ok">' + status.ok + '</span>'; + html += '<span class="badge ok" data-toggle="tooltip" title="' + VuFind.translate('Checked Out Items') + '">' + status.ok + '</span>'; } if (status.warn > 0) { - html += '<span class="badge warn">' + status.warn + '</span>'; + html += '<span class="badge warn" data-toggle="tooltip" title="' + VuFind.translate('renew_item_due_tooltip') + '">' + status.warn + '</span>'; level = ICON_LEVELS.WARNING; } if (status.overdue > 0) { - html += '<span class="badge overdue">' + status.overdue + '</span>'; + html += '<span class="badge overdue" data-toggle="tooltip" title="' + VuFind.translate('renew_item_overdue_tooltip') + '">' + status.overdue + '</span>'; level = ICON_LEVELS.DANGER; } $element.html(html); + $('[data-toggle="tooltip"]', $element).tooltip(); return level; } }); @@ -205,13 +206,14 @@ $(document).ready(function registerAccountAjax() { render: function render($element, status, ICON_LEVELS) { var level = ICON_LEVELS.NONE; if (status.available > 0) { - $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('hold_available') + '"></i>'); + $element.html('<i class="fa fa-bell text-success" data-toggle="tooltip" title="' + VuFind.translate('hold_available') + '"></i>'); level = ICON_LEVELS.GOOD; } else if (status.in_transit > 0) { - $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>'); + $element.html('<i class="fa fa-clock-o text-warning" data-toggle="tooltip" title="' + VuFind.translate('request_in_transit') + '"></i>'); } else { $element.addClass("holds-status hidden"); } + $('[data-toggle="tooltip"]', $element).tooltip(); return level; } }); @@ -222,13 +224,14 @@ $(document).ready(function registerAccountAjax() { render: function render($element, status, ICON_LEVELS) { var level = ICON_LEVELS.NONE; if (status.available > 0) { - $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('ill_request_available') + '"></i>'); + $element.html('<i class="fa fa-bell text-success" data-toggle="tooltip" title="' + VuFind.translate('ill_request_available') + '"></i>'); level = ICON_LEVELS.GOOD; } else if (status.in_transit > 0) { - $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>'); + $element.html('<i class="fa fa-clock-o text-warning" data-toggle="tooltip" title="' + VuFind.translate('request_in_transit') + '"></i>'); } else { $element.addClass("holds-status hidden"); } + $('[data-toggle="tooltip"]', $element).tooltip(); return level; } }); @@ -239,13 +242,14 @@ $(document).ready(function registerAccountAjax() { render: function render($element, status, ICON_LEVELS) { var level = ICON_LEVELS.NONE; if (status.available > 0) { - $element.html('<i class="fa fa-bell text-success" title="' + VuFind.translate('storage_retrieval_request_available') + '"></i>'); + $element.html('<i class="fa fa-bell text-success" data-toggle="tooltip" title="' + VuFind.translate('storage_retrieval_request_available') + '"></i>'); level = ICON_LEVELS.GOOD; } else if (status.in_transit > 0) { - $element.html('<i class="fa fa-clock-o text-warning" title="' + VuFind.translate('request_in_transit') + '"></i>'); + $element.html('<i class="fa fa-clock-o text-warning" data-toggle="tooltip" title="' + VuFind.translate('request_in_transit') + '"></i>'); } else { $element.addClass("holds-status hidden"); } + $('[data-toggle="tooltip"]', $element).tooltip(); return level; } }); diff --git a/themes/finc/js/check_item_statuses.js b/themes/finc/js/check_item_statuses.js index 50ac88dea84ba00615607a8bf73a1944eeacd43a..e3571bd61d4d7e2c1f81066579788c9026a3218a 100644 --- a/themes/finc/js/check_item_statuses.js +++ b/themes/finc/js/check_item_statuses.js @@ -1,175 +1,201 @@ /*global Hunt, VuFind */ -/*exported checkItemStatuses, itemStatusFail */ - -function linkCallnumbers(callnumber, callnumber_handler) { - if (callnumber_handler) { - var cns = callnumber.split(',\t'); - for (var i = 0; i < cns.length; i++) { - cns[i] = '<a href="' + VuFind.path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; +VuFind.register('itemStatuses', function ItemStatuses() { + function linkCallnumbers(callnumber, callnumber_handler) { + if (callnumber_handler) { + var cns = callnumber.split(',\t'); + for (var i = 0; i < cns.length; i++) { + cns[i] = '<a href="' + VuFind.path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; + } + return cns.join(',\t'); } - return cns.join(',\t'); + return callnumber; } - return callnumber; -} - -function displayItemStatus(result, $item) { - $item.removeClass('js-item-pending'); - $item.find('.status').empty().append(result.availability_message); - $item.find('.ajax-availability').removeClass('ajax-availability hidden'); - if (typeof(result.error) != 'undefined' - && result.error.length > 0 - ) { - // Only show error message if we also have a status indicator active: - if ($item.find('.status').length > 0) { + function displayItemStatus(result, $item) { + $item.addClass('js-item-done').removeClass('js-item-pending'); + $item.find('.status').empty().append(result.availability_message); + $item.find('.ajax-availability').removeClass('ajax-availability hidden'); + if (typeof(result.error) != 'undefined' + && result.error.length > 0 + ) { $item.find('.callnumAndLocation').empty().addClass('text-danger').append(result.error); - } else { - $item.find('.callnumAndLocation').addClass('hidden'); - } - $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); - } else if (typeof(result.full_status) != 'undefined' - && result.full_status.length > 0 - && $item.find('.callnumAndLocation').length > 0 - ) { - // Full status mode is on -- display the HTML and hide extraneous junk: - $item.find('.callnumAndLocation').empty().append(result.full_status); - $item.find('.callnumber,.hideIfDetailed,.location,.status').addClass('hidden'); - } else if (typeof(result.missing_data) != 'undefined' - && result.missing_data - ) { - // No data is available -- hide the entire status area: - $item.find('.callnumAndLocation,.status').addClass('hidden'); - } else if (result.locationList) { - // We have multiple locations -- build appropriate HTML and hide unwanted labels: - $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); - var locationListHTML = ""; - for (var x = 0; x < result.locationList.length; x++) { - locationListHTML += '<div class="groupLocation">'; - if (result.locationList[x].availability) { - locationListHTML += '<span class="text-success"><i class="fa fa-ok" aria-hidden="true"></i> ' - + result.locationList[x].location + '</span> '; - } else if (typeof(result.locationList[x].status_unknown) !== 'undefined' - && result.locationList[x].status_unknown - ) { - if (result.locationList[x].location) { - locationListHTML += '<span class="text-warning"><i class="fa fa-status-unknown" aria-hidden="true"></i> ' + $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); + } else if (typeof(result.full_status) != 'undefined' + && result.full_status.length > 0 + && $item.find('.callnumAndLocation').length > 0 + ) { + // Full status mode is on -- display the HTML and hide extraneous junk: + $item.find('.callnumAndLocation').empty().append(result.full_status); + $item.find('.callnumber,.hideIfDetailed,.location,.status').addClass('hidden'); + } else if (typeof(result.missing_data) !== 'undefined' + && result.missing_data + ) { + // No data is available -- hide the entire status area: + $item.find('.callnumAndLocation,.status').addClass('hidden'); + } else if (result.locationList) { + // We have multiple locations -- build appropriate HTML and hide unwanted labels: + $item.find('.callnumber,.hideIfDetailed,.location').addClass('hidden'); + var locationListHTML = ""; + for (var x = 0; x < result.locationList.length; x++) { + locationListHTML += '<div class="groupLocation">'; + if (result.locationList[x].availability) { + locationListHTML += '<span class="text-success"><i class="fa fa-ok" aria-hidden="true"></i> ' + + result.locationList[x].location + '</span> '; + } else if (typeof(result.locationList[x].status_unknown) !== 'undefined' + && result.locationList[x].status_unknown + ) { + if (result.locationList[x].location) { + locationListHTML += '<span class="text-warning"><i class="fa fa-status-unknown" aria-hidden="true"></i> ' + + result.locationList[x].location + '</span> '; + } + } else { + locationListHTML += '<span class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> ' + result.locationList[x].location + '</span> '; } - } else { - locationListHTML += '<span class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> ' - + result.locationList[x].location + '</span> '; + locationListHTML += '</div>'; + locationListHTML += '<div class="groupCallnumber">'; + locationListHTML += (result.locationList[x].callnumbers) + ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; + locationListHTML += '</div>'; } - locationListHTML += '</div>'; - locationListHTML += '<div class="groupCallnumber">'; - locationListHTML += (result.locationList[x].callnumbers) - ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; - locationListHTML += '</div>'; + $item.find('.locationDetails').removeClass('hidden'); + $item.find('.locationDetails').html(locationListHTML); + // next three lines finc-specific, added in #5737, check functionality, CK - Fixme 5.1 + } else if (result.callnumber.length == 0 && result.location.length == 0) { + // hide location and callnumber information if both are empty + $item.find('.callnumAndLocation').addClass('hidden'); + } else { + // Default case -- load call number and location into appropriate containers: + $item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler) + '<br/>'); + $item.find('.location').empty().append( + result.reserve === 'true' + ? result.reserve_message + : result.location + ); } - $item.find('.locationDetails').removeClass('hidden'); - $item.find('.locationDetails').html(locationListHTML); - // next three lines finc-specific, added in #5737, check functionality, CK - Fixme 5.1 - } else if (result.callnumber.length == 0 && result.location.length == 0) { - // hide location and callnumber information if both are empty - $item.find('.callnumAndLocation').addClass('hidden'); - } else { - // Default case -- load call number and location into appropriate containers: - $item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler) + '<br/>'); - $item.find('.location').empty().append( - result.reserve === 'true' - ? result.reserve_message - : result.location - ); - } -} - -function itemStatusFail(response, textStatus) { - if (textStatus === 'abort' || typeof response.responseJSON === 'undefined') { - return; } - // display the error message on each of the ajax status place holder - $('.js-item-pending .callnumAndLocation').addClass('text-danger').empty().removeClass('hidden') - .append(typeof response.responseJSON.data === 'string' ? response.responseJSON.data : VuFind.translate('error_occurred')); -} - -var itemStatusIds = []; -var itemStatusEls = {}; -var itemStatusTimer = null; -var itemStatusDelay = 200; -var itemStatusRunning = false; -function runItemAjaxForQueue() { - // Only run one item status AJAX request at a time: - if (itemStatusRunning) { - itemStatusTimer = setTimeout(runItemAjaxForQueue, itemStatusDelay); - return; - } - itemStatusRunning = true; - $.ajax({ + var ItemStatusHandler = { + name: "default", + //array to hold IDs and elements + itemStatusIds: [], itemStatusEls: [], + url: '/AJAX/JSON?method=getItemStatuses', + itemStatusRunning: false, dataType: 'json', method: 'POST', - url: VuFind.path + '/AJAX/JSON?method=getItemStatuses', - data: {'id': itemStatusIds} - }) - .done(function checkItemStatusDone(response) { - for (var j = 0; j < response.data.statuses.length; j++) { - var status = response.data.statuses[j]; - displayItemStatus(status, itemStatusEls[status.id]); - itemStatusIds.splice(itemStatusIds.indexOf(status.id), 1); + itemStatusTimer: null, + itemStatusDelay: 200, + + checkItemStatusDone: function checkItemStatusDone(response) { + var data = response.data; + for (var j = 0; j < data.statuses.length; j++) { + var status = data.statuses[j]; + displayItemStatus(status, this.itemStatusEls[status.id]); + this.itemStatusIds.splice(this.itemStatusIds.indexOf(status.id), 1); + } + }, + itemStatusFail: function itemStatusFail(response, textStatus) { + if (textStatus === 'error' || textStatus === 'abort' || typeof response.responseJSON === 'undefined') { + return; } - itemStatusRunning = false; - }) - .fail(function checkItemStatusFail(response, textStatus) { - itemStatusFail(response, textStatus); - itemStatusRunning = false; - }); -} + // display the error message on each of the ajax status place holder + $('.js-item-pending .callnumAndLocation').addClass('text-danger').empty().removeClass('hidden') + .append(typeof response.responseJSON.data === 'string' ? response.responseJSON.data : VuFind.translate('error_occurred')); + }, + itemQueueAjax: function itemQueueAjax(id, el) { + clearTimeout(this.itemStatusTimer); + this.itemStatusIds.push(id); + this.itemStatusEls[id] = el; + this.itemStatusTimer = setTimeout(this.runItemAjaxForQueue.bind(this), this.itemStatusDelay); + el.addClass('js-item-pending').removeClass('hidden'); + el.find('.callnumAndLocation').removeClass('hidden'); + el.find('.callnumAndLocation .ajax-availability').removeClass('hidden'); + el.find('.status').removeClass('hidden'); + }, -function itemQueueAjax(id, el) { - if (el.hasClass('js-item-pending')) { - return; - } - clearTimeout(itemStatusTimer); - itemStatusIds.push(id); - itemStatusEls[id] = el; - itemStatusTimer = setTimeout(runItemAjaxForQueue, itemStatusDelay); - el.addClass('js-item-pending').removeClass('hidden'); - el.find('.callnumAndLocation').removeClass('hidden'); - el.find('.callnumAndLocation .ajax-availability').removeClass('hidden'); - el.find('.status').removeClass('hidden'); -} + runItemAjaxForQueue: function runItemAjaxForQueue() { + if (this.itemStatusRunning) { + this.itemStatusTimer = setTimeout(this.runItemAjaxForQueue.bind(this), this.itemStatusDelay); + return; + } + $.ajax({ + dataType: this.dataType, + method: this.method, + url: VuFind.path + this.url, + context: this, + data: { 'id': this.itemStatusIds } + }) + .done(this.checkItemStatusDone) + .fail( this.itemStatusFail) + .always(function queueAjaxAlways() { + this.itemStatusRunning = false; + }); + }//end runItemAjax + }; -function checkItemStatus(el) { - var $item = $(el); - if ($item.find('.hiddenId').length === 0) { - return false; - } - var id = $item.find('.hiddenId').val(); - itemQueueAjax(id + '', $item); -} + //add you own overridden handler here + var OdItemStatusHandler = Object.create(ItemStatusHandler); + OdItemStatusHandler.url = '/Overdrive/getStatus'; + OdItemStatusHandler.itemStatusDelay = 200; + OdItemStatusHandler.name = "overdrive"; + OdItemStatusHandler.itemStatusIds = []; + OdItemStatusHandler.itemStatusEls = []; + + //store the handlers in a "hash" obj + var checkItemHandlers = { + 'ils': ItemStatusHandler, + 'overdrive': OdItemStatusHandler, + }; -var itemStatusObserver = null; -function checkItemStatuses(_container) { - var container = typeof _container === 'undefined' - ? document.body - : _container; + function checkItemStatus(el) { + var $item = $(el); + if ($item.hasClass('js-item-pending') || $item.hasClass('js-item-done')) { + return; + } + if ($item.find('.hiddenId').length === 0) { + return false; + } + var id = $item.find('.hiddenId').val(); + var handlerName = 'ils'; + if ($item.find('.handler-name').length > 0) { + handlerName = $item.find('.handler-name').val(); + } - var ajaxItems = $(container).find('.ajaxItem'); - for (var i = 0; i < ajaxItems.length; i++) { - var id = $(ajaxItems[i]).find('.hiddenId').val(); - itemQueueAjax(id, $(ajaxItems[i])); + //queue the element into the handler + checkItemHandlers[handlerName].itemQueueAjax(id, $item); } - // Stop looking for a scroll loader - if (itemStatusObserver) { - itemStatusObserver.disconnect(); + + function checkItemStatuses(_container) { + var container = typeof _container === 'undefined' + ? document.body + : _container; + + var ajaxItems = $(container).find('.ajaxItem'); + for (var i = 0; i < ajaxItems.length; i++) { + var id = $(ajaxItems[i]).find('.hiddenId').val(); + var handlerName = 'ils'; + if ($(ajaxItems[i]).find('.handler-name').length > 0) { + handlerName = $(ajaxItems[i]).find('.handler-name').val(); + } + if ($(ajaxItems[i]).data("handler-name")) { + handlerName = $(ajaxItems[i]).data("handler-name"); + } + checkItemHandlers[handlerName].itemQueueAjax(id, $(ajaxItems[i])); + } } -} -$(document).ready(function checkItemStatusReady() { - if (typeof Hunt === 'undefined') { - checkItemStatuses(); - } else { - itemStatusObserver = new Hunt( - $('.ajaxItem').toArray(), - {enter: checkItemStatus} - ); + function init(_container) { + if (typeof Hunt === 'undefined') { + checkItemStatuses(_container); + } else { + var container = typeof _container === 'undefined' + ? document.body + : _container; + new Hunt( + $(container).find('.ajaxItem').toArray(), + { enter: checkItemStatus } + ); + } } + + return { init: init, check: checkItemStatuses }; }); diff --git a/themes/finc/js/collection_record.js b/themes/finc/js/collection_record.js index 5c085b9525facd53c22400ea0fecf4dca2a010d6..a7784ba6c322d3c3b9bde45b32c8a0eff700fd3e 100644 --- a/themes/finc/js/collection_record.js +++ b/themes/finc/js/collection_record.js @@ -3,19 +3,19 @@ function toggleCollectionInfo() { } function showMoreInfoToggle() { - // no rows in table? don't bother! - if ($("#collectionInfo").find('tr').length < 1) { - return; - } - // finc-specific: Keep Accordion OPEN on load - CK - // toggleCollectionInfo(); - $("#moreInfoToggle").removeClass('hidden'); - //$("#moreInfoToggle").click(function moreInfoToggleClick(e) { - // e.preventDefault(); - // toggleCollectionInfo(); - //}); + // no rows in table? don't bother! + if ($("#collectionInfo").find('tr').length < 1) { + return; + } + // finc-specific: Keep Accordion OPEN on load - CK + // toggleCollectionInfo(); + $("#moreInfoToggle").removeClass('hidden'); + //$("#moreInfoToggle").click(function moreInfoToggleClick(e) { + // e.preventDefault(); + // toggleCollectionInfo(); + //}); } $(document).ready(function collectionRecordReady() { - showMoreInfoToggle(); + showMoreInfoToggle(); }); diff --git a/themes/finc/js/hierarchyTree.js b/themes/finc/js/hierarchyTree.js index a0afca2660ebf68789605be36946e3f171791a0a..99dcab2b8801a6410f56cf74650368142298fbc4 100644 --- a/themes/finc/js/hierarchyTree.js +++ b/themes/finc/js/hierarchyTree.js @@ -1,6 +1,6 @@ /*global VuFind */ -var hierarchyID, recordID, htmlID, hierarchyContext, hierarchySettings; +var hierarchyID, recordID, hierarchySource, htmlID, hierarchyContext, hierarchySettings; /* Utility functions */ function htmlEncodeId(id) { @@ -15,7 +15,7 @@ function html_entity_decode(string) { var tmp_str = string.toString(); for (var symbol in hash_map) { - if (hash_map.hasOwnProperty(symbol)) { + if (Object.prototype.hasOwnProperty.call(hash_map, symbol)) { var entity = hash_map[symbol]; tmp_str = tmp_str.split(entity).join(symbol); } @@ -27,18 +27,18 @@ function html_entity_decode(string) { function getRecord(id) { $.ajax({ - url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id}), + url: VuFind.path + '/Hierarchy/GetRecord?' + $.param({id: id, hierarchySource: hierarchySource}), dataType: 'html' }) - .done(function getRecordDone(response) { - $('#tree-preview').html(html_entity_decode(response)); - // Remove the old path highlighting - $('#hierarchyTree a').removeClass("jstree-highlight"); - // Add Current path highlighting - var jsTreeNode = $(":input[value='" + id + "']").parent(); - jsTreeNode.children("a").addClass("jstree-highlight"); - jsTreeNode.parents("li").children("a").addClass("jstree-highlight"); - }); + .done(function getRecordDone(response) { + $('#tree-preview').html(html_entity_decode(response)); + // Remove the old path highlighting + $('#hierarchyTree a').removeClass("jstree-highlight"); + // Add Current path highlighting + var jsTreeNode = $(":input[value='" + id + "']").parent(); + jsTreeNode.children("a").addClass("jstree-highlight"); + jsTreeNode.parents("li").children("a").addClass("jstree-highlight"); + }); } function changeNoResultLabel(display) { @@ -75,29 +75,30 @@ function doTreeSearch() { url: VuFind.path + '/Hierarchy/SearchTree?' + $.param({ lookfor: keyword, hierarchyID: hierarchyID, + hierarchySource: hierarchySource, type: $("#treeSearchType").val() }) + "&format=true" }) - .done(function searchTreeAjaxDone(data) { - if (data.results.length > 0) { - $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); - var jstree = $('#hierarchyTree').jstree(true); - jstree.close_all(); - for (var i = data.results.length; i--;) { - var id = htmlEncodeId(data.results[i]); - jstree._open_to(id); - } - for (var j = data.results.length; j--;) { - var tid = htmlEncodeId(data.results[j]); - $('#hierarchyTree').find('#' + tid).addClass('jstree-search'); + .done(function searchTreeAjaxDone(data) { + if (data.results.length > 0) { + $('#hierarchyTree').find('.jstree-search').removeClass('jstree-search'); + var jstree = $('#hierarchyTree').jstree(true); + jstree.close_all(); + for (var i = data.results.length; i--;) { + var id = htmlEncodeId(data.results[i]); + jstree._open_to(id); + } + for (var j = data.results.length; j--;) { + var tid = htmlEncodeId(data.results[j]); + $('#hierarchyTree').find('#' + tid).addClass('jstree-search'); + } + changeNoResultLabel(false); + changeLimitReachedLabel(data.limitReached); + } else { + changeNoResultLabel(true); } - changeNoResultLabel(false); - changeLimitReachedLabel(data.limitReached); - } else { - changeNoResultLabel(true); - } - $('#treeSearchLoadingImg').addClass('hidden'); - }); + $('#treeSearchLoadingImg').addClass('hidden'); + }); } } @@ -147,19 +148,23 @@ function buildTreeWithXml(cb) { hierarchyID: hierarchyID, id: recordID, context: hierarchyContext, + hierarchySource: hierarchySource, mode: 'Tree' } }) - .done(function getTreeDone(xml) { - var nodes = buildJSONNodes($(xml).find('root')); - cb.call(this, nodes); - }); + .done(function getTreeDone(xml) { + var nodes = buildJSONNodes($(xml).find('root')); + cb.call(this, nodes); + }); } $(document).ready(function hierarchyTreeReady() { // Code for the search button hierarchyID = $("#hierarchyTree").find(".hiddenHierarchyId")[0].value; recordID = $("#hierarchyTree").find(".hiddenRecordId")[0].value; + hierarchySource = $("#hierarchyTree").find(".hiddenHierarchySource"); + hierarchySource = hierarchySource.length ? hierarchySource[0].value : 'Solr'; + htmlID = htmlEncodeId(recordID); hierarchyContext = $("#hierarchyTree").find(".hiddenContext")[0].value; var inLightbox = $("#hierarchyTree").parents("#modal").length > 0; @@ -190,6 +195,7 @@ $(document).ready(function hierarchyTreeReady() { var tree = $("#hierarchyTree").jstree(true); tree.select_node(htmlID); // finc-specific: opens full hierarchy tree by default #14067 - CK + // tree._open_to(htmlID); tree.open_all(); if (!inLightbox && hierarchyContext === "Collection") { @@ -212,11 +218,6 @@ $(document).ready(function hierarchyTreeReady() { scrollToClicked(); }) - - .on('after_open.jstree', function (e, data) { - $(".jstree-anchor").removeAttr("title"); - }) - .jstree({ plugins: ['search', 'types'], core: { @@ -225,7 +226,8 @@ $(document).ready(function hierarchyTreeReady() { url: VuFind.path + '/Hierarchy/GetTreeJSON', data: { hierarchyID: hierarchyID, - id: recordID + id: recordID, + hierarchySource: hierarchySource }, statusCode: { 200: function jsTree200Status(json /*, status, request*/) { @@ -244,7 +246,7 @@ $(document).ready(function hierarchyTreeReady() { types: { record: { // finc-specific: remove file icon, insert via SCSS -- CK -// icon: 'fa fa-file-o' + // icon: 'fa fa-file-o' }, collection: { icon: 'fa fa-folder' diff --git a/themes/finc/js/lightbox.js b/themes/finc/js/lightbox.js index 8cdfbe87fe9d0cec2d198dd47dc4231c84df65ca..c6c302fba6384a2502df709fa60321fd712bf9c1 100644 --- a/themes/finc/js/lightbox.js +++ b/themes/finc/js/lightbox.js @@ -9,6 +9,7 @@ VuFind.register('lightbox', function Lightbox() { var refreshOnClose = false; var _modalParams = {}; // Elements + // finc: keep _origin - see #17984 var _modal, _modalBody, _clickedButton, _origin = null; // Utilities function _storeClickedStatus() { @@ -42,12 +43,14 @@ VuFind.register('lightbox', function Lightbox() { } // Public: Present an alert + // finc: keep role='alert' - see #18988 and #18790 function showAlert(message, _type) { var type = _type || 'info'; _html('<div class="flash-message alert alert-' + type + '" role="alert">' + message + '</div>' + '<button class="btn btn-default" data-dismiss="modal">' + VuFind.translate('close') + '</button>'); _modal.modal('show'); } + // finc: keep role='alert' - see #18988 and #18790 function flashMessage(message, _type) { var type = _type || 'info'; _modalBody.find('.flash-message,.fa.fa-spinner').remove(); @@ -140,7 +143,9 @@ VuFind.register('lightbox', function Lightbox() { // Add lightbox GET parameter if (!obj.url.match(/layout=lightbox/)) { var parts = obj.url.split('#'); - obj.url = parts[0].indexOf('?') < 0 ? parts[0] + '?' : parts[0] + '&'; + obj.url = parts[0].indexOf('?') < 0 + ? parts[0] + '?' + : parts[0] + '&'; obj.url += 'layout=lightbox'; if (_currentUrl) { obj.url += '&lbreferer=' + encodeURIComponent(_currentUrl); @@ -152,11 +157,7 @@ VuFind.register('lightbox', function Lightbox() { .done(function lbAjaxDone(content, status, jq_xhr) { var errorMsgs = []; var flashMessages = []; - if (jq_xhr.status === 204) { - // No content, close lightbox - close(); - return; - } else if (jq_xhr.status !== 205) { + if (jq_xhr.status !== 205) { var testDiv = $('<div/>').html(content); errorMsgs = testDiv.find('.flash-message.alert-danger:not([data-lightbox-ignore])'); flashMessages = testDiv.find('.flash-message:not([data-lightbox-ignore])'); @@ -178,7 +179,8 @@ VuFind.register('lightbox', function Lightbox() { // - not a failed login if ( obj.method && ( - obj.url.match(/catalogLogin/) || obj.url.match(/MyResearch\/(?!Bulk|Delete|Recover)/) + obj.url.match(/catalogLogin/) + || obj.url.match(/MyResearch\/(?!Bulk|Delete|Recover)/) ) && flashMessages.length === 0 ) { @@ -262,37 +264,29 @@ VuFind.register('lightbox', function Lightbox() { * data-lightbox-title = Lightbox title (overrides any title the page provides) */ _constrainLink = function constrainLink(event) { - /** - * Fixme: TEST correct Lightbox behavior as in #13088 - CK - */ - if (typeof $(this).data('lightboxReset') !== 'undefined') { - VuFind.lightbox.reset(); - } var $link = $(this); - if (typeof $link.data('lightboxIgnore') !== 'undefined' || - typeof $link.attr('href') === 'undefined' || - $link.attr('href').charAt(0) === '#' || - $link.attr('href').match(/^[a-zA-Z]+:[^/]/) || // ignore resource identifiers (mailto:, tel:, etc.) - (typeof $link.attr('target') !== 'undefined' && - ( - $link.attr('target').toLowerCase() === '_new' || - $link.attr('target').toLowerCase() === 'new' - ) - ) + if (typeof $link.data("lightboxIgnore") != "undefined" + || typeof $link.attr("href") === "undefined" + || $link.attr("href").charAt(0) === "#" + || $link.attr("href").match(/^[a-zA-Z]+:[^/]/) // ignore resource identifiers (mailto:, tel:, etc.) + || (typeof $link.attr("target") !== "undefined" + && ( + $link.attr("target").toLowerCase() === "_new" + || $link.attr("target").toLowerCase() === "new" + )) ) { return true; } if (this.href.length > 1) { event.preventDefault(); - var obj = {url: $(this).data('lightboxHref') || this.href}; - if ('string' === typeof $(this).data('lightboxPost')) { + var obj = {url: $(this).data('lightbox-href') || this.href}; + if ("string" === typeof $(this).data('lightbox-post')) { obj.type = 'POST'; - obj.data = $(this).data('lightboxPost'); + obj.data = $(this).data('lightbox-post'); } - _lightboxTitle = $(this).data('lightbox-title'); + _lightboxTitle = $(this).data('lightbox-title') || false; _modalParams = $(this).data(); VuFind.modal('show'); - _origin = $(this); ajax(obj); _currentUrl = this.href; return false; @@ -436,6 +430,7 @@ VuFind.register('lightbox', function Lightbox() { } } } + // finc: restore focus after deleting records, #20379 function setOrigin(origin) { _origin = origin; } @@ -496,6 +491,7 @@ VuFind.register('lightbox', function Lightbox() { }); }); } + function reset() { _html(VuFind.translate('loading') + '...'); _originalUrl = false; @@ -510,6 +506,7 @@ VuFind.register('lightbox', function Lightbox() { if (VuFind.lightbox.refreshOnClose) { VuFind.refreshPage(); } else { + // finc: keep 'unbindFocus()' unbindFocus(); this.setAttribute('aria-hidden', true); _emit('VuFind.lightbox.closing'); @@ -518,7 +515,7 @@ VuFind.register('lightbox', function Lightbox() { _modal.on('hidden.bs.modal', function lightboxHidden() { VuFind.lightbox.reset(); _emit('VuFind.lightbox.closed'); - // set focus back on launching element + // finc: set focus back on launching element if (_origin !== null && _origin !== 'undefined') { _origin.focus(); } @@ -552,6 +549,7 @@ VuFind.register('lightbox', function Lightbox() { render: render, // Reset reset: reset, + // finc: restore focus after deleting records, #20379 setOrigin: setOrigin, // Init init: init diff --git a/themes/finc/js/openurl.js b/themes/finc/js/openurl.js index 32456f36e7c04290abecd394f11d02e837abd81a..6b69fbd884c4a6dc93cd2d4f394d3340632bccd7 100644 --- a/themes/finc/js/openurl.js +++ b/themes/finc/js/openurl.js @@ -18,9 +18,7 @@ VuFind.register('openurl', function OpenUrl() { }) .fail(function getResolverLinksFail(response, textStatus) { $target.removeClass('ajax_availability').addClass('text-danger').empty(); - if (textStatus === 'abort' || typeof response.responseJSON == 'undefined') { - return; - } + if (textStatus === 'abort' || typeof response.responseJSON == 'undefined') { return; } $target.append(response.responseJSON.data); }); } @@ -47,8 +45,8 @@ VuFind.register('openurl', function OpenUrl() { } } -// Assign actions to the OpenURL links. This can be called with a container e.g. when -// combined results fetched with AJAX are loaded. + // Assign actions to the OpenURL links. This can be called with a container e.g. when + // combined results fetched with AJAX are loaded. function init(_container) { var container = _container || $('body'); // assign action to the openUrlWindow link class @@ -70,11 +68,10 @@ VuFind.register('openurl', function OpenUrl() { } else { new Hunt( container.find('.openUrlEmbed.openUrlEmbedAutoLoad a').toArray(), - {enter: embedOpenUrlLinks} + { enter: embedOpenUrlLinks } ); } } - return { init: init, embedOpenUrlLinks: embedOpenUrlLinks diff --git a/themes/finc/scss/_common.scss b/themes/finc/scss/_common.scss new file mode 100644 index 0000000000000000000000000000000000000000..6e2127f9f20139f7f62caae08ecd5167db32772f --- /dev/null +++ b/themes/finc/scss/_common.scss @@ -0,0 +1,292 @@ +// This file ist for common basic elements, such as +// empty containers, +// tooltips +// radiuses, +// pagination, +// helper classes ... + +// ***************************************************************** +// ************ Hide empty containers ****************************** +// ***************************************************************** + +// DO NEITHER INCLUDE <span> NOR <div> here: +// Dropdown carets are empty spans +// divs are used for slider track handles +h1, +h2, +h3, +h4, +label, +.label { + // Empty labels collapse automatically (not available in IE8) + &:empty { + display: none; + } +} + + +// ***************************************************************** +// ************ Tooltips ******************************************* +// ***************************************************************** + +// the original tooltips are black BG and very narrow, see customVariables +.tooltip-inner { + padding: $tooltip-inner-padding; +} + + +// ***************************************************************** +// ************ Helper Classes ************************************* +// ***************************************************************** + +// Screen reader-hidden content +// Override nav li position for screen reader content (in)visibility +.nav > li.sr-only { + position: absolute; +} + +// Class to replace 'class="hidden-sm hidden-md hidden-lg"' +.hidden-sm-up { + @media (min-width: $screen-sm-min) { + display: none; + } +} + +// Class to replace 'class="hidden-sm hidden-md"' +.hidden-sm-md { + @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { + display: none; + } +} + +// Class to replace 'class="hidden-xs hidden-md hidden-lg"' +.visible-sm-only { + @media (max-width: $screen-xs-max) { + display: none; + } + + @media (min-width: $screen-md-min) { + display: none; + } +} + +// Class to replace 'class="hidden-xs hidden-lg"' +.visible-sm-md-only { + @media (max-width: $screen-xs-max) { + display: none; + } + + @media (min-width: $screen-lg-min) { + display: none; + } +} + + +// Flex container for wrapping flex elements, such als header +.flex-container { + @media (min-width: $screen-sm-min) { + display: flex; + } + + // We need to reverse order of the header child elements (searchbox + navbar) on anything but mobile for the mobile menu to work as xpected + header & { + @media (min-width: $screen-sm-min) { + > *:first-child { + order: 2; + } + + > *:nth-child(2) { + order: 1; + } + } + } +} + +// Inline: force inline display +.inline { + display: inline; + + .template-name-editlist & { + display: inherit; + } +} + +// Floats +.left { + float: left !important; +} + +.right { + float: right !important; +} + + +// NO Gutter/Padding at all +.no-gutter-all { + padding: 0 !important; +} + +// No LEFT and RIGHT gutters +.no-gutter { + padding-left: 0 !important; + padding-right: 0 !important; + + > [class^='col-'] { + padding-left: 0; + padding-right: 0; + } +} + +// NO gutter TOP and BOTTOM (keep !important) +.no-gutter-tp-btm { + padding-bottom: 0 !important; + padding-top: 0 !important; +} + +// NO gutter BOTTOM +.no-gutter-btm { + padding-bottom: 0; +} + +// NO gutter LEFT +.no-gutter-l { + padding-left: 0; +} + +// NO gutter TOP +.no-gutter-t { + padding-top: 0; +} + +// NO gutter RIGHT +.no-gutter-r { + padding-right: 0 !important; +} + +// Gutter LEFT +.gutter-l, +.no-gutter-r.gutter-l { + padding-left: ($grid-gutter-width / 2); +} + +// Gutter BOTTOM +.gutter-btm { + padding-bottom: ($grid-gutter-width / 2); +} + +// Gutter RIGHT +.gutter-r { + padding-right: ($grid-gutter-width / 2); +} + +// Gutter TOP +.gutter-top { + padding-top: ($grid-gutter-width / 2); +} + +// Margin BOTTOM +.margin-btm { + margin-bottom: ($grid-gutter-width / 2); +} + +// Margin LEFT +.margin-l { + margin-left: ($grid-gutter-width / 2); +} + +// Margin LEFT on MOBILE only +.margin-l-xs { + @media (max-width: $screen-xs-max) { + margin-left: ($grid-gutter-width / 2); + } +} + +// Margin RIGHT +.margin-r { + margin-right: ($grid-gutter-width / 2); +} + +// Margin TOP +.margin-t { + margin-top: ($grid-gutter-width / 2); +} + +.margin-t-half { + margin-top: ($grid-gutter-width / 4); +} + +// NO margin BOTTOM +.no-margin-btm { + margin-bottom: 0; +} + +.margin-btm { + margin-bottom: ($grid-gutter-width / 2); +} + +.margin-btm-half { + margin-bottom: ($grid-gutter-width / 4); +} + +// NO margin LEFT +.no-margin-l { + margin-left: 0; +} + +// NO margin RIGHT +.no-margin-r { + margin-right: 0; +} + +// NO margin TOP +.no-margin-t, +.sidebar h4.no-margin-t { + margin-top: 0; +} + +// No-cover - prevent no cover images from being easily clickable +// and loading in lightbox, used in record - cover +.nocover { + cursor: default; +} + + +// NO bullets for LIs +// How to: Apply style to parent UL! +.no-bullet { + padding-left: 0; + + li { + list-style-type: none; + } +} + + +//// Width 100% +.w-100, +.form-control.w-100 { + width: 100%; +} + +// Color helper classes for development +.border { + &.red { + border: 1px solid $brand-danger; + } + + &.yellow { + border: 1px solid $state-warning-bg; + } + + &.orange { + border: 1px solid $brand-warning; + } + + &.green { + border: 1px solid $brand-success; + } + + &.blue { + border: 1px solid $brand-info; + } +} diff --git a/themes/finc/scss/_customFonts.scss b/themes/finc/scss/_customFonts.scss new file mode 100644 index 0000000000000000000000000000000000000000..d1a057ca81dcc0742f51703e7391550785d867fb --- /dev/null +++ b/themes/finc/scss/_customFonts.scss @@ -0,0 +1,20 @@ +// This file is for all font-related variables incl. line height + +// FONTS +//// Base font size +$font-size-base: 16px !default; +//// We use these to define default font stacks +$font-family-sans-serif: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif !default; +$font-family-serif: Georgia, Cambria, 'Times New Roman', Times, serif !default; +$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace !default; + +//// SET FONT SIZES using the following pattern! This is the only way, font sizes will be adapted proportionally +//// font-size: ($font-size-base * 1.5) +//// OR us the em-calculator function below, like so: font-size: em(14) +//// There are also the variables $font-size-large and $...small which are calculated +//// $font-size-base * 1.25 --> ~18px and $font-size-base * 0.85 --> ~12px + +$font-size-breadcrumbs: ($font-size-base * .85) !default; + +// Default line height +$line-height-base: 1.5 !default; diff --git a/themes/finc/scss/_customMixins.scss b/themes/finc/scss/_customMixins.scss index 460b1cce5117d8a23c1ab8ccd142af2425add8ad..a91b3762073299f474538804ff55088fcf5c28a7 100644 --- a/themes/finc/scss/_customMixins.scss +++ b/themes/finc/scss/_customMixins.scss @@ -1,29 +1,62 @@ -// For Default border style see Variables: $border-default-styles +// Use this for all finc-specific mixins + +// ***************************************************************** +// ************ Borders ******************************************** +// ***************************************************************** +// For Default border style see Variables: $border-default-styles // Right-hand border on Sidebar? Only activate when border butts right border, leave commented out for standard border @mixin right-border-on-sidebar($border-right-width) { + // For $border-right-width: SEE customVariables border-right-width: $border-right-width; } -// for $border-right-width: SEE customVariables - // Activate when Sidebar ought to be pulled to the right edge/border @mixin pull-sidebar-to-right($margin-right-width) { + // for $margin-right-width: SEE customVariables margin-right: $margin-right-width; } -// for $margin-right-width: SEE customVariables +// ***************************************************************** +// ************ Pagination and search tools alignment ************** +// ***************************************************************** -//// Pagination (Search results) AND searchtools (Search results) -//// Default is left, we use centered alignment. If left is desired, -//// comment out next mixing and '.pagination ...' and 'searchtools ...' in compiled.scss +// Pagination (Search results) AND searchtools (Search results) +// Default is left, we use centered alignment. If left is desired, +// comment out next mixing and '.pagination ...' and 'searchtools ...' in compiled.scss @mixin content-centered-display-as-table { display: table; margin: 1.5em auto; } +// ***************************************************************** +// ************ Select elements - custom look ********************** +// ***************************************************************** +@mixin select-element-custom-look { + // prevent standard look in Firefox and Chrome + -moz-appearance: none !important; + -webkit-appearance: none !important; + background-position: 100% center; + background-repeat: no-repeat; + background-color: $select-bg-color; + // This is the litte down arrow + background-image: $select-default-arrow; + border: $select-default-border-styles; + border-radius: $select-default-border-radius; + height: $select-default-height; + min-width: $select-default-min-width; + padding: $select-default-padding; +} + +// ***************************************************************** +// ************ Accessibility: Outline, sr-only ... **************** +// ***************************************************************** + // Outline Mixin -- complements BS outline mixin in partials/mixin but does not rewrite it -@mixin outline($size: $outline-default-size, $color: $outline-default-color, $style: $outline-default-style) { +@mixin outline( + $size: $outline-default-size, + $color: $outline-default-color, + $style: $outline-default-style) { outline: $style $size $color; } @@ -39,8 +72,10 @@ width: auto; } -// Responsive data tables mixin - +// ***************************************************************** +// ************ Responsive data tables ***************************** +// ***************************************************************** +// This breaks table down into more readable chunks on small devices @mixin table-resp-data { thead, tbody, diff --git a/themes/finc/scss/_customVariables.scss b/themes/finc/scss/_customVariables.scss index 24e6a91e497583bba2a0a5a8b7c307d04615d495..0bd4ec8a795e6f005473a50588d77ea078df253d 100644 --- a/themes/finc/scss/_customVariables.scss +++ b/themes/finc/scss/_customVariables.scss @@ -1,137 +1,170 @@ -// see _variables.scss for default values - -// FONTS -//// Base font size -$font-size-base: 16px !default; -//// We use these to define default font stacks -$font-family-sans-serif: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif !default; -$font-family-serif: Georgia, Cambria, 'Times New Roman', Times, serif !default; -$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace !default; - -//// SET FONT SIZES using the following pattern! This is the only way, font sizes will be adapted proportionally -//// font-size: ($font-size-base * 1.5) -//// OR us the em-calculator function below, like so: font-size: em(14) -//// There are also the variables $font-size-large and $...small which are calculated -//// $font-size-base * 1.25 --> ~18px and $font-size-base * 0.85 --> ~12px - -$font-size-breadcrumbs: ($font-size-base * .85) !default; - -// Default line height -$line-height-base: 1.5 !default; - -// COLORS -$brand-primary: #204563 !default; -$brand-secondary: #e7e7e7 !default; -$brand-danger: #cc4b37 !default; // darker red = #a94442 +// Use this file to set variables for your theme or re-define variables from parent themes +// see variables.scss for default values + + +// ***************************************************************** +// ************ Imports ******************************************** +// ***************************************************************** +//@import '../../bootstrap3/scss/bootstrap'; + +// Import parent theme variables to have them all available here +// @import '../../bootstrap3/scss/vendor/bootstrap/variables'; +@import '../../bootstrap3/scss/vendor/font-awesome/variables'; + +@import 'customFonts'; + + +// ***************************************************************** +// ************ Common colors referenced elsewhere in the code ***** +// ***************************************************************** + +$white: #fff !default; +$ghost: #fafafa !default; +$snow: #f9f9f9 !default; // use $snow preferentially, rather than $ghost, $vapor ... +$vapor: #f6f6f6 !default; +$white-smoke: #f5f5f5 !default; +$silver: #efefef !default; +$smoke: #eee !default; +$gainsboro: #ddd !default; +$iron: #ccc !default; +$base: #aaa !default; +$aluminum: #999 !default; +$jumbo: #888 !default; +$monsoon: #777 !default; +$steel: #666 !default; +$charcoal: #555 !default; +$tuatara: #444 !default; +$oil: #333 !default; +$jet: #222 !default; +$black: #000 !default; + +$red: #f00 !default; + +// ***************************************************************** +// ************ Theme basic colors ********************************* +// ***************************************************************** + +$brand-primary: #204563 !default; +$brand-secondary: #e7e7e7 !default; +$brand-danger: #cc4b37 !default; // darker red = #a94442 $brand-danger-transparent: transparentize($brand-danger, .1) !default; // used e.g. for invalid form fields -$brand-info: #083152 !default; -$brand-success: #085218 !default; // darker green = #3c763d -$brand-warning: #f08a24 !default; +$brand-info: #083152 !default; +$brand-success: #085218 !default; // darker green = #3c763d +$brand-warning: #f08a24 !default; $traffic-light-yellow: #ff0 !default; +$road-sign-yellow: #fdda16 !default; +// Global background color for active items (e.g., navs or dropdowns). +$component-active-bg: $brand-primary !default; -$white: #fff; -$ghost: #fafafa; -$snow: #f9f9f9; -$vapor: #f6f6f6; -$white-smoke: #f5f5f5; -$silver: #efefef; -$smoke: #eee; -$gainsboro: #ddd; -$iron: #ccc; -$base: #aaa; -$aluminum: #999; -$jumbo: #888; -$monsoon: #777; -$steel: #666; -$charcoal: #555; -$tuatara: #444; -$oil: #333; -$jet: #222; -$black: #000; -$btn-default-color: $oil !default; +// Disabled colors +// for 'button disabled' colors see below +$default-disabled-color: $gainsboro !default; +$pagination-disabled-color: $default-disabled-color !default; -// Close modal and adv search buttons -$modal-close-bg: $white !default; -$modal-close-bg-hover: $white !default; -$modal-close-color: $btn-default-color !default; -$modal-close-color-hover: $brand-danger !default; -//// Anchor/Link text decoration -$textdecoration-anchor: none !default; -// needs to be overriden in catalogues if visited color does not fit - AP -$a-visited-color: $steel !default; -//// FORM FEEDBACK states colors and, by default, alerts colors (i.e. alerts are -//// defined like so '$alert-success-bg: $state-success-bg;') -$state-light-text-on-dark: $white !default; -$state-success-text: $state-light-text-on-dark !default; -$state-success-bg: $brand-success !default; -//$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default; +// ***************************************************************** +// ************ General dimensions ********************************* +// ***************************************************************** +// The general gutter width (padding between columns) is calculated +// like so: '$grid-gutter-width / 2' +// which gives you the left or right gutter width; this can be taken +// further by using '$grid-gutter-width / 1.5' etc +$grid-gutter-width: 30px !default; +$half-gutter: ($grid-gutter-width / 2); +$quarter-gutter: ($grid-gutter-width / 4); -$state-info-text: $state-light-text-on-dark !default; -$state-info-bg: $brand-info !default; -// $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default; +// Navigation elements default height: +// This height _must_ be adapted to the button height +// (.btn, .search-filter-toggle {}), which changes with +// default font size; apply to navigation elements such as select dropdowns +$navigation-element-default-height: 38px !default; -$state-warning-text: $state-light-text-on-dark !default; -$state-warning-bg: $brand-warning !default; -$alert-warning-bkg: $brand-warning !default; -// $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default; +// Top-padding for #content -> .row first-of-type (and .sidebar) +// - change this value to change both; +// sidebar gets an additional padding on half the content-top-padding +// for better horizontal alignment +$content-top-padding: $grid-gutter-width / 2 !default; +$content-top-padding-edit-list-xs: $grid-gutter-width / 2 !default; +$mainbody-sidebar-top-padding: $content-top-padding !default; +$mainbody-sidebar-top-padding-xs: 0 !default; -$state-danger-text: $state-light-text-on-dark !default; // NOTE: this renders the text white -- requires red background! Used for remove-searchgroup button (adv search) and overdues -$state-danger-bg: $brand-danger !default; -// $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default; +// Sidebar item padding +$sidebar-item-padding: .75em 1em !default; -$state-link-hover-bg: $white !default; -$state-link-hover-color: $black !default; +// Table cell padding - adjust in themes to avoid content jumps when switching tabs +// $table-cell-padding: 5px !default; -//// ALERTS (see states, above, and compiled.scss - Code needs to go there to overwrite defaults) +// Thumbnail sizes for media items: search results, list entries, record views +$thumbnail-width-small: 60px !default; +$thumbnail-width-medium: 100px !default; +$thumbnail-width-large: 160px !default; -//// Highlighter in search results - activate and change BG here -// mark, -// .mark { -// background-color: $state-warning-bg; -// } -//// FORM ELEMENTS -$select-bg-color: $ghost !default; -////// Inputs with labels -- top margin for better alignment -$inputs-top-margin: .2rem; +// ***************************************************************** +// ************ Radii ********************************************** +// ***************************************************************** +// Get rid of all round corners +$border-radius-base: 0 !default; +$border-radius-large: 0 !default; +$border-radius-small: 0 !default; + +$pager-border-radius: 0 !default; + +$badge-border-radius: 0 !default; + +$modal-close-br: 0 !default; + +// labels.scss uses no variables, therefore it must overwrite values in compiled.scss!! +// more on Labels, below +// Fixme when done more elegantly in BS +$label-radius: 0 !default; + +// Define radii for bulk action toolbar, add-to-/remove-from, reset-filter and other buttons, see buttons.scss +$toolbar-button-radius: 0 !default; + + + + +// ***************************************************************** +// ************ Border styles ************************************** +// ***************************************************************** -//// BORDERS $border-color: $iron !default; -////// Note that the nav-tabs have a different border color, use the following variable in your SCSS to change it + +// Note that the nav-tabs have a different border color, +// use the following variable in your SCSS to change it // $nav-tabs-border-color: $gainsboro !default; // defined in BS _variables.scss // Default border styles -$border-default-styles: 1px solid $border-color !default; +$border-default-styles: 1px solid $border-color !default; // $border-color defined in customColors.scss $border-default-white: 1px solid $white !default; -// Width and position of Sidebar border (see customMixins.scss) -- change to "0" and "-15px" only for sidebar on the right, butting on right border +// Width and position of Sidebar border (see customMixins.scss) +// change to "0" and "-15px" only for sidebar on the right, butting on right border $border-right-width: 1px !default; $margin-right-width: inherit !default; -//// Outlines (focus, accessibility etc.) -- overriding default bootstrap accessibility plugin scss variables -$outline-default-color: $black !default; -$outline-default-size: 1px !default; -$outline-default-style: solid !default; -//// PAGER -$pagination-disabled-color: $gainsboro !default; -//// BUTTONS -$btn-active-hover-bg: $iron !default; -$btn-badge-hover-color: $brand-secondary !default; -$btn-info-hover-bg: lighten($brand-info, 25%) !default; -$btn-info-hover-color: $black !default; -$btn-info-active-hover-bg: lighten($brand-info, 20%) !default; -////// Use darker text color when default-buttons are used on white BG (new class: bth-transparent -////// for updateCart in search results, date-range slider in sidebar and adv search -////// NOTE that the hover states use the default color too! + +// ***************************************************************** +// ************ Button styles ************************************** +// ***************************************************************** + +$btn-default-color: $oil !default; + +// Use darker text color when default-buttons are used on white BG (new class: bth-transparent +// for updateCart in search results, date-range slider in sidebar and adv search +// NOTE that the hover states use the default color too! + +// $btn-primary-color: $btn-primary-color !default; // already defined this way in BS _variables, use this variable to change it +// $btn-primary-bg: $brand-primary !default; // already defined this way in BS _variables, use this variable to change it $btn-primary-color: $white !default; $btn-primary-bg: $brand-primary !default; $btn-primary-hover-bg: $brand-secondary !default; @@ -139,10 +172,11 @@ $btn-primary-hover-color: $brand-primary !default; $btn-primary-disabled-color: $brand-secondary !default; $btn-primary-disabled-hover-bg: $brand-secondary !default; $btn-primary-disabled-hover-color: $brand-primary !default; -// $btn-primary-color: $btn-primary-color !default; // already defined this way in BS _variables, use this variable to change it -// $btn-primary-bg: $brand-primary !default; // already defined this way in BS _variables, use this variable to change it + $btn-secondary-border-color: $steel !default; + $btn-success-active-hover-bg: $brand-success !default; + $btn-transparent-color: $steel !default; $btn-transparent-active-color: $white !default; $btn-transparent-bg: transparent !default; @@ -154,162 +188,1196 @@ $btn-language-hover-bg: $brand-primary !default; $btn-language-active-hover-bg: $steel !default; $btn-language-hover-color: $white !default; -$btn-header-nav-medium-only-padding: 6px 2px !default; +$btn-active-hover-bg: $iron !default; +$btn-badge-hover-color: $brand-secondary !default; +$btn-info-hover-bg: lighten($brand-info, 25%) !default; +$btn-info-hover-color: $black !default; +$btn-info-active-hover-bg: lighten($brand-info, 20%) !default; + +// Close modal button +$modal-close-bg: $white !default; +$modal-close-bg-hover: $white !default; +$modal-close-color: $btn-default-color !default; +$modal-close-color-hover: $brand-danger !default; + +// Navbar toggler +$navbar-default-toggle-hover-bg: $white !default; + +// Accordion/collapse toggler +$toggler: $snow !default; +$toggler-active: lighten($snow, 1%) !default; + + +// Navbar button sizes +$btn-header-nav-padding-sm: 6px 2px !default; +$btn-header-nav-focus-hover-background-color: $component-active-bg !default; +$btn-header-nav-focus-hover-color: $white !default; +$btn-header-nav-focus-hover-outline: 1px $white dotted !default; + +// Padding for toolbar (sidebar) buttons in record view/detail view +// for 'Add-to-Bookbag', see below +$record-view-toolbar-button-padding: .5rem !default; + +// Align tops of elements to adv search button-content in same row +// (e.g. header: searchbox content + navigation on right-hand side) +$button-top-padding: 6px !default; + +// For radii, see above + + + + +// ***************************************************************** +// ************ Feedback state colors ****************************** +// ************ Alert colors *************************************** +// ***************************************************************** +// NOTE: alerts are defined like so '$alert-success-bg: $state-success-bg;' +// - therefore changing state colors will change the corresponding alerts too +$state-light-text-on-dark: $white !default; + +$state-success-text: $state-light-text-on-dark !default; +$state-success-bg: $brand-success !default; +//$state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default; + +$state-info-text: $state-light-text-on-dark !default; +$state-info-bg: $brand-info !default; +// $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default; + +$state-warning-text: $state-light-text-on-dark !default; +$state-warning-bg: $brand-warning !default; +$alert-warning-bkg: $brand-warning !default; +// $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default; + +$state-danger-text: $state-light-text-on-dark !default; // NOTE: this renders the text white -- requires red background! Used for remove-searchgroup button (adv search) and overdues +$state-danger-bg: $brand-danger !default; +// $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default; + +// Links inside individual alert type boxes +$state-link-alert-warning-color: $oil !default; + +$state-link-hover-bg: $white !default; +$state-link-hover-color: $black !default; + -// Padding for toolbar buttons in record view/detail view -$record-view-toolbar-button-padding: .5rem; -//// BADGES (elements in sidebar that hold the hit numbers) + +// ***************************************************************** +// ************ Highlighter in search results colors *************** +// ***************************************************************** +// activate and change BG here +// mark, +// .mark { +// background-color: $state-warning-bg; +// } + + + + +// ***************************************************************** +// ************ Tooltips colors ************************************ +// ***************************************************************** +// default bg is black +$tooltip-color: $snow !default; +$tooltip-bg: lighten($brand-primary, 20%) !default; +$tooltip-arrow-color: $tooltip-bg !default; +$tooltip-inner-padding: .75em !default; + + + + +// ***************************************************************** +// ************ Badge styles *************************************** +// ***************************************************************** + +// Badges are used for elements in MyAccount menu that hold the hit numbers and for the numbers listed in the result list. +$badge-font-weight: normal !default; $badge-bg: transparent !default; $badge-color: $jet !default; -$badge-font-weight: normal !default; $badge-link-color: $brand-danger !default; $badge-link-hover-color: saturate($brand-danger, 50%) !default; -////// Sidebar Badge/fa Colors +// sidebar badge icon colors $sidebar-badge-fa-color: darken($brand-secondary, 40%) !default; -//// Arrows -$slick-arrow-hover-color: red !default; -//// Navbar toggler -$navbar-default-toggle-hover-bg: $white !default; -//// Accordion/Collapse Toggler -$toggler: $snow !default; -$toggler-active: lighten($snow, 1%) !default; -//// Dropdown Background - this is used for ALL dropdowns, incl. the language selector; -//// originally defined under BS _variables.scss, and used in _dropdowns.scss; you could use a value such as +// ***************************************************************** +// ************ Alerts/Feedback state ****************************** +// ***************************************************************** + +$state-link-text-decoration: underline !default; + +$state-inside-holding-info-margin-left: 1rem !default; +$state-inside-holding-info-margin-right: 1rem !default; +$state-inside-holding-info-margin-top: 1rem !default; + + + + +// ***************************************************************** +// ************ Default anchor/link styles general ***************** +// ***************************************************************** +$link-color: $brand-primary !default; +$link-hover-color: darken($link-color, 15%) !default; +$link-text-decoration: none !default; +$link-hover-decoration: underline !default; +$link-on-dark-bg-color: invert($link-color) !default; + +// see also '$state-link-hover-color' below + + + + +// ***************************************************************** +// ************ Outlines (focus, accessibility etc.) *************** +// ***************************************************************** +// override default bootstrap accessibility plugin scss variables +// there is a mixin @mixin outline($size) {outline: $outline-default-style $size $outline-default-color;} +$outline-default-color: $black !default; +$outline-default-size: 1px !default; +$outline-default-style: solid !default; + + + + +// ***************************************************************** +// ************ Form elements ************************************** +// ***************************************************************** + +// Validator colors +$validator-color: $brand-danger !default; + +$form-control-box-shadow: none !default; +$form-control-xs-max-width: em(400px) !default; + + +// ************ Form elements: Fieldsets *************************** +$fielset-default-border: $border-default-styles !default; + +// Adv Search fieldsets general +$fieldset-advanced-srch-margin-top: $grid-gutter-width !default; +$fieldset-advanced-srch-padding-bottom: ($grid-gutter-width / 2) !default; +$fieldset-advanced-srch-padding-left: ($grid-gutter-width / 2) !default; +$fieldset-advanced-srch-padding-right: ($grid-gutter-width / 2) !default; +// Adv Search top-most fieldset with search terms +$fieldset-advanced-srch-search-terms-border: 0 !default; +$fieldset-advanced-srch-search-terms-float: unset !default; +$fieldset-advanced-srch-search-terms-margin-top: 0 !default; +$fieldset-advanced-srch-search-terms-padding-bottom: 0 !default; +$fieldset-advanced-srch-search-terms-padding-left: 0 !default; +$fieldset-advanced-srch-search-terms-width: unset !default; +$fieldset-advanced-srch-search-terms-xs-margin-bottom: $half-gutter !default; +$fieldset-advanced-srch-search-terms-xs-padding-bottom: 5px !default; + +// Edit favorites list +$fieldset-edit-favorites-list-border: 0 !default; +$fieldset-edit-favorites-list-padding: 0 15px !default; +$fieldset-edit-favorites-list-xs-float: none !default; +$fieldset-edit-favorites-list-legend-font-size: $font-size-base !default; +$fieldset-edit-favorites-list-legend-margin-left: -14px !default; + +// Adv Search Date Range slider +$fieldset-date-range-margin-left: ($grid-gutter-width / 2) !default; +$fieldset-date-range-margin-right: 0 !default; +$fieldset-date-range-width: 30.25% !default; +$fieldset-date-range-xs-sm-margin-left: 0 !default; +$fieldset-date-range-xs-sm-width: auto !default; + + +// ************ Form elements: Legends ***************************** +$legend-border: 0 !default; +$legend-font-size: ($font-size-base * 1.25) !default; +$legend-margin-bottom: 0 !default; + +$legend-padding-left: ($grid-gutter-width / 2) !default; +$legend-padding-right: ($grid-gutter-width / 2) !default; +$legend-width: auto !default; + +// Recent acquisitions search form (search/NewItem) +$legend-padding-left-recent-acquisitions: 0 !default; + + +// ************ Form elements: Labels ****************************** +$label-font-weight: normal !default; + +$label-adv-search-form-padding-left: 0 !default; +$label-adv-search-form-padding-right: 0 !default; +$label-adv-search-form-display: block !default; + +$label-edit-favorites-list-padding-left: 0 !default; + + +// ************ Form elements: Inputs ****************************** +// Default Bootstrap variable for input focus color (BS default is #66afe9) +$input-border-focus: $brand-primary !default; +// Inputs with labels -- top margin for better alignment +$input-top-margin: .2rem !default; +// Invalid input AND textarea elements +$input-textarea-invalid-margin-right: 2px !default; +// Invalid input highlighting on focus +$input-invalid-focus-border-color: $brand-danger-transparent !default; +$input-invalid-focus-box-shadow: 0 0 2px 1px $brand-danger-transparent !default; +// Max width on xs to prevent bleed for input, select, textarea +$input-select-textarea-xs-max-width: 100% !default; +// Styles for type='email' and type='text' +$input-email-text-border: $border-default-styles !default; +$input-email-text-padding: ($grid-gutter-width / 4) !default; +// Styles for inputs in header and search form +$input-header-search-form-border: $border-default-white !default; +$input-header-search-form-box-shadow: none !default; +$input-header-search-form-border-outline-offset: -1px !default; + +$input-radio-edit-favorites-list-margin-left: 1.4rem !default; +$input-radio-edit-favorites-list-position: relative !default; + + +// ************ Form elements: Selects ***************************** +$select-bg-color: $snow !default; + +// The next two settings are used to remove the standard select down arrow +$select-remove-standard-down-arrow-background-image: none !default; +$select-remove-standard-down-arrow-height: auto !default; +// The next settings replaces the select down arrow +$select-default-arrow: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+) !default; +$select-default-border-styles: $border-default-styles !default; +$select-default-border-radius: 0 !default; +$select-default-height: $navigation-element-default-height !default; +$select-default-min-width: 5em !default; +$select-default-padding: em(4px) em(20px) em(4px) em(7px) !default; + +$select-in-header-border: 0 !default; +$select-search-form-sm-max-width: 140px !default; + + + + +// ***************************************************************** +// ************ Non-form Labels (result list etc.) ***************** +// ***************************************************************** +// The following is for labels in the result list, such as +// item format (e-book, book, ... - ['.result-formats .format']) +// or the labels in status (accessible as ... ['.status .label']) +$label-result-format-status-display: inline-block !default; // Default is inline +$label-result-format-status-font-weight: normal !default; // Default is bold +$label-result-format-status-margin-top: .75em !default; // for default see _labels.scss +$label-result-format-status-padding: .5em !default; // for default see _labels.scss +// for radius, see above, this is a radius NOT using variables in Bootstrap: + +// labels.scss uses no variables and '.format' extends it! +// Therefore we overwrite it here, even though this is should be variables-only file! +// Fixme when done more elegantly in BS +// Also, the display settings for labels aren't very useful since they use no variables either. +.label, +.result-formats .format { + border-radius: $label-radius; + display: $label-result-format-status-display; + font-weight: $label-result-format-status-font-weight; + margin-top: $label-result-format-status-margin-top; + padding: $label-result-format-status-padding; +} + + + + +// ***************************************************************** +// ************ Dropdowns ****************************************** +// ***************************************************************** +// Dropdown background is used for ALL dropdowns, incl. the language selector; +// originally defined under BS _variables.scss, and used in _dropdowns.scss; +// you could use values such as: + //// $dropdown-link-hover-color: lighten($brand-primary, 15%); //// $dropdown-link-hover-bg: lighten($brand-primary, 50%); -//// Other variables include $dropdown-link-active-color; $dropdown-link-active-bg; $dropdown-link-color ... -//// Tooltips (default bg is black) -$tooltip-color: $snow !default; -$tooltip-bg: lighten($brand-primary, 20%) !default; -$tooltip-arrow-color: $tooltip-bg !default; +// Other variables include + +//// $dropdown-link-active-color; +//// $dropdown-link-active-bg; +//// $dropdown-link-color ... + +$dropdown-bottom-padding: 0 !default; +$dropdown-top-padding: 0 !default; +$dropdown-box-shadow: none !default; + + +// ************ RVK Notation Dropdown ****************************** +$dropdown-rvk-toggler-color: inherit !default; +$dropdown-rvk-toggler-text-decoration: inherit !default; +$dropdown-rvk-toggler-text-decoration-active-focus-hover: inherit !default; +$dropdown-rvk-toggler-icon-content: $fa-var-angle-down !default; +$dropdown-rvk-toggler-icon-font-family: 'FontAwesome', sans-serif !default; +$dropdown-rvk-toggler-icon-padding-left: 2px !default; + +$dropdown-rvk-list-padding-inline-start: 1.5em !default; +$dropdown-rvk-list-padding-inline-start-xs-sm: 0 !default; +$dropdown-rvk-list-remove-bullet-points-list-style-type: none !default; +$dropdown-rvk-list-item-icon-content: $fa-var-level-down !default; +$dropdown-rvk-list-item-icon-font-family: 'FontAwesome', sans-serif !default; +$dropdown-rvk-list-item-icon-margin-right: .5em !default; + + + + +// ***************************************************************** +// ************ Accordions/Collapse elements *********************** +// ***************************************************************** +$accordion-toggler-display: block !default; +$accordion-toggler-padding: ($grid-gutter-width / 2) !default; +$accordion-toggler-text-align: left !default; +$accordion-toggler-width: 100% !default; + +// Add accordion open/close icons here +$accordion-toggler-icon-content-closed: '\f107' !default; +$accordion-toggler-icon-content-expanded: '\f106' !default; +$accordion-toggler-icon-content-expanded-color: darken($brand-secondary, 10%) !default; +$accordion-toggler-icon-float: right !default; +$accordion-toggler-icon-font-family: FontAwesome, sans-serif !default; + + + + +// ***************************************************************** +// ************ Slick slider elements ****************************** +// ************ Carousels ****************************************** +// ************ Channels ******************************************* +// ***************************************************************** + +// ************ Slick slider elements ****************************** +$slick-arrow-hover-color: $red !default; +// Pull channels prev + next buttons in +$slick-prev-left-position: 1rem !default; +$slick-prev-left-position-xs-sm: 0 !default; +$slick-next-right-position: 1rem !default; +$slick-next-right-position-xs-sm: 0 !default; + +// Icon size +$slick-next-prev-icon-font-size: 38px !default; + + +// ************ Carousels ****************************************** +$carousel-control-elements-color: $brand-primary !default; +$carousel-control-elements-text-shadow: none !default; + +$carousel-control-elements-left-right-background-image: none !default; +$carousel-control-elements-left-right-background-image-hover-background-image: none !default; +$carousel-control-elements-left-right-background-image-hover-color: lighten($brand-primary, 2%) !default; + +$similar-items-carousel-indicators-li-border-color: $brand-primary !default; +$similar-items-carousel-indicators-li-box-shadow: none !default; +$similar-items-carousel-indicators-li-height: $font-size-base * 1.5 !default; +$similar-items-carousel-indicators-li-width: $font-size-base * 1.5 !default; +$similar-items-carousel-indicators-active-box-shadow: none !default; + + + + + +// ***************************************************************** +// ************ Tables ********************************************* +// ***************************************************************** + +$table-border: 1px solid $brand-secondary !default; +$table-elements-border-top: 0 !default; +$table-layout: auto !default; +$table-in-tabs-width: 100% !default; + + + + + + +// ***************************************************************** +// ************ Headings ******************************************* +// ***************************************************************** + +$h1-font-size: 2rem !default; +// remove top margin to align with sidebars etc +$h1-first-of-type-margin-top: 0 !default; + + +$h2-font-size: 1.75rem !default; +$h2-font-size-sidebar: 1.5rem !default; +$h2-font-size-sidebar-xs-sm: 1.25rem !default; + +$h2-margin-left-sidebar-xs: $grid-gutter-width / 2 !default; +$h2-margin-left-sidebar-xs-sm-margin-top: 1.75rem !default; +$h2-first-of-type-margin-top-sidebar-sm-up: 0 !default; + -//// Hierarchy -$jstree-clicked-color: $steel; -$jstree-clicked-hover-color: $black; -$jstree-ocl-hover-before-color: $jumbo; +$h3-font-size: 1.5rem !default; +// H3 for favorites lists in MyAccount; +$h3-margin-bottom-fav-lists: .25rem !default; +$h3-margin-left-fav-lists: 1.5rem !default; + + +$h4-font-size: 1.25rem !default; +$h4-font-size-sidebar-sm-only: $font-size-base + 2px !default; +// H4 atop Sidebar - align to limit and sort +$h4-margin-top-sidebar: 0 !default; +$h4-margin-top-sidebar-sm-only: ($grid-gutter-width / 2) !default; + +$h4-text-align-sidebar-xs-sm: center !default; + + + + +// ***************************************************************** +// ************ Body and main container colors ********************* +// ***************************************************************** -//// ELEMENT COLORS $body-bg: $snow !default; //// Main content are bg $main-bg: $white !default; -//// Header and header elements colors + + + +// ***************************************************************** +// ************ Header and navbar ********************************* +// ***************************************************************** + +// Header and header elements colors $header-bg-color: $gainsboro !default; + $navbar-bg-color: $header-bg-color !default; +// BG color of the Hamburger dropdown on XS - make same color as active Hamburger menu +$navbar-bg-color-xs: $navbar-default-toggle-hover-bg !default; +$navbar-xs-openend-margin-bottom: 1rem !default; $navbar-fixed-bg-color: $header-bg-color !default; -//// Breadcrumbs -$breadcrumb-bg: $brand-primary !default; +// Header, navbar and breadcrumbs default margin for centered design - change here for full width +$header-navbar-breadcrumbs-margin: 0 auto !default; +$container-for-navbar-display-as: flex !default; +$container-for-navbar-flex-direction-xs: column !default; + +// Navbar gets a minimum-height of 50px by default, activate and change here! +// For fixed navbars ('navbar-fixed-top' in 'header.phtml'), the body requires +// a padding-bottom of the same height as the navbar because of postition.fixed +// When using 'affix' instead of 'navbar-fixed-top', +// set '$navbar-height', '$navbar-height-xs' and '$navbar-height-sm' to '=0' +// or set the paddings-top under 'body' in compiled.scss to '=0'; see also the comment in header.phtml +$navbar-height: 112px !default; // = 7 x 1rem //80px !default; +$navbar-height-sm: $navbar-height !default; + +// Set navbar-height for small devices, USE px as rem will throw an error in conjunction with _variables.scss +$navbar-height-xs: $navbar-height + 14px !default; +$navbar-min-height-adv-search: 3rem !default; +$navbar-max-height-xs: $navbar-height + 32px !default; + +$navbar-opened-on-xs-position: absolute !default; + +// Define hamburger menu (xs) +$hamburger-menu-icon-font-size: 150% !default; +$hamburger-menu-margin-bottom: 0 !default; +$hamburger-menu-margin-right: 0 !default; +$hamburger-menu-margin-top: 0 !default; + +$hamburger-menu-flex: 0 1 auto !default; +$hamburger-menu-margin-left-xs: 0 !default; +$hamburger-menu-margin-right-xs: 0 !default; + +// This is for the menu items in the header incl. language, My account etc +$header-menu-flex-sm-up: 1 0 40%; +$header-menu-flex-adv-srch-sm-up: 1 0 100%; +$header-menu-flex-order-sm-up: 3; +$header-menu-padding-top-sm-up: 6px; + + +$library-name-float: left !default; +$library-name-padding: ($grid-gutter-width / 4) ($grid-gutter-width / 2) ($grid-gutter-width / 4) 0 !default; +$library-name-height-xs: auto !default; +$library-name-padding-right-sm: ($grid-gutter-width / 4) !default; +$library-name-focus-hover-outline-offset: -2px !default; + +$library-name-img-height-sm: $navigation-element-default-height !default; +$library-name-img-height-max-width-sm: inherit !default; + + +$language-selector-dropdown-margin-right: 15px !default; +$language-selector-dropdown-menu-background-color: $navbar-bg-color !default; +$language-selector-dropdown-menu-border: 0 !default; +$language-selector-dropdown-menu-min-width: auto !default; +$language-selector-dropdown-menu-link-color: $link-color; +$language-selector-dropdown-menu-btn-padding-left: 0 !default; +$language-selector-dropdown-menu-btn-padding-right: 0 !default; +$language-selector-dropdown-menu-btn-focus-hover-background-color: $white !default; + +$language-selector-one-language-only-display: block !default; +$language-selector-one-language-only-margin-top: 0 !default; +$language-selector-one-language-only-position: relative !default; + +$language-selector-one-language-only-btn-background: transparent !default; +$language-selector-one-language-only-btn-background-color-xs: $navbar-bg-color-xs !default; +$language-selector-one-language-only-btn-color: $brand-primary !default; + +$language-selector-one-language-only-btn-float-sm: right !default; +$language-selector-one-language-only-btn-float-md: right !default; + +$language-selector-one-language-only-btn-padding-sm: 6px 0 !default; +$language-selector-one-language-only-btn-padding-md: 6px !default; +$language-selector-one-language-only-btn-padding-left-xs: 18px !default; +$language-selector-one-language-only-btn-width-xs: 100% !default; +$language-selector-one-language-only-btn-text-align-xs: left !default; + +// Exclamation triangle in header navigation (My Account) +$icon-exclamation-triangle-in-header-padding: 0 !default; + + + +// ***************************************************************** +// ************ Search box ************************************ +// ***************************************************************** +// Make search box container flexible - values should complement those +// of #header-collapse (i.e. the right-hand side navigation elements) +$search-container-right-padding: 0 !default; +$search-container-sm-max-left-padding: 0 !default; +$search-container-sm-up-flex: 0 1 auto !default; +$search-container-sm-only-flex: content !default; +$search-container-sm-up-width: auto !default; +$search-container-margin-bottom-xs: 1rem !default; + +// '.searchForm' is the class of the entire form itself +$search-form-display: flex !default; +$search-form-flex-wrap: wrap !default; +$search-form-justify-content: flex-start !default; +$search-form-margin-bottom: 0 !default; +$search-form-margin-top: 0 !default; +$search-form-padding-top: 0 !default; + +$search-form-sm-up-width: 100% !default; +// if you want to change the display from 'flex' to, say, +// 'block' for sm only devices, set this to 'block' or whatever +$search-form-sm-only-display-no-flex: flex !default; + +// The '.searchForm' input field is the actual search input, the select element is for 'media type' +$search-form-input-type-sm-max-display: flex !default; +$search-form-input-type-sm-max-flex-grow: 2 !default; +$search-form-input-type-sm-max-flex-shrink: 2 !default; + +$search-form-input-only-sm-max-flex-basis: 50% !default; +$search-form-select-only-sm-max-flex-basis: 20% !default; + +// Pls. beware that the next two settings affect the active-filters' buttons too +// (reset in 'header-active-filters.scss' -> button[id^='dropdown-toggle-']) +$search-form-btn-form-control-vertical-align: top !default; +$search-form-btn-form-control-float-xs: left !default; + +$search-form-advanced-search-button-border: 1px solid $oil !default; + + + +// ***************************************************************** +// ************ Advanced Search ************************************ +// ***************************************************************** + +// Advanced Search Terms +// Pls. note: +// In most instance we have MOVED the advanced search TERMS INTO the BREADCRUMBS area +// in order to keep the header ok on small devices. If this is the case in your instance, +// you can ignore the '$adv_search_terms...' settings. +$adv-search-terms-bg: $brand-secondary !default; +$adv-search-terms-border: 1px solid $brand-primary !default; +$adv-search-terms-bottom-margin: 0 !default; +$adv-search-terms-padding: .5em !default; +$adv-search-terms-width: 100% !default; + +// Advanced Search Links +$adv-search-links-border: 1px solid $brand-primary !default; +$adv-search-links-list-style: none !default; +$adv-search-links-margin: 0 !default; +$adv-search-links-padding: 0 !default; +$adv-search-links-top-margin-sm: 5px !default; + +$adv-search-links-anchor-top-border: 0 !default; +$adv-search-links-anchor-display: inline !default; +$adv-search-links-anchor-padding-left: .5em !default; + +$adv-search-links-anchor-border-bottom-sm-up: 0 !default; +$adv-search-links-anchor-border-right-sm-up: 1px solid $brand-primary !default; +$adv-search-links-anchor-display-sm-up: inline !default; +$adv-search-links-anchor-padding-right-sm-up: .5em !default; +$adv-search-links-sm-to-md-max-max-width: 400px !default; + + + + + +// ***************************************************************** +// ************ Active filters in header *************************** +// ***************************************************************** +// Outer container for filter names and reset button; +// we overwrite BS value 'display: flex;' so filters' container will fit +// in searchbox without squashing right-hand menu; +// Set this to 'flex' to maintain BS-theme behaviour +$active-filters-outer-container-display: unset !default; +// Remove left-padding of outer filters' container for alignment under 'Remove all' button +$active-filters-outer-filters-container-padding-left: 0 !default; + +// Variables for 'Active filters' and 'Remove all filters' in Header +// Use orange and black for sufficient color contrast +$search-filter-remove-all-color: $black !default; +$search-filter-remove-all-bg: $brand-warning !default; + +$search-filter-remove-text-decoration: none !default; + + +// Give these the same look on focus/hover as 'Remove all' +$search-filter-remove-hover-bg: $search-filter-remove-all-bg !default; +$search-filter-remove-hover-color: $search-filter-remove-all-color !default; + +$search-filter-values-remove-color: $white !default; + +$search-filters-margin-bottom: 4px !default; +$search-filters-padding: .5rem 5px !default; + + +$search-filter-remove-button-vertical-align: top !default; +$search-filter-remove-button-text-display: inline-block !default; +$search-filter-remove-button-text-max-width: 200px !default; +$search-filter-remove-button-text-overflow: hidden !default; +$search-filter-remove-button-text-text-overflow: ellipsis !default; +$search-filter-remove-button-text-white-space: nowrap !default; + +$search-filter-remove-icon: '\f00d' !default; +$search-filter-remove-icon-hover-color: $red !default; +$search-filter-remove-icon-vertical-align: top !default; +$search-filter-remove-icon-in-dropdown-position: absolute !default; +$search-filter-remove-icon-in-dropdown-distance-from-right: 1rem !default; + + +// Unset the vertical alignment from BS to align dropdowns AND indiv. buttons +// The relevant definition is '.searchForm .btn {vertical-align: top;}' +$search-filter-dropdown-button-vertical-align: unset !default; +$search-filter-dropdown-button-float: none !default; + +// ***************************************************************** +// ************ Breadcrumbs **************************************** +// ***************************************************************** + +$breadcrumb-bg: $brand-primary !default; +// Default is 'white' $breadcrumb-color: $btn-primary-color !default; -//// Link color for breadcrumb .active -$breadcrumb-active-color: $btn-primary-color !default; +// Link color for '.active' breadcrumb (last item in list) +// For yellow, you can activate something like +// $breadcrumb-active-color: $road-sign-yellow !default; +// Default is button collor +$breadcrumb-active-color: $btn-primary-color; + +$breadcrumb-divider-color: $breadcrumb-color !default; + +$breadcrumb-anchor-hover-bg: transparent !default; +$breadcrumb-anchor-hover-color: darken($breadcrumb-color, 10%) !default; + +// Separator between breadcrumb elements, use this variable to change it +// $breadcrumb-separator: '/' !default; +$breadcrumbs-advanced-results-top-padding: .6rem !default; +$breadcrumbs-bottom-margin: 0 !default; +// Hide breadcrumbs divider on xs +$breadcrumbs-divider-display-xs: none !default; +// take 2 px off for borders +$breadcrumbs-left-padding: ($grid-gutter-width / 2 - 2px) !default; +$breadcrumbs-top-margin: 0 !default; +$breadcrumbs-top-padding: 10px !default; + + + + + +// ***************************************************************** +// ************ Main content block ********************************* +// ***************************************************************** + +$content-xs-padding-top: 1.5rem !default; + + + + +// ***************************************************************** +// ************ Result list, Favorites and other lists ************* +// ***************************************************************** +// This is for all lists that use the result list view or elements thereof. +// For Search Control, Bulk Elements, Pagination, see below + +// Media container +$result-list-record-result-media-container-xs-sm-padding-left: 0 !default; +$result-list-record-result-media-container-xs-sm-padding-right: 0 !default; + +// Result entry +$result-list-result-hyphens: auto !default; +$result-list-result-padding-bottom: ($grid-gutter-width / 2) !default; +$result-list-result-padding-left: 0 !default; +$result-list-result-width: 100% !default; +// Alternating colors in search results +$result-list-result-nth-of-type-2-background-color: $brand-secondary !default; + +$result-list-result-title-font-size: ($font-size-base * 1.25) !default; +$result-list-result-title-xs-font-size: $font-size-base !default; +$result-list-result-title-sm-font-size: $font-size-base + 2px !default; +$result-list-result-title-font-weight: normal !default; + +// Record entry-related links in result list, incl. QR codes, public favorites lists etc. +$result-links-fa-icons-text-align: left !default; + +// Favorites list in MyAccount + +// Favorites lists: list description +$favorites-list-desc-margin-top: 1rem !default; + +// Favorites lists: Edit favorites entry +$result-links-favorites-edit-delete-tool-a-margin-top: 1em !default; +$result-links-favorites-edit-delete-tool-a-xs-sm-margin-top: 0 !default; +$result-links-favorites-edit-delete-tool-a-submenu-margin-top: 0 !default; + +// Saved in Favorites-lists box +$result-links-saved-in-alert-color: $state-light-text-on-dark !default; +$result-links-saved-in-ul-list-style: circle !default; +$result-links-saved-in-ul-list-style-position: inside !default; +$result-links-saved-in-ul-padding: 0 !default; +$result-links-saved-in-ul-a-color: $state-light-text-on-dark !default; + +// Access icons +$result-list-access-icons-container-text-align: center !default; +$result-list-access-icons-container-width: 17% !default; + +$result-list-access-icon-color: $brand-primary !default; +$result-list-access-icon-text-align: center !default; +$result-list-access-icon-i-font-size: 4.5em !default; +$result-list-access-icon-i-width: 100% !default; + +$result-list-access-icon-inner-font-size: 1rem !default; +$result-list-access-icon-inner-padding-top: .75rem !default; +$result-list-access-icon-inner-text-align: center !default; +$result-list-access-icon-inner-width: 100% !default; +$result-list-access-icon-inner-display-xs: block !default; + + + + + + +// ***************************************************************** +// ************ Search control elements **************************** +// ***************************************************************** +// use 'unset' if you want to avoid flex display +$search-stats-flex-grow: unset !default; +// Set display type for the containing element; +// use 'block' if you want to avoid flex display +$search-controls-container-display-type: block !default; +$hits-count-top-padding: .5em !default; +$limit-search-sort-float: left !default; +$limit-sort-bottom-margin: 5px !default; +$sort-container-float: right !default; + +// Set this to '0' if you want to align the result list items under 'hits per page' etc +$result-item-left-padding: 0 !default; + +$limit-sort-label-line-height: normal !default; +$limit-sort-label-margin-bottom: 0 !default; +$limit-sort-label-padding-right: 0 !default; + +$limit-sort-select-margin-bottom: 5px !default; +$limit-sort-select-margin-bottom-xs-sm: 0 !default; +$limit-sort-select-margin-right-xs-sm: em(3px) !default; + +$limit-sort-button-label-select-display-xs-sm: block !default; + +$limit-margin-right: ($grid-gutter-width / 2) !default; +$limit-margin-right-xs: 0 !default; +$limit-float-xs-sm: $limit-search-sort-float !default; +$limit-label-text-align: left !default; + +$sort-select-max-width: 12em !default; +$sort-select-max-width-below-410px: 9em !default; + +$sort-inner-form-display: block !default; +$limit-sort-inner-element-display: inline-block !default; +$limit-sort-inner-element-display-xs-sm-display: flex !default; + +$search-tools-links-background-color-xs-sm: $white !default; +$search-tools-links-border-xs-sm: 1px solid $iron !default; +$search-tools-links-color-xs-sm: $oil !default; +$search-tools-links-display-xs-sm: block !default; +$search-tools-links-margin-bottom-xs-sm: 5px !default; +$search-tools-links-padding-xs-sm: 6px 12px !default; +$search-tools-links-text-align-xs-sm: center !default; +$search-tools-links-touch-action-xs-sm: manipulation !default; +$search-tools-links-vertical-align-xs-sm: middle !default; +$search-tools-links-white-space-xs-sm: nowrap !default; + + + + +// ***************************************************************** +// ************ Pagination (results)/pager (records) *************** +// ***************************************************************** + +$pager-margin-bottom: 0 !default; +$pager-margin-top-xs: em(40px) !default; + + + + + +// ***************************************************************** +// ************ Bookbag ******************************************** +// ***************************************************************** + +$book-bag-list-item-margin-bottom: .5em !default; +// change the following to adjust the styling of the add-to-bookbag item to match the other items +// the default '$record-view-toolbar-button-padding' can be adjusted above +$book-bag-add-to-in-sidebar-toggler-background: transparent !default; +$book-bag-add-to-in-sidebar-toggler-border-radius: 0 !default; +$book-bag-add-to-in-sidebar-toggler-color: $brand-primary !default; +$record-view-toolbar-button-padding-add-to-bookbag: 1.2rem !default; +$book-bag-add-to-in-sidebar-toggler-padding: $record-view-toolbar-button-padding $record-view-toolbar-button-padding-add-to-bookbag !default; +$book-bag-add-to-in-sidebar-toggler-text-align-xs: left !default; +$book-bag-add-to-in-sidebar-toggler-text-decoration: underline !default; +$book-bag-add-to-in-sidebar-toggler-width: 100% !default; + +$book-bag-add-to-icon-content: '\f067' !default; +$book-bag-remove-from-icon-content: '\f068' !default; + + -//// Sidebar item hover effect (doesn't exist in VF out-of-the-box); active is for selected facets +// ***************************************************************** +// ************ Bulk Action Toolbar ******************************** +// ***************************************************************** + +$bulk-action-buttons-element-clear: both !default; +$bulk-action-buttons-element-padding-bottom: ($grid-gutter-width / 2) !default; +$bulk-action-buttons-element-padding-left: $result-item-left-padding !default; +$bulk-action-buttons-element-padding-top: ($grid-gutter-width / 2) !default; + +$bulk-action-buttons-element-btn-group-sm-md-margin-left: -($grid-gutter-width / 2) !default; +$bulk-action-buttons-element-btn-group-sm-margin-right: -($grid-gutter-width / 2) !default; +$bulk-action-buttons-element-add-to-bookbag-sm-max-width: 163px !default; + +$bulk-action-record-view-checkbox-container-float: left !default; +$bulk-action-record-view-checkbox-container-margin-right: .5em !default; +$bulk-action-record-view-checkbox-input-margin-top: $input-top-margin !default; + + + + + +// ***************************************************************** +// ************ Sidebar elements *********************************** +// ***************************************************************** + +// Menu in MyAccount, selected facets +$sidebar-facet-active-background-color: $brand-warning !default; +$sidebar-facet-active-color: $black !default; +$sidebar-facet-active-hover-color: $btn-primary-color !default; +$sidebar-facet-active-text-inside-padding-left: 1.25em !default; +$sidebar-or-facet-text-indent: -3px !default; +$sidebar-facet-my-account-padding: 0 !default; +$sidebar-facet-my-account-link-width: 100% !default; +$sidebar-exclude-facet-margin-left: -1.5rem !default; +$sidebar-exclude-facet-min-height: $navigation-element-default-height !default; +$sidebar-exclude-facet-padding-left: 1em !default; + +$sidebar-facet-title-padding: 0 !default; +$sidebar-facet-title-background-color: $brand-secondary !default; +$sidebar-facet-title-background-color-focus-hover: $button-title-hover-bg !default; + +$sidebar-my-account-danger-success-warning-background: transparent !default; +$sidebar-my-account-danger-success-warning-padding: 0 !default; + +// Sidebar item hover effect (doesn't exist in VF out-of-the-box); active is for selected facets $sidebar-item-hover-bg: transparentize($brand-secondary, .9) !default; $sidebar-item-active-hover-bg: transparentize($brand-primary, .1) !default; -//// ELEMENT COLORS - END -// COLORS - END -// DIMENSIONS -//// The general gutter width (padding between columns) is calculated like so: '$grid-gutter-width / 2' -//// which gives you the left or right gutter width; this can be taken further by using '$grid-gutter-width / 1.5' etc -$grid-gutter-width: 30px !default; -$half-gutter: ($grid-gutter-width / 2); -$quarter-gutter: ($grid-gutter-width / 4); +// Text alignment 'more'-toggles in sidebar; +// default is center. If center alignment is desired, comment out this next line +// and the '.narrow-toggle ...' in compiled.scss +$narrow-toggle-text-alignment: left !default; -//// Navbar gets a minimum-height of 50px by default, activate and change here! -//// For fixed navbars ('navbar-fixed-top' in 'header.phtml'), the body requires a padding-bottom of the same height as the navbar because of postition.fixed -//// When using 'affix' instead of 'navbar-fixed-top', set '$navbar-height', '$navbar-height-xs' and '$navbar-height-sm' to '=0' -//// or set the paddings-top under 'body' in compiled.scss to '=0'; see also the comment in header.phtml -$navbar-height: 112px !default; // = 7 x 1rem //80px !default; -$navbar-height-sm: $navbar-height !default; -//// Set navbar-height for small devices, USE px as rem will throw an error in conjunction _variables.scss -$navbar-height-xs: $navbar-height + 40px !default; -$navbar-max-height-xs: $navbar-height + 40px !default; -//// Navigation elements default height: This height _must_ be adapted to the button height (.btn, .search-filter-toggle {}), which changes with default font size; apply to navigation elements such as select dropdowns -$navigation-element-default-height: 38px; +$sidebar-facet-word-break: break-all !default; +$sidebar-facet-word-break-sm: initial !default; +$sidebar-facet-link-text-decoration: none !default; -//// Top-padding for #content -> .row first-of-type (and .sidebar) - change this value to change both; -//// sidebar gets an additional padding on half the content-top-padding for better horizontal alignment -$content-top-padding: $grid-gutter-width / 2 !default; +$sidebar-facet-badge-font-size: 1em !default; +$sidebar-facet-badge-padding-right: 2px !default; + + +$sidebar-facet-badge-my-research-icons-display: inline-table !default; +$sidebar-facet-badge-my-research-icons-font-size: 90% !default; +$sidebar-facet-badge-my-research-icons-max-height: unset !default; +$sidebar-facet-badge-my-research-icons-padding: 3px 7px !default; + +$sidebar-facet-badge-background-color: transparent !default; +$sidebar-facet-badge-has-error-border: 1px solid $brand-danger !default; +$sidebar-facet-badge-has-error-border-focus-over: $sidebar-facet-badge-has-error-border !default; + + +// BG of bell and clock icons Warning signs in MyAccount sidebar/header navigation +$icon-with-text-success-color: $brand-success !default; +$icon-with-text-warning-color: $brand-warning !default; -//// Sidebar item padding -$sidebar-item-padding: .75em 1em !default; -//// Table cell padding - adjust in themes to avoid content jumps when switching tabs -// $table-cell-padding: 5px !default; -// JS Tree -// left padding on JSTree child icon elements -$jstree-li-left-padding: 1.5em !default; -//// MODAL dimensions + + +// ***************************************************************** +// ************ Record tabs **************************************** +// ***************************************************************** +$record-tabs-padding-left: 1em !default; +$record-tabs-padding-left-xs: 0 !default; + +$record-tabs-tab-content-padding: ($grid-gutter-width / 2) !default; +$record-tabs-tab-content-a-word-break: break-word !default; +$record-tabs-tab-content-a-active-display: inline-block !default; +$record-tabs-tab-content-a-active-width: 100% !default; + +$record-tabs-holdings-collapse-padding-left: ($grid-gutter-width / 2) !default; +$record-tabs-holdings-tab-pane-ul-margin-top: 1em !default; +$record-tabs-holdings-tab-pane-ul-padding-left: 0 !default; +$record-tabs-holdings-tab-pane-li-margin-bottom: .5em !default; + +$record-tabs-availability-column-a-xs-sm-display: block !default; +$record-tabs-availability-column-a-xs-sm-margin-top: 6px !default; +$record-tabs-availability-column-a-xs-sm-word-break: break-word !default; + + + + + + +// ***************************************************************** +// ************ Off-canvas ***************************************** +// ***************************************************************** + +// Back-button in opened off-canvas sidebar (on LEFT) +$off-canvas-left-close-icon-content: '\00a0\f105' !default; +// Off-canvas toggler button in normal pages (sidebar on LEFT) +$off-canvas-left-open-icon-content: '\f100\00a0' !default; + +// Back-button in opened off-canvas sidebar (on RIGHT) +$off-canvas-right-close-icon-content: '\f104\00a0' !default; +// Off-canvas toggler button in normal pages (sidebar on RIGHT) +$off-canvas-right-open-icon-content: '\00a0\f101' !default; + +// Book cover/icon on right for SM up on Off-canvas LEFT +$off-canvas-left-media-icon-on-right-sm-up-float: right !default; +$off-canvas-left-media-icon-on-right-sm-up-margin-left: $grid-gutter-width / 2 !default; +$off-canvas-left-media-icon-on-right-sm-up-margin-right: 0 !default; +$off-canvas-left-media-icon-on-right-sm-up-padding-right: 0 !default; + +// Padding to make left/right sidebar button edges visible +$off-canvas-active-sidebar-padding: 2px !default; + + + + + +// ***************************************************************** +// ************ Modal/lightbox ************************************* +// ***************************************************************** $modal-lg: 900px !default; $modal-md: 600px !default; $modal-sm: 300px !default; +// Prevent transparent lightbox bug +$modal-dialog-mainbody-left-float: none !default; -// BREADCRUMBS -//// Separator between breadcrumb elements, use this variable to change it -//// $breadcrumb-separator: '/' !default; +$modal-dialog-input-max-width-sm-up: em(400px) !default; +$modal-dialog-max-width-lg-up: em(900px) !default; +$modal-dialog-max-width-sm-up: 85% !default; +// Reduce size of modal to keep close-button visible +$modal-dialog-max-width-xs: 80% !default; +$modal-dialog-min-width-sm-up: $modal-md !default; +$modal-dialog-width-md-up: $modal-lg !default; +$modal-dialog-width-sm-up: auto !default; -// GET RID of ALL ROUND CORNERS -$border-radius-base: 0 !default; -$border-radius-large: 0 !default; -$border-radius-small: 0 !default; +$modal-dialog-open-overflow: initial !default; -$pager-border-radius: 0 !default; +// Close button - see also the section 'Buttons' above +$modal-dialog-close-button-border: 0 !default; +$modal-dialog-close-button-left: auto !default; +$modal-dialog-close-button-opacity: unset !default; +$modal-dialog-close-button-right: 0 !default; +$modal-dialog-close-button-right-sm-up: -60px !default; +$modal-dialog-close-button-left-sm-up-rtl: -60px !default; +$modal-dialog-close-button-focus-hover-opacity: unset; -$badge-border-radius: 0 !default; -$modal-close-br: 0 !default; -//// labels.scss uses no variable! Therefore it will be necessary -//// to overwite in compiled.scss!!, Fixme when done more elegantly in BS -$label-radius: 0 !default; -// GET RID of ALL ROUND CORNERS - END -// GET RID of BOX-SHADOWS -.dropdown-menu { - box-shadow: none; -} +// ***************************************************************** +// ************ Footer elements ************************************ +// ***************************************************************** +$footer-border-top: 0 !default; +$footer-padding-top: ($grid-gutter-width / 1.5) !default; +$footer-ul-padding-left: ($grid-gutter-width / 2) !default; +$footer-poweredby-padding: ($grid-gutter-width / 2) !default; +$footer-poweredby-img-height: em(28px) !default; +$footer-poweredby-img-margin-left: ($grid-gutter-width / 4) !default; +$footer-poweredby-img-margin-right: ($grid-gutter-width / 4) !default; +$footer-poweredby-font-size: small !default; -// ALIGNMENTS -//// Align tops of elements to transparent button-content in same row (e.g. header: searchbox content + navigation on right-hand side) -$button-top-padding: 6px; -//// Text alignment 'more'-toggles in sidebar; -//// default is center. If center alignment is desired, comment out this next line -//// and the '.narrow-toggle ...' in compiled.scss -$narrow-toggle-text-alignment: left !default; -// ALIGNMENTS - END -// FUNCTIONS -//// em Calculator (just write the desired px into the function call, like so: font-size: em(12) or em(12px) -@function em($pixels, $context: $font-size-base) { - @if (unitless($pixels)) { - $pixels: $pixels * 1px; - } - @if (unitless($context)) { - $context: $context * 1px; - } +// ***************************************************************** +// ************ Home page ****************************************** +// ***************************************************************** +$home-page-margin-top: 1em !default; +$browse-container-padding-top: 1em !default; +$browse-container-xs-margin-top: 2rem !default; - @return $pixels / $context * 1em; -} +$home-page-well-xs-background-color: transparent !default; +$home-page-well-xs-border: 0 !default; +$home-page-well-xs-box-shadow: none !default; +$home-page-well-xs-min-height: ($grid-gutter-width / 2) !default; +$home-page-well-xs-padding: 0 !default; + +$home-page-browse-suggest-2-columns-only-last-child-float: left !default; +$home-page-browse-item-active-focus-hover-background-color: $brand-primary !default; +$home-page-browse-item-active-focus-hover-text-decoration: underline !default; + + + + + + +// ***************************************************************** +// ************ Hierarchy Tree/JS tree ***************************** +// ***************************************************************** + +$jstree-tree-selector-border-bottom: $border-default-styles !default; +$jstree-tree-selector-margin-bottom: 1em !default; +$jstree-tree-selector-padding-bottom: 1em !default; + +$jstree-tree-selector-item-margin-right: 2em !default; +$jstree-tree-selector-item-last-of-type-margin-right: 0 !default; + +$jstree-tree-holder-border-right: 0 !default; + +$jstree-container-ul-padding-left: 0 !default; + +// Top-most JS tree icon +$jstree-topmost-icon-closed-content: '\f0da' !default; +$jstree-topmost-icon-float: left !default; +$jstree-topmost-icon-font-size: 150% !default; +$jstree-topmost-icon-hover-before-color: $jumbo !default; +$jstree-topmost-icon-margin-left: 0 !default; +$jstree-topmost-icon-margin-top: -5px !default; +$jstree-topmost-icon-min-width: 18px !default; // do NOT translate into em +$jstree-topmost-icon-open-content: '\f0d7' !default; +$jstree-topmost-icon-padding: 0 !default; +$jstree-topmost-icon-width: 0 !default; + + +$jstree-facet-node-line-height: 1.75 !default; +$jstree-facet-node-list-style: none !default; +$jstree-facet-node-padding-left: 1.5em !default; + +$jstree-children-ul-margin-top: .75em !default; +$jstree-children-ul-padding-left: 0 !default; + +// FontAwesome Unicode fa-file-o +$jstree-children-li-before-icon-content: '\f016' !default; +$jstree-children-li-before-icon-display: inline-block !default; +$jstree-children-li-before-icon-font-family: FontAwesome, sans-serif !default; +$jstree-children-li-left-padding: $jstree-facet-node-padding-left; +$jstree-children-li-before-icon-width: $jstree-children-li-left-padding !default; + +$jstree-anchor-hyphens: auto !default; +$jstree-anchor-padding-left: 0 !default; +$jstree-anchor-white-space: normal !default; + + +$jstree-clicked-padding-bottom: .25em !default; +$jstree-clicked-padding-right: .5em !default; +$jstree-clicked-padding-top: .3em !default; + + +$jstree-clicked-color: $steel !default; +$jstree-clicked-hover-color: $black !default; +$jstree-clicked-hover-focus-color: $jstree-clicked-hover-color !default; + + + + + +// ***************************************************************** +// ************ Resolver links, 'traffic lights' ******************* +// ***************************************************************** + +$resolver-links-show-availability-display: inline !default; + +$traffic-light-display: inline !default; +$traffic-light-padding: 0 !default; +$traffic-light-vertical-align: middle !default; + +// temporary, will be overwritten by first, second, third items below +$traffic-light-inner-items-background-color: $silver !default; +$traffic-light-inner-items-border-top: 3px solid transparent !default; +$traffic-light-inner-items-display: inline-block !default; +$traffic-light-inner-items-height: 1em !default; +$traffic-light-inner-items-margin: 0 -3px 0 0 !default; +$traffic-light-inner-items-padding-right: .5rem !default; +$traffic-light-inner-items-width: 1em !default; +$traffic-light-inner-items-last-margin-right: .5em !default; + +$traffic-light-access-denied-inner-first-background-color: $brand-danger !default; +$traffic-light-access-denied-inner-second-background-color: $gainsboro !default; +$traffic-light-access-denied-inner-last-background-color: $gainsboro !default; + +$traffic-light-access-limited-inner-first-background-color: $gainsboro !default; +$traffic-light-access-limited-inner-second-background-color: $traffic-light-yellow !default; +$traffic-light-access-limited-inner-last-background-color: $gainsboro !default; + +$traffic-light-access-open-inner-first-background-color: $gainsboro !default; +$traffic-light-access-open-inner-second-background-color: $gainsboro !default; +$traffic-light-access-open-inner-last-background-color: $brand-success !default; + + + + + + +// ***************************************************************** +// ************ Admin panel for translations *********************** +// ***************************************************************** +$translation-form-margin-top: 2em !default; +$translation-form-label-font-weight: bold !default; +$translation-form-input-checkbox-margin-top: 0 !default; +$translation-form-language-table-filter-text-align: right !default; + +$translation-form-tables-thead-background-color: $brand-secondary !default; +$translation-form-tables-thead-color: $charcoal !default; +$translation-form-tables-th-padding-bottom: .75rem !default; +$translation-form-tables-th-padding-top: 1rem !default; +$translation-form-tables-th-white-space: nowrap !default; + +$translation-form-sorting-cursor: pointer !default; +$translation-form-sorting-asc-desc-color: $brand-primary !default; +$translation-form-sorting-color: lighten($charcoal, 5%) !default; +$translation-form-sorting-icon: '\f0dc' !default; +$translation-form-sorting-asc-icon: '\f0de' !default; +$translation-form-sorting-desc-icon: '\f0dd' !default; + + + + + +// ***************************************************************** +// ************ AMSL sources *************************************** +// ***************************************************************** + +$amsl-sources-list-line-height: 1.5 !default; +$amsl-sources-list-padding-left: 0 !default; +$amsl-sources-list-li-top-level-list-style: none !default; +$amsl-sources-list-li-top-level-list-padding-bottom: .5em !default; +$amsl-sources-list-panel-body-span-margin: 10px 15px 10px 0 !default; -// FUNCTIONS - END diff --git a/themes/finc/scss/_bulkaction-visible-xs.scss b/themes/finc/scss/activate-on-demand/_bulkaction-visible-xs.scss similarity index 64% rename from themes/finc/scss/_bulkaction-visible-xs.scss rename to themes/finc/scss/activate-on-demand/_bulkaction-visible-xs.scss index 6a5e51b4d1ebed26a10364db65316ded2538500d..50b8c3070c50bd6d1d68c975649d38f91cc48f79 100644 --- a/themes/finc/scss/_bulkaction-visible-xs.scss +++ b/themes/finc/scss/activate-on-demand/_bulkaction-visible-xs.scss @@ -1,6 +1,7 @@ -// Bulk Action Buttons and result list checkboxes -// get hidden on mobile in Bootstrap Theme -- show them again -// in finc-based themes via this importable SCSS Partial +// Bulk Action Buttons and result list checkboxes get hidden on +// mobile in Bootstrap Theme - show them again. +// In finc-based themes import this SCSS partial or copy it to your own theme + @media (max-width: 767px) { .bulkActionButtons, .result .checkbox, diff --git a/themes/finc/scss/activate-on-demand/_visitedLinks.scss b/themes/finc/scss/activate-on-demand/_visitedLinks.scss new file mode 100644 index 0000000000000000000000000000000000000000..d47dcee50b5cc845fc23ac0ec329d08b6afc3656 --- /dev/null +++ b/themes/finc/scss/activate-on-demand/_visitedLinks.scss @@ -0,0 +1,67 @@ +// Import this file in your theme to style visited links in a different color +// You may also copy it to your theme and fine-tune it further (e.g. 'a:not(.btn):not(.text) ....' + +// By changing these variables in your own theme, you can override the !defaults +// This _does not_ re-style visited facets, filters, alerts +$a-visited-color: $steel !default; +$a-visited-color-standard-link-color: $steel !default;$state-link-hover-color: $black !default; +$a-visited-color-alerts: $jet !default; +$a-visited-color-active-facets: $a-visited-color-alerts !default; + +// define text-decoration and visited link color _for all but_ buttons, facets and breadcrumbs +a:not(.btn):not(.text) { + text-decoration: $link-text-decoration; + + // set different color for links already visited + &:visited { + color: $a-visited-color; + + .breadcrumb &, + .nav &, + .alert &, + .savedLists & { + color: unset; + + &:focus, + &:hover { + color: $a-visited-color; + } + } + + .alert &, + .savedLists & { + &:focus, + &:hover { + color: $a-visited-color-alerts; + } + } + + &.active { + .facet & { + color: $a-visited-color-active-facets; + + &:focus, + &:hover { + color: $sidebar-facet-active-hover-color; + } + } + } + } +} + +// Selected/active filters on top +.sidebar .facet.active, +.sidebar a.active { + + &:visited { + // keep important + color: $sidebar-facet-active-color !important; + + &:focus, + &:hover { + // keep important + color: $sidebar-facet-active-hover-color !important; + } + } +} + diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss index 12a4e2b53dbe0aeb4e671c8f00b31b87df8788f8..6a3dff6eaf5f31be0a3001e4bc90f2cbcfa2fbae 100644 --- a/themes/finc/scss/compiled.scss +++ b/themes/finc/scss/compiled.scss @@ -1,3406 +1 @@ -// HOWTO: -// Create a compiled.SCSS file for all sub-themes which imports the respective parent theme's compiled.scss -// in the cascade plus extends it - this way we ALWAYS have an UP-TO-DATE compiled.CSS imported -// which includes finc plus all subsequent customizations -// i.e. finc's compiled.scss imports bootstrap3/scss/compiled - the house-specific branches such as -// de_15 then import finc/scss/compiled -// ------------------------------------------------------- -// 0. Note for developers -// Do NOT EDIT compiled.css -// ------------------------------------------------------- -// 1. Define or re-define KEY VARIABLES and MIXINS (see 1.1 ff below) OR import your own -@import 'customVariables'; -// ------------------------------------------------------- -// 1.1 Unless imported, define standard colour variables here which are used in variables below prior to finc.scss import -// e.g. $brand-primary: #083152 !default; // blue -// $brand-secondary: #e7e7e7 !default; // white-lilac -// $brand-danger: #f04124 !default; // cinnabar -// $brand-info: #a0d3e8 !default; // cornflower -// $brand-success: #085218 !default; // sea-green -// $brand-warning: #f08a24 !default; // carrot -// ------------------------------------------------------- -// 1.2 DO NOT define site-specific colours in finc - this will be done in site-specific themes -// ------------------------------------------------------- -// 2. For Media queries: -// The maximum ranges are defined as the next higher element minus 1px, so: -// '$screen-xs-max' = $screen-sm-min - 1 -// Extra small screen / phone: up to '$screen-xs-max' (= '$screen-sm-min - 1') = everything below 767px; -// Small screen / tablet: from '$screen-sm-min' (= 768px) to '$screen-sm-max' (= '$screen-md-min -1') = 991px -// Medium screen / desktop: from '$screen-md-min' (=992px) to '$screen-md-max' (= '$screen-lg-min -1') = 1199px -// Large screen / wide desktop: '$screen-lg-min' (=1200px); -// So, you can simply use: // @media (max-width: $screen-xs-max) {...} for everything for XS devices -// ------------------------------------------------------- -// 2.1 Media queries for Navbar collapse/uncollapse: -// The point at which the navbar becomes uncollapsed is defined as -// $grid-float-breakpoint: $screen-sm-min !default; -// in Bootstrap -- so you can use '$screen-sm-min' unless you changed the definition -// The point at which the navbar begins collapsing is defined as -// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; -// ------------------------------------------------------- -// 3. import entire BS (finc) OR import (instances) finc compiled.scss to have variables and mixins defined above applied to it -@import '../../finc-accessibility/scss/compiled'; -// @import '../../finc/scss/compiled.scss' -@import 'customMixins'; -// ------------------------------------------------------- -// 4. Customize further - this for BASIC SETTINGS ONLY - all colour customization will be done in in site-specific themes -// ATTENTION: any change made here will affect ALL subsequent branches !!! - -// Allow us to use sections that act as rows -section { - @include make-row; -} - -// PLEASE ORGANIZE YOUR CSS ACCORDING TO THIS GENERAL RULE - keep exceptions to a minimum -// *, html, body -// h1, h2, h3, h4, h5, h6, -// p, -// address, blockquote, pre, -// ul, ol, li, dl, dt, dd, -// table, caption, tr, th, td, -// form, fieldset, legend, label, select, input, textarea, -// div, -// img, -// a, -// strong, em, abbr, q, cite, code, kbd, var, -// span, -// #id, -// .class -// Frequently used classes -// Custom areas of the theme - -// IDs and classes come last and should (if possible) be ordered according -// to their appearance on the page, e.g. header, breadcrumbs, main ... - -// Please sort selectors alphabetically, like so -// .foo { -// border: 1px solid red; -// margin-left: 1em; -// padding: 0 ($grid-gutter-width / 2); -// width: auto; -// } -// - -// Prevent content from jumping when changing tabs in record view -html { - overflow-y: scroll; -} - -// Add top-padding to body to accommodate fixed navbar; -- padding should equal navbar-height -- -// the home page, however, has a lower navbar because there is no breadcrumbs there -body { - // fix modal adding padding-right #13625 - padding-right: 0 !important; - - // for mobile - padding-top: $navbar-height-xs; - - // make exception for adv. search results - &.template-name-advanced { - @media (max-width: $screen-xs-max) { - padding-top: 3.25rem; - } - } - - // for XS - @media (max-width: $screen-xs-max) { - padding-top: 8.5rem; - } - - // for tablet - @media (min-width: $screen-sm-min) { - padding-top: $navbar-height-sm; - } - - //// for large - @media (min-width: $screen-md-min) { - padding-top: $navbar-height; - } - - //// for print - @media print { - padding-top: 0; - } -} - -// Collapse/Hide empty containers -- DO NOT INCLUDE span (dropdown carets are empty spans) NOR DIV (slider track handles) -h1, -h2, -h3, -h4, -label, -.label { - // Empty labels collapse automatically (not available in IE8) - &:empty { - display: none; - } -} - -// HEADINGS -//// h1 -h1 { - font-size: 2rem; - - //// remove top margin to align with sidebars etc., except advanced search on XS - &:first-of-type { - margin-top: 0; - - @media (max-width: $screen-xs-max) { - margin-top: revert; - - body:not(.template-name-advanced) & { - margin-top: 0; - } - } - } - - .template-dir-cart.template-name-cart .container &, - .template-dir-content.template-name-content &, - .template-dir-myresearch.template-name-editlist &, - .template-dir-search.template-name-newitem &, - .template-dir-search.template-name-reserves & { - @media (max-width: $screen-xs-max) { - padding-top: 15px; - } - } -} - -//// h2 -h2 { - font-size: 1.75rem; - - .sidebar & { - font-size: 1.5rem; - - @media (max-width: $screen-xs-max) { - margin-left: $grid-gutter-width / 2; - } - - @media (max-width: $screen-sm-max) { - font-size: 1.25rem; - margin-top: 1.75rem; - } - - @media (min-width: $screen-sm-min) { - &:first-of-type { - margin-top: 0; - } - } - } -} - -//// h3 -h3 { - font-size: 1.5rem; - - // H3 in MyAccount needs to be pushed in - #myresearch-sidebar & { - @media screen and (max-width: $screen-xs-max) { - padding-left: $half-gutter; - } - } -} - -//// h4 -h4 { - font-size: 1.25rem; - - ////// H4 atop Sidebar - align to limit and sort - .sidebar & { - margin-top: 0; - - @media (max-width: $screen-sm-max) { - text-align: center; - } - - // keep level with left-hand content - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - font-size: $font-size-base + 2px; - margin-top: ($grid-gutter-width / 2); - } - } -} - -//// h5 - -// HEADINGS - END - -// Paragraphs -// p {} - -// Address blocks -// address {} - -// Lists - -// ul, -/// no bullets for LIs -- (note: apply style to parent UL in phtml)! -.no-bullet { - padding-left: 0; - - li { - list-style-type: none; - } -} -// ol {} -// li {} -// dl {} -// dt {} -// dd {} - -// TABLES -// table {} -// caption {} -// tr {} -// th {} -// td {} - -.table { - // add border to tables -- otherwise we'd have to rewrite all tables with table-bordered - border: 1px solid $brand-secondary; - // make columns adapt to content - table-layout: auto; - - // Remove top borders (_tables.scss) - thead, - tbody, - tfoot { - > tr { - > th, - > td { - border-top: 0; - } - } - } -} - -//// Tables in Tabs, Record View etc -table { - .tab-content & { - width: 100%; - } -} - -//// Responsive data tables for small (xs) resolution (e.g. search history table) -@media screen and (max-width: $screen-xs-max) { - // Force table around, exc. solrMarc staffview - .table-resp-data:not(.citation) { - display: flex; - max-width: 90vw !important; - } - - .table-resp-data { - border: 0; - display: block; - } - - // .table-resp-data (default) -- is applied to XS - .table-resp-data { - @include table-resp-data; - } - - // limit width for staff view - .tab-pane.details-tab.active { - max-width: 80vw; - overflow-x: auto; - } -} - -//// Responsive data tables for medium (md) resolution (e.g. fines table) -@media screen and (max-width: $screen-md-max) { - .table-resp-data-md { - @include table-resp-data; - } -} - -// TABLES - END - -// FORMS -form { - // red-bordered input field, when empty, requires "form" for specifity, CK - // create enough space for red border - input:invalid, - textarea:invalid { - margin-right: 2px; - } - - input { - // show red border only when submitted empty or when in focus - &:invalid { - box-shadow: inherit; - } - - &:focus { - &:invalid, - &:required:invalid { - border-color: $brand-danger-transparent; - box-shadow: 0 0 2px 1px $brand-danger-transparent; - } - } - } -} - -//// Set max-width to make sure boxes don't bleed over the edge on XS (e.g. acquisitionpda, source_id:3 -input, -select, -textarea { - @media (max-width: $screen-xs-max) { - max-width: 100%; - } -} - -.form-control { - box-shadow: none; - height: $navigation-element-default-height; - - @media (max-width: $screen-xs-max) { - max-width: em(400px); - } -} - -//// Fieldsets -fieldset { - border: 1px solid $border-color; - - // Advanced Search - make fieldsets fit in a row (see last-of-type below) - .template-name-advanced & { - margin-top: $grid-gutter-width; - padding-bottom: ($grid-gutter-width / 2); - padding-left: ($grid-gutter-width / 2); - padding-right: ($grid-gutter-width / 2); - - // Advanced Search - &:nth-of-type(3) { - margin-left: ($grid-gutter-width / 2); - margin-right: ($grid-gutter-width / 2); - width: 30.25%; - - @media (max-width: $screen-sm-max) { - width: auto; - } - } - - - &:last-of-type { - float: right; - margin-right: 0; - } - } - - // advanced search fields top-most fieldset without width restrictions - .adv-search & { - border: 0; - // keep !important - float: unset !important; - margin-top: 0; - padding-bottom: 0; - padding-left: 0; - width: unset; - - @media (max-width: $screen-xs-max) { - margin-bottom: $half-gutter; - padding-bottom: 5px; - } - } -} - -.form-edit-list { - fieldset { - border: 0; - float: none; - padding: 0; - } - - legend { - font-size: $font-size-base; - margin-left: -14px; - } -} - -//// Legend elements -// Limit to etc in Advanced search -legend { - border: 0; - font-size: ($font-size-base * 1.25); - margin-bottom: 0; - // margin-top: $grid-gutter-width; - padding-left: ($grid-gutter-width / 2); - padding-right: ($grid-gutter-width / 2); - width: auto; - - // Recent acquisitions search form (search/NewItem) - .form-search-newitem & { - padding-left: 0; - } -} - -//// Labels -label { - - form & { - font-weight: normal; - } - - // remove padding in advanced search labels - #advSearchForm .mainbody & { - padding-left: 0; - padding-right: 0; - } - - // advanced search input and select items - .adv-input &, - .adv-select & { - display: block; - } -} - -//// label -.text-success, -.text-danger { - padding: .5em; -} - -.text-success { - background-color: $brand-success; - margin-bottom: 0; - - // Warning signs in MyAccount sidebar - &.fa-bell, - &.fa-clock-o { - color: $brand-success; - } -} - -.text-warning { - // Warning signs in MyAccount sidebar - &.fa-bell, - &.fa-clock-o { - color: $brand-warning; - } -} - -.text-danger { - background-color: $brand-danger; - - // Warning sign in front of Login/MyAccount in header - &.fa-exclamation-triangle { - color: $brand-danger; - } -} - -// Warning signs in MyAccount sidebar and warning sign in front of Login/MyAccount in header -.text-danger, -.text-success, -.text-warning { - &.fa-bell, - &.fa-clock-o, - &.fa-exclamation-triangle { - background: transparent; - padding: 0; - - // icons in header navbar on Hover - .btn:focus &, - .btn:hover & { - color: inherit; - } - } -} - -//// Required symbol - -.required { - color: $brand-danger; -} - -//// input {} -input[type='radio'], -input[type='checkbox'] { - margin-top: $inputs-top-margin; -} - -input[type='email'] { - border: $border-default-styles; - padding: ($grid-gutter-width / 4); -} - -input[type='text'] { - border: $border-default-styles; - padding: ($grid-gutter-width / 4); - - &:hover { - outline: 1px solid $input-border-focus; - } - - header &, - #searchForm & { - border: $border-default-white; - - &:hover { - box-shadow: none; - outline: 1px $input-border-focus solid; - } - } -} - -// pull from under fields select -#searchForm input { - &:hover, - &:focus { - border-color: $input-border-focus; - outline-offset: -1px; - } -} - -//// Select - for limit and sort-select widths see below -////// remove browser styles on select boxes and add custom styles (below) -select, -select.form-control { - -moz-appearance: none !important; - -webkit-appearance: none !important; - background-color: $select-bg-color; - // This is the litte down arrow - background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+); - background-position: 100% center; - background-repeat: no-repeat; - border: $border-default-styles; - border-radius: 0; - height: $navigation-element-default-height; - min-width: 5em; - padding: .25em 1.2em .25em .5em; - - &.form-control { - background-color: $select-bg-color; - } - - header & { - border: 0; - } -} - -select:hover { - outline: 1px solid $input-border-focus; -} - -//// Create list in add to favs dialog -.form-edit-list { - .radio { - &.inline { - display: block; - } - - input { - margin-left: 1.4rem; - position: relative; - } - - label { - padding-left: 0; - } - } -} - -//// include down arrow in search filter select box (All fields); note: .form-control might prevent the down arrow replacement! -//// ALSO applies to select boxes in Advanced Search, Hierarchy tree -select { - .searchForm &, - #advSearchForm &, - #hierarchyTreeHolder &, - .template-name-myresearch & { - background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMTJweCIgeT0iMHB4IiB3aWR0aD0iMjRweCIgaGVpZ2h0PSIzcHgiIHZpZXdCb3g9IjAgMCA2IDMiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDYgMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHBvbHlnb24gcG9pbnRzPSI1Ljk5MiwwIDIuOTkyLDMgLTAuMDA4LDAgIi8+PC9zdmc+); - } - - .searchForm & { - @media (min-width: $screen-sm-min) { - // prevent adv search button from breaking onto new line - max-width: 140px; - } - } -} - -// remove icons in limiter select boxes in adv. search -#advSearchForm .limiter-boxes select.form-control, -#newitem_department { - background-image: none; - height: auto; -} - -//// Select - END - -//// textarea {} - - -// FORMS - END - -// IMAGES - -//// img {} - -// IMAGES - END - -// LINKS // ANCHORS -//// define text-decoration and visited link color _for all but_ buttons, facets and breadcrumbs -a:not(.btn):not(.text) { - text-decoration: $textdecoration-anchor; - - // set different color for links already visited - &:visited { - color: $a-visited-color; - - .breadcrumb &, - .nav & { - color: unset; - } - } -} - -// LINKS - END - -// TEXT DECORATION - -// strong -// em - -// TEXT DECORATION - END - -// SPANS - -// IDs - -// CLASSES (Elements that are used in several places) -//// BUTTONS -// NOTE: buttons have a default height of 38px, the same as $navigation-element-default-height for select boxes etc - -////// Transparent buttons (button with border and darker text on light or white BGs) -.btn-transparent { - @include button-variant($btn-transparent-color, $btn-transparent-bg, $btn-transparent-border); - - &:hover, - &:focus { - color: lighten($btn-transparent-color, 80%); - } - - &.active { - color: $btn-transparent-active-color; - - &:focus, - &:hover { - background-color: $btn-transparent-active-color; - color: $black; - } - } -} - -.btn-default.active, -.btn-secondary.active { - &:focus, - &:hover { - background-color: $btn-active-hover-bg; - } -} - -.btn-info { - &:focus, - &:hover { - background-color: $btn-info-hover-bg; - } - - &.active { - &:focus, - &:hover { - background-color: $btn-info-active-hover-bg; - } - } -} - -.btn-primary, -.btn-primary.active { - &:focus, - &:hover { - background-color: $btn-primary-hover-bg; - color: $btn-primary-hover-color; - } - - // On datalightbox: - &[disabled="disabled"] { - color: $btn-primary-disabled-color; - } -} - -.btn-secondary { - &:focus, - &:hover { - border: 1px solid $btn-secondary-border-color; - } -} - -.btn-success.active { - &:focus, - &:hover { - background-color: $btn-success-active-hover-bg; - } -} - -.btn-primary.disabled { - &:focus, - &:hover { - background-color: $btn-primary-disabled-hover-bg; - color: $btn-primary-disabled-hover-color; - } -} - -.has-error .control-label, -.has-error .help-block, -.sms-error .control-label, -.sms-error .help-block { - color: $brand-danger; -} - -////// Bookbag Toggler -.sidebar .btn-bookbag-toggle { - width: 100%; -} - -// Overwrites BS.scss setting! Keep padding same as '.record-nav > li > a' or "Add to Bookbag" in Record View will be out of line -.nav .btn-bookbag-toggle a { - padding: $record-view-toolbar-button-padding 0; - - @media (max-width: $screen-sm-max) { - padding: $record-view-toolbar-button-padding; - } -} - -////// remove padding on SM up -.btn-bookbag-toggle { - @media (max-width: $screen-xs-max) { - text-align: left; - } -} - -////// Bookbag Toggler - END -//// BUTTONS - END - -//// DROPDOWNS -//// remove padding as it creates space above/below menu items (e.g. language selector) -//// remove box shadow -.dropdown-menu { - box-shadow: none; - padding-bottom: 0; - padding-top: 0; -} - -//// HOVERABLE DROPDOWNS -- make DDs hoverable as in FNDTN -// Add the dropdown-on-hover class to the parent li container! OR use on all (see commented-out part below) -// Menu will show on hover -@media (min-width: $screen-sm-min) { - .dropdown-on-hover:hover .dropdown-menu { - display: block; - } -} - -//// OR use on ALL dropdowns like so -//.dropdown:hover .dropdown-menu { -// display: block; -//} - -//// Record View Toggler - show extended information on single item -a.toggle { - color: inherit; - text-decoration: inherit; - - &:active, - &:focus, - &:hover { - text-decoration: inherit; - } - - &::after { - content: $fa-var-angle-down; - font-family: 'FontAwesome', sans-serif; - padding-left: 2px; - } -} - -.notation ul { - padding-inline-start: 1.5em; - - @media (max-width: $screen-md-min) { - padding-inline-start: 0; - } - - li { - list-style-type: none; - } - - li:nth-of-type(1)::before { - content: $fa-var-level-down; - font-family: 'FontAwesome', sans-serif; - margin-right: .5em; - } -} - -//// ALERTS -////// Light Text on Dark BG -.alert { - - a { - color: $state-light-text-on-dark; - text-decoration: underline; - } - - &.alert-warning a { - color: $oil; - } - - // Holdings tab - .holding-info & { - margin-left: 15px; - margin-right: 15px; - margin-top: 15px; - } - -} - -////// Alert Text Colors -.alert-success, -.alert-info, -.alert-danger { - clear: both; - color: $state-light-text-on-dark; - - &:hover { - color: $state-light-text-on-dark; - } -} - -//// DATE-RANGE slider (CSS is included in bootstrap-slider.min.css - currently no SCSS version!) -////// Keep enough room for slider handles; PLS note: there are sliders in sidefacets and adv. search -.slider-container { - padding: 4px 4px 4px 10px; -} - -////// Use parent container to increase specifity; -////// to avoid the !important, we would need to use even more parent containers -.slider-track .slider-handle { - height: 1em; - margin-top: -.2em !important; - width: 1em; -} - -//// Accordion / Collapse -////// Toggler BG and arrow -.accordion-toggler { - background: $toggler; - display: block; - padding: ($grid-gutter-width / 2); - text-align: left; - width: 100%; - - &::after { - content: ''; - float: right; - font-family: FontAwesome, sans-serif; - } -} - -.accordion-toggler:hover, -.accordion-toggler:focus, -.accordion-toggler[aria-expanded='true'] { - background: $toggler-active; -} - -.accordion-toggler[aria-expanded='true']::after { - color: darken($brand-secondary, 10%); - content: '\f106'; -} - -.accordion-toggler[aria-expanded='false']::after { - content: '\f107'; -} - -//// Tooltips (the original tooltips are black BG and very narrow, see customVariables) -.tooltip-inner { - padding: .75em; -} - -//// Collapse on tables: -////// normally, .collapse.in elements get assigned 'display: block', for tables it must be: 'display: table' or tables won't be 100% wide -table.collapse.in { - display: table; -} - -//// Collapse on tables - END - -//// Document Delivery Service (DDS) only -////// Error field -.error-field { - color: $brand-danger; - // font-size: ($font-size-base * .95); - font-weight: 500; - line-height: 200%; -} - -//// Document Delivery Service (DDS) only - END - -//// Display styleBasedIcons -.record, -.result { - // make sure that results items display correctly on XS - .media { - padding-left: 0; - padding-right: 0; - } - - .media-left, - .media-right { - &.record-icon img { - display: inline-block; - max-width: none; - width: $thumbnail-width-small; - } - } - - // push COVER down on SM and XS - .media-left { - @media (max-width: $screen-sm-max) { - margin-top: 15px; - } - } - - // cover - .ajaxcover .spinner { - height: 0; - position: absolute; - } - - .cover-container { - min-width: 6em; - } - - // cover - END -} - -//// Sprites for Mediaicons -.sprite-media-icon { - background: url('../../finc/images/sprite-mediaicons.png') 0 0 repeat; - float: left; - height: 54px; - width: 50px; -} - -////// 1st row of sprite -.book { - background-position: 0 0; -} - -.thesis { - background-position: -50px 0; -} - -.ebook { - background-position: -100px 0; -} - -.sets { - background-position: -150px 0; -} - -.electronic { - background-position: -250px 0; -} - -////// 2nd row of sprite -.journal { - background-position: 0 -55px; -} - -.electronicjournal { - background-position: -50px -55px; -} - -.newspaper { - background-position: -100px -55px; -} - -.manuscript { - background-position: -150px -55px; -} - -.braille { - background-position: -200px -55px; -} - -.microfilm { - background-position: -250px -55px; -} - -////// 3rd row of sprite -.software { - background-position: 0 -110px; -} - -.software-set { - background-position: -100px -110px; -} - -.kit { - background-position: -100px -110px; -} - -.image { - background-position: -150px -110px; -} - -.slide { - background-position: -200px -110px; -} - -.globe { - background-position: -250px -110px; -} - -////// 4th row of sprite -.film { - background-position: 0 -165px; -} - -.dvd { - background-position: -50px -165px; -} - -.video { - background-position: -100px -165px; -} - -.video-set { - background-position: -100px -165px; -} - -.map { - background-position: -200px -165px; -} - -.unknown { - background-position: -250px -165px; -} - -////// 5th row of sprite -.audio { - background-position: 0 -220px; -} - -.cd { - background-position: -50px -220px; -} - -.audiotape { - background-position: -100px -220px; -} - -.musicalscore { - background-position: -150px -220px; -} - -.chart { - background-position: -200px -220px; -} - -.physicalobject { - background-position: -250px -220px; -} - -////// 6th row of sprite -.audio-set { - background-position: 0 -275px; -} - -//// Sprites for Mediaicons - END - -// Helper Classes -//// Screen reader hidden content -//// Override nav li postion for screen reader content (in)visibility -.nav > li.sr-only { - position: absolute; -} - -// Class to remplace hidden-sm, hidden-md, hidden-lg -.hidden-sm-up { - @media (min-width: $screen-sm-min) { - display: none; - } -} - -.hidden-sm-md { - @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { - display: none; - } -} - -.visible-sm-only { - @media (max-width: $screen-xs-max) { - display: none; - } - - @media (min-width: $screen-md-min) { - display: none; - } -} - -.visible-sm-md-only { - @media (max-width: $screen-xs-max) { - display: none; - } - - @media (min-width: $screen-lg-min) { - display: none; - } -} - - -// Flex container for wrapping flex elements, such als header -.flex-container { - @media (min-width: $screen-sm-min) { - display: flex; - } -} - -//// we need to reverse order of the header child elements (searchbox + navbar) on anything but mobile for the mobile menu to work as xpected -@media (min-width: $screen-sm-min) { - header .flex-container { - > *:first-child { - order: 2; - } - - > *:nth-child(2) { - order: 1; - } - } -} - -//// force inline display -.inline { - display: inline; - - .template-name-editlist & { - display: inherit; - } -} - -//// floats -.left { - float: left !important; -} - -.right { - float: right !important; -} - -//// Gutters / No-Gutters; Margins / No-margins -////// NO Gutter/Padding at all -.no-gutter-all { - padding: 0 !important; -} - -////// No LEFT and RIGHT gutters -.no-gutter { - padding-left: 0 !important; - padding-right: 0 !important; - - > [class^='col-'] { - padding-left: 0; - padding-right: 0; - } -} - -////// NO gutter top and bottom (keep !important) -.no-gutter-tp-btm { - padding-bottom: 0 !important; - padding-top: 0 !important; -} - -////// NO gutter bottom -.no-gutter-btm { - padding-bottom: 0; -} - -////// NO gutter left -.no-gutter-l { - padding-left: 0; -} - -////// NO gutter top -.no-gutter-t { - padding-top: 0; -} - -////// NO gutter right -.no-gutter-r { - padding-right: 0 !important; -} - -////// gutter left -.gutter-l, -.no-gutter-r.gutter-l { - padding-left: ($grid-gutter-width / 2); -} - -////// gutter bottom -.gutter-btm { - padding-bottom: ($grid-gutter-width / 2); -} - -////// gutter right -.gutter-r { - padding-right: ($grid-gutter-width / 2); -} - -////// gutter top -.gutter-top { - padding-top: ($grid-gutter-width / 2); -} - -////// margin bottom -.margin-btm { - margin-bottom: ($grid-gutter-width / 2); -} - -////// margin left -.margin-l { - margin-left: ($grid-gutter-width / 2); -} - -////// margin left on mobile -.margin-l-xs { - @media (max-width: $screen-xs-max) { - margin-left: ($grid-gutter-width / 2); - } -} - -////// margin right -.margin-r { - margin-right: ($grid-gutter-width / 2); -} - -////// margin top -.margin-t { - margin-top: ($grid-gutter-width / 2); -} - -.margin-t-half { - margin-top: ($grid-gutter-width / 4); -} - -////// NO margin bottom -.no-margin-btm { - margin-bottom: 0; -} - -.margin-btm { - margin-bottom: ($grid-gutter-width / 2); -} - -.margin-btm-half { - margin-bottom: ($grid-gutter-width / 4); -} - -////// NO margin left -.no-margin-l { - margin-left: 0; -} - -////// NO margin right -.no-margin-r { - margin-right: 0; -} - -////// NO margin top -.no-margin-t, -.sidebar h4.no-margin-t { - margin-top: 0; -} - -//// no-cover - prevent no cover images from being easily clickable and loading in lightbox, used in record - cover -.nocover { - cursor: default; -} - -//// Width 100% -.w-100, -.form-control.w-100 { - width: 100%; -} - -//// Color helper classes for development -.border { - &.red { - border: 1px solid $brand-danger; - } - - &.yellow { - border: 1px solid $state-warning-bg; - } - - &.orange { - border: 1px solid $brand-warning; - } - - &.green { - border: 1px solid $brand-success; - } - - &.blue { - border: 1px solid $brand-info; - } -} - -//// Redi link-resolver traffic lights -.resolver .show-availability { - display: inline; -} - -.traffic-light { - display: inline; - padding: 0; - vertical-align: middle; - - span { - background-color: $silver; // temporary, will be overwritten by first, second, third items below - border-top: 3px solid transparent; - display: inline-block; - height: 1em; - margin: 0 -3px 0 0; - padding-right: .5rem; - width: 1em; - - &.last { - margin-right: .5em; - } - } - - &.access-denied { - span { - &.first { - background-color: $brand-danger; - } - - &.second { - background-color: $gainsboro; - } - - &.last { - background-color: $gainsboro; - } - } - } - - &.access-limited { - span { - &.first { - background-color: $gainsboro; - } - - &.second { - background-color: $traffic-light-yellow; - } - - &.last { - background-color: $gainsboro; - } - } - } - - &.access-open { - span { - - &.first { - background-color: $gainsboro; - } - - &.second { - background-color: $gainsboro; - } - - &.last { - background-color: $brand-success; - } - } - } -} - -//// Redi link-resolver traffic lights - END - -// HEADER and NAVBAR FIXED having the same width as rest of the page -// (the header can also run the full width of the viewport!) -header, -.navbar { - background-color: $header-bg-color; -} - -header, -.navbar, -.breadcrumbs { - // For a full-width header de-activate margin and all the media queries below; - // if not using a fixed navbar, also remove '.navbar' above - margin: 0 auto; - - // set width to width of BS container - @media (min-width: $screen-sm-min) { - width: $container-sm; - } - - @media (min-width: $screen-md-min) { - width: $container-md; - } - - @media (min-width: $screen-lg-min) { - width: $container-lg; - } -} - -// NAVBAR -//// Container for Navbar -.banner { - display: flex; // important for subsequent flex elements - - // stack elements on top of each other on XS - @media (max-width: $screen-xs-max) { - flex-direction: column; - } -} - -//// Fixed Navbar (Header) - When using a fixed header + autocomplete, -//// increase the z-index in autocomplete-results to a value higher than the fixed navbar (1030)! -.navbar-fixed { - background-color: $navbar-fixed-bg-color; - // max-width: $row-width; // Find equiv in BS - fixme CK -} - -//// Keep navbar on left, when offcanvas is active, give same appearance as ever -.offcanvas.active .navbar-fixed-top { - @media (max-width: $screen-xs-max) { - margin-top: -$navbar-height-xs; - position: relative; - } -} - -//// Navbar min-height is defined in BS (use customVariables to adjust); -//// however, there is no definition for small devices, so we introduce it here -.navbar { - background-color: $navbar-bg-color; - // Navbar gets a minimum-height of 50px by default, - // activate and change '$navbar-height' in customVariables.scss; - // set height, instead of min-height, or navbar may spill over breadcrumbs when - // body -> padding-top would need to be taller than navbar minimum-height - min-height: $navbar-height; - - .template-name-advanced & { - min-height: 3rem; - } - - header & { - @media (max-width: $screen-xs-max) { - min-height: 9rem; - } - } - - // The margin on navbar h1 PUSHes ALL NAVBAR ELEMENTS DOWN - h1 { - // margin: ($grid-gutter-width / 2) 0 0; ENABLE For VF 3.x Fixme - - span { - display: inline-block; - margin-left: 0; - margin-top: ($grid-gutter-width / 2); - - @media (max-width: $screen-sm-max) { - margin-left: ($grid-gutter-width / 2); - margin-top: ($grid-gutter-width / 8); - } - } - } - - // Searchbox see below - - // Set max height for xs devices - @media (max-width: $screen-xs-max) { - min-height: $navbar-height-xs; - } - - // Define height for navbar on small, allow for stacked search box + - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - min-height: $navbar-height-sm; - //padding-bottom: ($grid-gutter-width / 2); - } -} - -// style navbar toggler content when opened -.navbar-collapse.collapse.in { - @media (min-width: $screen-sm-min) { - background-color: $navbar-bg-color; - position: absolute; - top: calc(#{$navbar-height-xs} * 1.5); // Use this formula to allow for separately calculated $navbar-height in themes - } -} - -// Style language dropdown for XS -.navbar .navbar-nav { - @media (max-width: $screen-xs-max) { - // avoid BG bleed on small - margin-bottom: 0; - } -} - -.language.dropdown { - margin-right: 15px; -} - -.language .dropdown-menu { - background-color: $navbar-bg-color; - border: 0; - min-width: auto; - - .btn:hover, - .btn:focus { - background-color: $btn-language-hover-bg; - color: $btn-language-hover-color; - } - - .active .btn:hover { - background-color: $btn-language-active-hover-bg; - color: $btn-language-hover-color; - } - - li a { - color: $link-color; - - &.btn { - @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); - padding-left: 0; - padding-right: 0; - - &:hover { - background-color: $white; - } - } - } -} - -// Language select when there is only one language -.oneLanguage { - display: block; - margin-top: 0; - position: relative; - - .btn { - background: transparent; - color: $brand-primary; - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - float: right; - padding: 6px 0; - } - - @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { - float: right; - padding: 6px; - } - } - - .visible-sm-only { - text-transform: capitalize; - } -} - - -.navbar-header { - flex: 0 1 auto; - - // ensure proper margins on XS in conjunction with flex-direction: column - @media (max-width: $screen-xs-max) { - .container > & { - margin-left: 0; - margin-right: 0; - } - } -} - -//// Make right-hand header parts fit with searchbox - see also .search.container above -#header-collapse { - @media (min-width: $screen-sm-min) { - // position to the right! - order: 3; - padding-top: 6px; - } - - // Make smaller to fit searchbox and right-hand nav-elements - @media (min-width: $screen-sm-min) { - flex: 1 0 30%; - } - - // Make right-hand header parts full width for advanced search page since it doesn't use any left-hand header parts - .template-name-advanced & { - @media (min-width: $screen-sm-min) { - flex: 0 1 100%; - } - } - - //// Define header navbar on mobile - &.collapse.in { - @media (max-width: $screen-sm-max) { - background-color: $header-bg-color; - // margin-top: -($navbar-height-xs) - 10px; // pull over searchbox - - li > a.btn { - text-align: left; - } - - #langmenu { - padding-left: ($grid-gutter-width / 2); - width: 100%; - } - } - } -} - - -// Remove cart text to make searchbox fit on intermediate tablets -.cart-label { - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - display: none; - } -} - -//// Library name in navbar - we give it the same padding as a button, so if fits in with the header -.navbar-brand { - float: left; - padding: ($grid-gutter-width / 4) ($grid-gutter-width / 2) ($grid-gutter-width / 4) 0; - - img { - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - height: $navigation-element-default-height; - max-width: inherit; - } - } - - &:hover { - @include outline(); - outline-offset: -2px; - } - - @media (max-width: $screen-xs-max) { - height: auto; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - padding-right: ($grid-gutter-width / 4); - } -} - -//// Menu toggler for small -.navbar-toggle { - margin-bottom: 0; - margin-right: 0; - margin-top: 0; - - // toggler icon size - .fa { - font-size: 150%; - } - - &:hover, - &:focus { - background-color: $navbar-default-toggle-hover-bg; - - } -} - -//// navbar-right - add negative right margin to align last button in row to content in sidebar below (right padding) -.navbar-right { - @media (min-width: $screen-sm-min) { - margin-right: -$navbar-padding-horizontal; - } -} - -//// Button sizes in Nav-bars - this is to make them look the same as the 'find' button -.nav > li > a.btn { - @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); - - // make buttons fit on SM - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - padding: $btn-header-nav-medium-only-padding; // 6px 2px - } - - &:hover, - &:focus { - background-color: $component-active-bg; // Same hover-color must be applied to 'Advanced Search' button below - color: $white; - outline: 1px $white dotted; - } -} - -// NAVBAR - END - - -// SEARCHBOX and Autocomplete -// Make seachbox container flexible -- values should complement those of #header-collapse (right-hand side navigation elements) -.search.container { - padding-right: 0; - - // Float "Find" button next to searchbox on XS and SM - @media (max-width: $screen-sm-max) { - padding-left: 0; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - flex: content; - } - - // Make wider to fit searchbox and right-hand nav-elements - @media (min-width: $screen-sm-min) { - flex: 0 1 auto; - padding-top: $button-top-padding; - width: auto; - } -} - -.searchForm { - @media (max-width: $screen-sm-max) { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - margin-bottom: 0; - margin-top: 0; - padding-top: 0; - } - - // Search slot inside the search container - @media (min-width: $screen-sm-min) { - width: 100%; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - display: block; - } - - // Searchform input field + search for media type field - input:not([type='checkbox']), - select.searchForm_type { - @media (max-width: $screen-sm-max) { - display: flex; - flex-grow: 2; - flex-shrink: 2; - } - } - - input:not([type='checkbox']) { - @media (max-width: $screen-sm-max) { - flex-basis: 50%; - } - } - - select.searchForm_type { - @media (max-width: $screen-sm-max) { - flex-basis: 20%; - } - } - - // Fixme: Is there a navbar inside searchForm anywhere? - .navbar { - @media (max-width: $screen-sm-max) { - flex-grow: 1; - flex-shrink: 1; - } - } - - // Keep filters, shards etc; apply same styles to checkboxes on HOME and all other pages, - // therefore use .searchForm as parent - .checkbox { - clear: both; - display: block; - float: none; - padding-top: ($grid-gutter-width / 4); - - @media (max-width: $screen-sm-max) { - // make this correspond to .searchForm @media (max-width: $screen-sm-max) {}, requires "flex-wrap: wrap" on parent - display: flex; - margin-top: .7rem; - padding-top: 0; - width: 100%; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - position: static; - } - - // actual checkbox box - input { - margin-right: .25rem; - // make sure, input and label are aligned in acceptable manner between 100% and 200% zoom - margin-top: $inputs-top-margin; - } - } - - // BUTTONS in search form - .btn, - .form-control { - // align pre-filter and search button - vertical-align: top; - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - float: left; - } - } - - // make advanced search button discernible - .btn-transparent { - @media (max-width: $screen-sm-max) { - border: 1px solid $oil; - } - } -} - -//// Autocomplete: -////// When using a fixed header + autocomplete, increase the z-index in -////// autocomplete-results to a value higher than the fixed navbar (1030)! -.autocomplete-results { - z-index: $zindex-navbar-fixed + 1; -} - -//// Advanced Search links in header -// outer container for adv search links -.navbar-form { - margin-bottom: 0; // overwrites BS theme - - .result-advanced & { - // create sufficient space for header menu - @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { - max-width: 400px; - } - } -} - -.adv_search_terms { - background-color: $brand-secondary; - border: 1px solid $brand-primary; - margin-bottom: 0; - padding: .5em; - width: 100%; -} - -.adv_search_links { - border: 1px solid $brand-primary; - list-style: none; - margin: 0; - padding: 0; - - @media (min-width: $screen-sm-min) { - margin-top: 5px; - } - - a { - border-top: 0; - display: inline; - padding-left: .5em; - - &:last-of-type { - border-bottom: 0; - } - } - - // 786px and above as inline list - @media (min-width: $screen-sm-min) { - a { - border-bottom: 0; - border-right: 1px solid $brand-primary; - display: inline; - padding-right: .5em; - - &:last-of-type { - border-right: 0; - } - } - } -} - - -// SEARCHBOX and Autocomplete - END - -// BREADCRUMBS - for detailed switches see customVariables -//// When Breadcrumbs are added to header.phtml, it might be necessary to remove the breadcrumb bottom margin -.breadcrumb { - font-size: $font-size-breadcrumbs; - margin-bottom: 0; - margin-top: 0; - padding-left: ($grid-gutter-width / 2 - 2px); // take 2 px off for borders - padding-top: 10px; - - // Divider (li + li::before) + Last element (li): - > li, - > li + li::before { - color: $white; - } - - // Result list after an advanced search in width XS should not get this slash - > li + li::before { - @media (max-width: $screen-xs-max) { - display: none; - } - } - - //// color of links in breadcrumbs - a { - color: $white; - } - - > .active a { - color: darken($breadcrumb-active-color, 10%); - } - - //// more contrast: switch the colors - a:focus, - a:hover { - background-color: transparent; - color: darken($white, 10%); - } -} - -// Search result after advanced search needs padding for XS -.result-advanced .breadcrumbs { - @media (max-width: $screen-xs-max) { - padding-top: .6rem; - } -} - -// BREADCRUMBS - END - -// MAIN CONTENT (SIDEBAR BELOW, ADVANCED SEARCH BELOW) -//// set background and add border round main content, also applies to footer -.main .container, -.main .container-fluid, -footer { - background-color: $main-bg; - border: 1px solid $border-color; - padding-bottom: $grid-gutter-width / 1.5; - - @media print { - border: 0; - padding: 0; - } -} - -.mainbody { - @media screen and (max-width: $screen-xs-max) { - width: 100%; - } -} - -footer { - border-top: 0; - padding-top: ($grid-gutter-width / 1.5); -} - -//// Search results -.result-body { - h2 { - margin: 0; - } -} - -// re-define header for records in list of result, #19396 -header { - background-color: transparent; - width: auto; -} - -//// Add top padding to sidebar and content for better looks; -.mainbody, -.sidebar { - padding-top: $content-top-padding; - - @media (max-width: $screen-xs-max) { - padding-top: 0; - } -} - -////// Bulk Action Toolbar -.bulk-action-buttons, -.bulkActionButtons { - clear: both; - padding-bottom: ($grid-gutter-width / 2); - padding-top: ($grid-gutter-width / 2); -} - -// SEARCH-CONTROLS -// remove flex display -.search-header { - display: block; -} - -.search-stats { - flex-grow: unset; -} - -//// Push hits count down to align with limit and sort select boxes -.hits-count { - padding-top: .5em; -} - -// For INLINE, left-floated 'hits per page' and 'sort by' selectors use -.limit, -.search-sort { - float: left; -} - -.limit, -.sort { - margin-bottom: 5px; - - label { - line-height: normal; - margin-bottom: 0; - padding-right: 0; - } - - select { - margin-bottom: 5px; - } -} - -.limit { - margin-right: ($grid-gutter-width / 2); - - @media (max-width: $screen-xs-max) { - float: right; - margin-bottom: 7px; - margin-right: 0; - } - - label { - text-align: left; - } -} - -.limit-inner, -.sort-inner { - display: inline-block; - - // top align buttons and select boxes - .btn { - vertical-align: top; - } -} - -.search-sort { - display: block; -} - -// limit the width of the select field if necessary - for more select details, see FORMS section above -.sort select { - max-width: 12em; - - @media only screen and (max-width: 410px) { - max-width: 9em; - } -} - - -// FIXME: THis needs to be reviewed for #18810 - CK -@media (max-width: $screen-sm-max) { - .limit, - .sort { - float: left; - - button, - label, - select { - display: block; - } - - select { - margin-bottom: 0; - margin-right: .2rem; - } - } - - // Place the refresh button near to the select box - .limit-inner, - .sort-inner { - display: flex; - } -} - -// SEARCH CONTROLS - END - -//// Off-Canvas -.close-offcanvas { - @media (max-width: $screen-xs-max) { - display: block; - } -} - -//// Re-define button arrows by adding space to the :after/:before elements -- see offcanvas.scss -.offcanvas-left { - .close-offcanvas::after { - content: '\00a0\f105'; - } - - .search-filter-toggle::before { - content: '\f100\00a0'; - } - - .record { - .media-left.img-col { - // push record view icon to right (Desktop) on left-aligned sidebars -- pls NOTE: required offcanvas = true - @media (min-width: $screen-sm-min) { - float: right; - margin-left: $grid-gutter-width / 2; - margin-right: 0; - padding-right: 0; - } - } - } -} - -// push record view icon to right (Desktop) on left-aligned sidebars -- pls NOTE: required offcanvas = true -.offcanvas-right { - .close-offcanvas::before { - content: '\f104\00a0'; - } - - .search-filter-toggle::after { - content: '\00a0\f101'; - } -} - -// make 100% wide to avoid conflict with other elements, for back-toggler see below -.offcanvas-toggler, -.offcanvas-toggler button { - width: 100%; -} - -.offcanvas-toggler { - @media print { - display: none; - } -} - -//// Off-Canvas - END - -//// Search Results padding -////// pulled out via .search-results, use negative gutter-width divided by 2 -////// then add padding on results, to create normal column padding appearance -.search-results { - margin-left: -($grid-gutter-width / 2); -} - -//// Results and bulk action buttons -- add padding, to create normal column padding appearance -.result, -.bulk-action-buttons, -.bulkActionButtons { - padding-left: ($grid-gutter-width / 2); - - .btn-group { - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - // truncate "add to bookbag"-button on top of search results to make it fit -- see also '#updateCart' above, CK - margin-left: -($grid-gutter-width / 2); - margin-right: -($grid-gutter-width / 2); - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { - margin-left: -($grid-gutter-width / 2); - } - } -} - -// truncate "add to bookbag"-button on top of search results to make it fit -- see also '.btn-group' above, CK -#updateCart { - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - max-width: 163px; - } -} - -//// Results (see BS SCSS/COMPONENTS/... for details) -.result { - hyphens: auto; - // Results padding - Do not add margin-top or -bottom as it will result in uneven heights - padding-bottom: ($grid-gutter-width / 2); - width: 100%; // make full width, otherwise ugly look - - // remove padding for print - @media print { - padding: 0; - } - - // Alternating colors in search results - &:nth-of-type(2n) { - background-color: $brand-secondary; - } - - .title { - font-size: ($font-size-base * 1.25); // overwrites VF-BS-Theme setting - font-weight: normal; // overwrites VF-BS-Theme setting - - // reset font size for xs - @media (max-width: $screen-xs-max) { - font-size: $font-size-base; - } - - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - font-size: $font-size-base + 2px; - } - } - - // Make add-to-cart icon same color as link - .cart-link-icon { - color: inherit; - } -} - -//// Harmonize behaviour to the edit button -.result-links .dropdown-toggle { - &:focus { - outline: 1px dotted $black; // FIXME: REPLACE with variable, consider using '$input-border-focus' - } -} - -//// Saved in -.savedLists { - - &.alert { - color: $state-light-text-on-dark; - } - - ul { - list-style: none; - padding: 0; - - a { - color: $state-light-text-on-dark; - } - } -} - -//// access-icon in resultlist -.result { - .media-left { - text-align: center; - width: 17%; - - //// remove left padding for print - @media print { - padding-left: 0; - } - - } -} - -.access-icon { - color: $brand-primary; - text-align: center; - - i { - font-size: 4.5em; - width: 100%; - } - - span { - font-size: 1rem; - padding-top: .75rem; - text-align: center; - width: 100%; - - @media screen and (max-width: $screen-xs-max) { - display: block; - } - } -} - -//// Remaining radiusses - The labels.scss uses no variable and format extends it! -//// Therefore it will be necessary to overwite here in compiled.scss!!, Fixme when done more elegantly in BS -//// Also, the display settings aren't very useful (no variables either) -.label, -.result .format, -.sidebar .format { - border-radius: $label-radius; - display: inline-block; // Default is inline - font-weight: normal; // Default is bold - margin-top: .75em; - padding: .5em; // for default see _labels.scss -} - -////// Pagination & Searchtools (for mixin see customVariables.scss) -.pagination, -.searchtools { - @include content-centered-display-as-table; - - @media print { - display: none; - } -} - -//// Fix pagination on XS to display fewer items and remove first and last -.pagination { - @media screen and (max-width: $screen-xs-max) { - li { - // hide last and first page buttons - &.first, - &.last, - // select 7th to 11th child - &:nth-child(n+10):nth-child(-n+13) { - display: none; - } - } - } -} - -// Display searchtools-links as buttons on XS, SM -@media screen and (max-width: $screen-sm-max) { - .searchtools { - a { - background-color: $white; - border: 1px solid $iron; - color: $oil; - // align on top of each other - display: block; - margin-bottom: 5px; - padding: 6px 12px; - text-align: center; - touch-action: manipulation; - vertical-align: middle; - white-space: nowrap; - } - } -} - -////// Pagination & Searchtools - END - -////// [List] view=tabs - -.template-dir-search.template-name-results .tab-content .offcanvas-toggler { - display: none; -} - -////// [List] view=tabs - END - -//// Search results - END - -//// Record View (Detail view) -////// Pager -/////// The pager occurs in record/view but also in collection/view -.pager { - margin-bottom: 0; - - .disabled { - display: none; - } -} - -////// Pager - END - -////// Tabs -/////// Add padding-left to tabs area -.record-tabs { - padding-left: 1em; - - @media screen and (max-width: $screen-xs-max) { - padding-left: 0; - } -} - -/////// Add left and right borders -.tab-content { - border-bottom: 1px solid $nav-tabs-border-color; - border-left: 1px solid $nav-tabs-border-color; - border-right: 1px solid $nav-tabs-border-color; - padding: ($grid-gutter-width / 2); - - // make long ai-Links break to prevent overspilling content on XS - a { - word-break: break-word; - } -} - -/////// Format items in availability column -.availability-column { - @media (max-width: $screen-sm-max) { - a { - display: block; - margin-top: 6px; - word-break: break-word; - } - } -} - -//////// Tab-content active needs to display 'inline-block' top display the border correctly (or content will bleed over) -.tab-content > .active { - display: inline-block; - // make tables inside tabs use full width - width: 100%; -} - -////// branch info, opening hours etc -.holdings-tab { - .collapse.in { - padding-left: ($grid-gutter-width / 2); - } -} - -/////// Holdings, Access-Tab -.holdings-tab { - // increase 'line height' for links to improve readability - &.tab-pane { - ul { - margin-top: 1em; - padding-left: 0; - } - - li { - margin-bottom: .5em; - } - } -} - -////// Hierarchy tree -#treeSelector { - border-bottom: $border-default-styles; - margin-bottom: 1em; - padding-bottom: 1em; - - .item { - margin-right: 2em; - - &:last-of-type { - margin-right: 0; - } - } -} - - -#hierarchyTreeHolder { - border-right: 0; -} - -#hierarchyTree { - ul.jstree-container-ul { - padding-left: 0; - } - - /////// Make dropdown-icon wider - .jstree-closed > .jstree-ocl::before, - .jstree-open > .jstree-ocl::before { - font-size: 150%; - margin-left: 0; - margin-top: -5px; - min-width: 18px; // do not translate into em - } - - .jstree-closed > .jstree-ocl, - .jstree-open > .jstree-ocl { - - &:hover::before, - &:focus::before { - color: $jstree-ocl-hover-before-color; - } - } - - li.jstree-facet, - li.jstree-node { - line-height: 1.75; - list-style: none; - padding-left: $jstree-li-left-padding; - } - - li li::before { - // FontAwesome Unicode fa-file-o - content: '\f016'; - display: inline-block; - font-family: FontAwesome; - // same as padding-left set on li, above - margin-left: -$jstree-li-left-padding; - //same as padding-left set on li, above - width: $jstree-li-left-padding; - } - - // Align child elements under open/close toggle - .jstree-children { - margin-top: .75em; - padding-left: 0; - } - - // prevent tree LIs from overspilling on small! - .jstree-anchor { - hyphens: auto; - padding-left: 0; - white-space: normal; - } - - // Current item - .jstree-clicked { - color: $jstree-clicked-color; - padding-bottom: .25em; - padding-right: .5em; - padding-top: .3em; - - &:hover, - &:focus { - color: $jstree-clicked-hover-color; - } - } -} - -.hierarchy-tree .jstree-ocl::before, -.jstree-facet .jstree-ocl::before { - float: left; - margin-left: 0; - padding: 0; - width: 0; -} - - -////// Hierarchy tree - END - -////// Tabs - END -////// Similar items carousel - remove 3d effects -.carousel-control { - color: $brand-primary; - text-shadow: none; - - &.left, - &.right { - background-image: none; - - &:hover { - background-image: none; - color: lighten($brand-primary, 2%); - } - } -} - -// depth required for specificity -#similar-items-carousel .carousel-indicators li { - border-color: $brand-primary; - box-shadow: none; - height: $font-size-base * 1.5; - width: $font-size-base * 1.5; -} - -.carousel-indicators .active { - box-shadow: none; -} - -////// Similar items carousel - END - -//// Record View - END - -//// Home Page -////// Add top margin for better looks -.searchHomeContent { - margin-top: 1em; - - @media (max-width: $screen-xs-max) { - // Fixme: Where is .well used? CK - .well { - background-color: transparent; - border: 0; - box-shadow: none; - min-height: ($grid-gutter-width / 2); - padding: 0; - } - } -} - -////// browsing suggestion lists on home (prevent torn-apart look when 2 columns only) -.browsesuggest.columns + .columns:last-child { - float: left; -} - -//// Home Page - END - -//// Advanced Search -// .tab-content.adv-search container wraps advanced search terms and links -.adv-search { - @media (max-width: $screen-xs-max) { - margin-bottom: 0; - margin-left: -($half-gutter); - } - - // keep this parent for overwriting specifity - // input field - .adv-term-input { - // overwrite finc - @media (min-width: $screen-sm-min) { - width: 99%; - } - } - - // keep this parent for overwriting specifity - // search type select - .adv-term-type { - - // overwrite finc - @media (min-width: $screen-sm-min) { - max-width: 99%; - } - } - - // keep this parent for overwriting specifity - // Suchfeld remove-X-Buttons - .adv-term-remove { - // overwrite finc - height: unset; - margin-top: 26px; - opacity: unset; - width: 44px; - - @media (max-width: $screen-xs-max) { - border: $border-default-styles; - line-height: 1.5; - margin-top: 29px; - } - - @media (min-width: $screen-md-min) { - margin-left: 2px; - } - } -} - -.adv-input, -.adv-select { - float: left; - - @media (max-width: $screen-xs-max) { - padding-left: $half-gutter; - } -} - -.adv-input { - @media (max-width: $screen-xs-max) { - width: 100%; - } - - @media (min-width: $screen-sm-min) { - width: 59%; - } - - @media (min-width: $screen-md-min) { - width: 62%; - } -} - -.adv-select { - - @media (min-width: $screen-sm-min) { - max-width: unset; - width: 28%; - } -} - -////// Set colors for remove search field buttons -.adv-term-remove { - color: $alert-danger-color; - - &:hover, - &:focus { - color: $brand-danger; - opacity: 1; - } -} - -.add_search_link, -.adv-group-close { - margin-bottom: 4px; - padding-bottom: $button-top-padding; - padding-top: $button-top-padding; - - @media (max-width: $screen-xs-max) { - margin-top: $half-gutter; - } -} - -// this is the "remove search group" button -.adv-group-close { - color: $state-danger-text; - - @media (min-width: $screen-sm-min) { - float: right; - } - - i { - color: $brand-danger; - } -} - -////// Swap find and clear buttons for consistency -.adv-submit { - .fnd-btn { - float: right; - } - - .clear-btn { - float: left; - } -} - -//// Advanced Search - END - -//// Sources List (/sources/home.phtml) -#sources-list { - line-height: 1.5; - // align to left edge - padding-left: 0; - - // remove list style on top level as sources have a number! - li { - list-style: none; - padding-bottom: .5em; - - // use list style on child elements - ul li { - list-style: initial; - padding-bottom: 0; - } - } -} - -//// Sources List - END - -.browse-item.active { - &:focus, - &:hover { - background-color: $brand-primary; - text-decoration: underline; - } -} - -// MAIN CONTENT - END - -// SIDEBAR -// Width when offcanvas is off -body:not(.offcanvas) .sidebar { - @media (max-width: $screen-xs-max) { - width: 100%; - } -} - -//// pull content to right border for sidebar right -.container .sidebar { - &.right { - @include pull-sidebar-to-right($margin-right-width); - padding-left: 0; - } -} - -//// Style myaccount sidebar menues to get the same look as facets -.myresearch-menu { - - a { - width: 100%; - - &:not(:last-of-type) { - border-bottom: 1px solid $border-color; - padding: $sidebar-item-padding; // overwrite values from BS with more sensible values - } - } - - .facet { - padding: 0 !important; - } -} - -//// Style facets to have the same height as items in myresearch-menu -.facet-group { - .facet, - .title { - padding: $sidebar-item-padding; - } - - .facetOR.active { - // Style active facets to get proper left padding - .text { - text-indent: -3px; - } - } - - &:not(.active-filters) .title { - &:hover, - &:focus { - @include outline(); - background-color: $button-title-hover-bg; - } - } - - > li { - margin: 0; - padding: 0; - } - - ////// Pull exclude facets to the right, align with accordion/collapse triangles and headings - .facet.excludable { - padding-left: 1.5em; - padding-right: .5em; - } -} - -//// Remove right-hand borders for facets on the right only, -//// ONLY works when sidebar butts right border -.sidebar.right { - .facet-group .facet, - .facet-group .title, - .facet-group .collapse, - .facet-group .collapsing, - .facet-group > .facet { - @include right-border-on-sidebar($border-right-width); - } -} - - -//// Offcanvas is used to widths of 767px ($screen-xs-max) -.offcanvas.active .sidebar { - @media (max-width: $screen-xs-max) { - padding-right: ($grid-gutter-width / 2); - } -} - -//// Sidebar standard items - fixme -.sidebar a { - // border: 1px solid $border-color; - // margin-bottom: -1px; -} - -//// Selected/active filters on top - fixme -.sidebar .facet.active, -.sidebar a.active { - background-color: $brand-warning; - color: $black; - - // also include icon - .fa { - color: inherit; - } -} - -//// Collapse-Facet Titles -.facet.title, -.sidebar a.title { - background-color: $brand-secondary; -} - -//// Create hover-effect -.facet:hover:not(.button), -.facet:focus:not(.button) { - background: $sidebar-item-hover-bg; - - .active-filters & { - background: $sidebar-item-active-hover-bg; - } -} - -.facet.active:hover:not(.button), -.facet.active:focus:not(.button) { - background: $sidebar-item-active-hover-bg; -} - -.sidebar .facet.active { - &:focus, - &:hover { - color: $white; - - // also include icon - .fa { - color: inherit; - } - } -} - -// #13625 avoid ugly linebreaks with overlong words, RL -.sidebar { - -moz-hyphens: auto; - -ms-hyphens: auto; - -o-hyphens: auto; - -webkit-hyphens: auto; - hyphens: auto; - - .facet { - -ms-word-break: break-all; - word-break: break-all; // fallback for hyphens: auto (Chrome on Desktop and bug on Mozilla for capitalized words) - - a, - .text { - -ms-word-break: break-word; - word-break: break-word; - } - - a { - text-decoration: none; - } - } - - // special case: on sm-size AND search result facats - @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { - & div[id^='side-panel-'] { - .facet { - a, - .text { - -ms-word-break: initial; - word-break: initial; - } - } - } - } -} - -//// OR- and AND-Facets + item counts in MyAccount -///// Style the exclude icons -.exclude i { - color: $badge-link-color; - - &:hover, - &:focus { - color: $badge-link-hover-color; - } -} - -.itemCount { - &:hover, - &:focus { - color: $badge-link-hover-color; - - .active & { - color: $brand-secondary; - } - } -} - -.badge { - font-size: 1em; - // remove right padding to align with accordion arrows - padding-right: 2px; - - &.btn-danger, - &.btn-info, - &.btn-success, - &.btn-transparent, - &.btn-warning { - &:focus, - &:hover { - color: $btn-badge-hover-color; - } - } - - // Numbers in MyAccount sidebar - &.ok, - &.overdue { - .myresearch-menu & { - color: $white; - // unset height attribute from .facet .badge {} definition for better looks - max-height: unset; - padding: 3px 7px; - } - } - - ////// light color on dark, when active (Facets + My Account) - we need the parent for specificity - .facet.active > &, - .sidebar a.active > & { - background-color: transparent; - color: $btn-primary-color; - } -} - -//// OR- and AND-Facets, item counts - END - -//// More-Toggle (for variable, see customVariables.scss) -.narrow-toggle { - text-align: $narrow-toggle-text-alignment; -} - -//// More-Toggle - END - -//// Toolbar in Detailview -////// Offcanvas back button -.sidebar .close-offcanvas { - // make same as toolbar padding, below - margin-right: $record-view-toolbar-button-padding; - text-align: left; - width: 100%; -} - -@media (min-width: $screen-sm-min) { - .nav-stacked.nav > li > a { - padding-left: 0; - padding-right: 0; - } -} - -// Override record.scss value here to keep it consistent with .nav .btn-bookbag-toggle a -.record-nav > li > a, -.record-nav > li a { - padding: $record-view-toolbar-button-padding; -} - -.record-nav > li > a { - @media (max-width: $screen-xs-max) { - text-align: left; - } -} - -//// Toolbar in Detailview - END - -// SIDEBAR - END - -// FOOTER -//// For border around footer and background + top padding, see 'footer' above - -//// Align footer lists to their headings -footer ul { - padding-left: ($grid-gutter-width / 2); -} - -.powered-by { - padding: ($grid-gutter-width / 2); - - img { - height: 1.75em; - margin-left: ($grid-gutter-width / 4); - margin-right: ($grid-gutter-width / 4); - } - - span { - font-size: small; - } - - a:hover { - @include outline(); - } -} - -// FOOTER - END - -// MODALS -//// Prevent transparent lightbox bug -- keep !important to override! -.modal-dialog .mainbody.left { - float: none !important; -} - -.modal td[data-title="openURL:"], -.modal .table-resp-data td { - word-break: break-word; -} - -//// Set modal width to make better use of the screen -@media (max-width: $screen-xs-max) { - .modal-dialog { - max-width: 80%; // keep close-button visible - } -} - -@media (min-width: $screen-sm-min) { - .modal-dialog { - max-width: 85%; - min-width: $modal-md; - width: auto; - - // complex nesting required to overwrite BS values, CK - #modal & { - input.form-control { - max-width: em(400px); - } - } - } -} - -@media (min-width: $screen-md-min) { - // Automatically set modal's width for larger viewports - .modal-dialog { - width: $modal-lg; - } -} - -@media (min-width: $screen-lg-min) { - .modal-dialog { - max-width: em(900px); - } -} - -// fix modal adding padding-right #13625 -.modal-open { - overflow: initial; -} - -// Modal close button (Needs to be as fancy to override BS styles) -#modal .modal-content > .close, -#modal .modal-content > .adv-term-remove { - background: $modal-close-bg; - border: 0; - border-radius: $modal-close-br; - color: $modal-close-color; - left: auto; - opacity: unset; - right: 0; - - @media (min-width: $screen-sm-min) { - right: -60px; - - body.rtl & { - left: -60px; - } - } - - &:focus, - &:hover { - background-color: $modal-close-bg-hover; - color: $modal-close-color-hover; - opacity: unset; - } -} - -// MODALS - END - -// Bookbag -//// Style items list in bookbag -.cart ul li { - margin-bottom: .5em; -} - -.record-checkbox { - float: left; - margin-right: .5em; - - input[type='checkbox'], - input[type='checkbox']:focus { - // make sure, input and label are aligned in acceptable manner between 100% and 200% zoom - margin-top: $inputs-top-margin; - } -} - -// Bookbag - END - -// Result List - -.result-links { - // Favorites list in MyAccount - .edit.tool, - .del-button { - a { - margin-top: 1em; - - @media (max-width: $screen-sm-max) { - margin-top: 0; - } - } - - li a { - margin-top: 0; - } - } -} - -// Favorites list description -.list-desc { - margin-top: 1rem; -} - -// Result List - END - -// Translate feature (Admin panel) -#set-translation-form { - margin-top: 2em; -} - -#set-translation label { - font-weight: bold; -} - -.checkbox-inline input[type='checkbox'] { - margin-top: 0; -} - -#language-table_filter { - text-align: right; -} - -#language-table, -#set-translation { - thead { - background-color: $brand-secondary; - color: $charcoal; - } - - tr th { - // prevent sort icon from breaking - padding-bottom: .75rem; - padding-top: 1rem; - white-space: nowrap; - } -} - -.sorting, -.sorting_asc, -.sorting_desc { - cursor: pointer; - - .fa-fw::before { - display: inline; - } -} - - -.sorting_asc, -.sorting_desc { - color: $brand-primary; - - .fa-fw::before { - color: inherit; - } -} - - -.sorting { - color: lighten($charcoal, 5%); - - .fa-fw { - position: relative; - - &::before { - content: '\f0dc'; - } - } -} - -.sorting_asc .fa-fw::before { - content: '\f0de'; -} - -.sorting_desc .fa-fw::before { - content: '\f0dd'; -} - -// Accessibility - -a.exclude { - &:hover, - &:focus { - @include outline(1px); - - color: $badge-link-hover-color; - outline-offset: -2px; - } - -} - -input { - &[type='checkbox'], - &[type='radio'] { - - &:focus, - &:hover { - @include outline(2px); - } - } -} - -.alert-danger, -.alert-info, -.alert-success, -.alert-warning { - a:focus, - a:hover { - background-color: $state-link-hover-bg; - color: $state-link-hover-color; - } -} - -// Accessibility - END - -// Account -.has-error .form-control, -.sms-error .form-control { - border: 1px solid $brand-danger; - - &:focus, - &:hover { - border: 1px solid $brand-danger; - } -} - - -// Account - END - -// Dropdown Toggler color -// WARNING: Potential Breaking Change! -// e.g. bookbag delete button with dropdown -- could result in white text on white background -// --> introduce variable "$btn-transparent-active-color" in custom themes and define color -// FIXME: MOVE TO buttons or dropdowns section -.open .btn-transparent.dropdown-toggle { - &[aria-expanded="true"] { - color: $btn-transparent-active-color; - } - - &:active, - &:focus, - &:hover { - color: $btn-transparent-active-color; - } -} - -// Delete dropdown in favorites list -.open > .dropdown-menu { - .template-dir-myresearch.template-name-mylist .result-links & { - float: none; - position: relative; - } -} - -// AMSL -.template-dir-amsl.template-name-sources-list { - .panel-body span { - margin: 10px 15px 10px 0; - } -} - -// #17407 change text of button when expanded / collapsed -#collapse-all-toggler:not(.expanded) .text-expanded { - display: none; -} - -#collapse-all-toggler.expanded .text-collapsed { - display: none; -} - -// AMSL - END - -.template-dir-browse.template-name-home { - .browse-container { - @media (max-width: $screen-xs-max) { - padding-top: 15px; - } - } -} - - -// CHANNELS -.slick-arrow:not(.slick-disabled).slick-next, -.slick-arrow:not(.slick-disabled).slick-prev { - &:focus::before, - &:hover::before { - color: $slick-arrow-hover-color; - outline: 2px $black dotted; - } -} - -// CHANNELS - END - -// ZEND-FORMS - -#dds-form { - - label[data-required="true"][for]::after { - content: ' *'; - } - - .form-group { - ul li { - color: $brand-danger; - } - } -} - -// ZEND-FORMS - END +@import 'finc'; diff --git a/themes/finc/scss/components/_accordions-collapse-elements.scss b/themes/finc/scss/components/_accordions-collapse-elements.scss new file mode 100644 index 0000000000000000000000000000000000000000..e336ad88564c396f4c8772a59823022e8dd2747e --- /dev/null +++ b/themes/finc/scss/components/_accordions-collapse-elements.scss @@ -0,0 +1,42 @@ +// Use this for all general accordion styles +// For collapsibles in Record Tabs see record-tabs.scss + + +// Accordion / Collapse: Toggler background and arrow +.accordion-toggler { + background: $toggler; + display: $accordion-toggler-display; + padding: $accordion-toggler-padding; + text-align: $accordion-toggler-text-align; + width: $accordion-toggler-width; + + &::after { + // this gets filled below + content: ''; + float: right; + font-family: FontAwesome, sans-serif; + } +} + +.accordion-toggler:hover, +.accordion-toggler:focus, +.accordion-toggler[aria-expanded='true'] { + background: $toggler-active; +} + +.accordion-toggler[aria-expanded='true']::after { + color: $accordion-toggler-icon-content-expanded-color; + content: $accordion-toggler-icon-content-expanded; +} + +.accordion-toggler[aria-expanded='false']::after { + content: $accordion-toggler-icon-content-closed; +} + + +// Collapse on tables: +// normally, .collapse.in elements get assigned 'display: block', +// for tables it must be: 'display: table' or tables won't be 100% wide +table.collapse.in { + display: table; +} diff --git a/themes/finc/scss/components/_admin-panel.scss b/themes/finc/scss/components/_admin-panel.scss new file mode 100644 index 0000000000000000000000000000000000000000..7a16539fb24e1b200463231b286d4d9ec216a490 --- /dev/null +++ b/themes/finc/scss/components/_admin-panel.scss @@ -0,0 +1,74 @@ +// Use this for all admin-panel/translate-feature-related styles + +// Translate feature (Admin panel) +#set-translation-form { + margin-top: $translation-form-margin-top; +} + +#set-translation label { + font-weight: $translation-form-label-font-weight; +} + +.checkbox-inline input[type='checkbox'] { + margin-top: $translation-form-input-checkbox-margin-top; +} + +#language-table_filter { + text-align: $translation-form-language-table-filter-text-align; +} + +#language-table, +#set-translation { + thead { + background-color: $translation-form-tables-thead-background-color; + color: $translation-form-tables-thead-color; + } + + tr th { + // prevent sort icon from breaking + padding-bottom: $translation-form-tables-th-padding-bottom; + padding-top: $translation-form-tables-th-padding-top; + white-space: $translation-form-tables-th-white-space; + } +} + + +.sorting, +.sorting_asc, +.sorting_desc { + cursor: $translation-form-sorting-cursor; + + .fa-fw::before { + display: inline; + } +} + + +.sorting_asc, +.sorting_desc { + color: $translation-form-sorting-asc-desc-color; + + .fa-fw::before { + color: inherit; + } +} + +.sorting { + color: $translation-form-sorting-color; + + .fa-fw { + position: relative; + + &::before { + content: $translation-form-sorting-icon; + } + } +} + +.sorting_asc .fa-fw::before { + content: $translation-form-sorting-asc-icon; +} + +.sorting_desc .fa-fw::before { + content: $translation-form-sorting-desc-icon; +} diff --git a/themes/finc/scss/components/_alerts.scss b/themes/finc/scss/components/_alerts.scss new file mode 100644 index 0000000000000000000000000000000000000000..11180d47637ca23908bdf8543297ab5a7fb6d490 --- /dev/null +++ b/themes/finc/scss/components/_alerts.scss @@ -0,0 +1,45 @@ +// Use this for all general alerts styles + +// Light Text on Dark BG +.alert { + + a { + color: $state-light-text-on-dark; + text-decoration: $state-link-text-decoration; + } + + &.alert-warning a { + color: $state-link-alert-warning-color; + } + + // Alerts inside the Holdings tab + .holding-info & { + margin-left: $state-inside-holding-info-margin-left; + margin-right: $state-inside-holding-info-margin-right; + margin-top: $state-inside-holding-info-margin-top; + } +} + +// Alert Text Colors +.alert-danger, +.alert-info, +.alert-success { + clear: both; + color: $state-light-text-on-dark; + + &:hover { + color: $state-light-text-on-dark; + } +} + +// Accessibility +.alert-danger, +.alert-info, +.alert-success, +.alert-warning { + a:focus, + a:hover { + background-color: $state-link-hover-bg; + color: $state-link-hover-color; + } +} diff --git a/themes/finc/scss/components/_amsl.scss b/themes/finc/scss/components/_amsl.scss new file mode 100644 index 0000000000000000000000000000000000000000..94a0b0450febf9977d978602d5d98b9794dff017 --- /dev/null +++ b/themes/finc/scss/components/_amsl.scss @@ -0,0 +1,36 @@ +// Use this for all AMSL element styles (record view) + +// amsl/sources-list - home +#sources-list { + line-height: $amsl-sources-list-line-height; + // align to left edge + padding-left: $amsl-sources-list-padding-left; + + // remove list style on top level as sources have a number! + li { + list-style: $amsl-sources-list-li-top-level-list-style; + padding-bottom: $amsl-sources-list-li-top-level-list-padding-bottom; + + // use list style on child elements + li { + list-style: initial; + padding-bottom: 0; + } + } +} + +.template-dir-amsl.template-name-sources-list { + .panel-body span { + margin: $amsl-sources-list-panel-body-span-margin; + } +} + +// #17407 change text of button when expanded / collapsed +#collapse-all-toggler:not(.expanded) .text-expanded { + display: none; +} + +#collapse-all-toggler.expanded .text-collapsed { + display: none; +} + diff --git a/themes/finc/scss/components/_bookbag.scss b/themes/finc/scss/components/_bookbag.scss new file mode 100644 index 0000000000000000000000000000000000000000..c42fc1203168d486e87a7f3dffa9d39bec40ba73 --- /dev/null +++ b/themes/finc/scss/components/_bookbag.scss @@ -0,0 +1,56 @@ +// Use this for all bookbag-related styles +// For 'Add to Bookbag' in bulk action buttons see buttons.scss + + +// ***************************************************************** +// ************ Add-to-bookbag icons ******************************* +// ***************************************************************** + +// overwrite add-to-bookbag icons +.btn-type-add::before { + content: $book-bag-add-to-icon-content; +} + +// overwrite remove-from-bookbag icons +.btn-type-minus::before { + content: $book-bag-remove-from-icon-content; +} + + + +// ***************************************************************** +// ************ Add-to-bookbag in Sidebar ************************** +// ***************************************************************** + +// Add-to-Bookbag Buttons in SIDEBAR (Record view/details view) +.sidebar .btn-bookbag-toggle { + width: $book-bag-add-to-in-sidebar-toggler-width; + + // Keep padding same as '.record-nav > li > a' + // otherwise 'Add to Bookbag' in Record View will be out of line + a { + background: $book-bag-add-to-in-sidebar-toggler-background; + border-radius: $book-bag-add-to-in-sidebar-toggler-border-radius; + // make 'Add to Bookbag' in Record View look the same as other links + color: $book-bag-add-to-in-sidebar-toggler-color; + padding: $book-bag-add-to-in-sidebar-toggler-padding; + // lend this the same look as the other links + text-decoration: $book-bag-add-to-in-sidebar-toggler-text-decoration; + + @media (max-width: $screen-sm-max) { + padding: $record-view-toolbar-button-padding $record-view-toolbar-button-padding $record-view-toolbar-button-padding $record-view-toolbar-button-padding-add-to-bookbag; + text-align: left; + } + } +} + + + +// ***************************************************************** +// ************ Bookbag in Lighbox/on sep. Page ******************** +// ***************************************************************** + +// Items list in bookbag +.cart ul li { + margin-bottom: $book-bag-list-item-margin-bottom; +} diff --git a/themes/finc/scss/components/_breadcrumbs.scss b/themes/finc/scss/components/_breadcrumbs.scss new file mode 100644 index 0000000000000000000000000000000000000000..2dab593b5c7e4f17c8ea459ce311ca04184510b0 --- /dev/null +++ b/themes/finc/scss/components/_breadcrumbs.scss @@ -0,0 +1,51 @@ +// Use this file to style all your breadcrumbs-related styles +// For variables see customVariables +// For '.breadcrumbs' margins see header-navbar.scss + +// '.breadcrumbs' ist the outer container, '.breadcrumb' is the actual ul container + +.breadcrumbs { + // Search result after advanced search needs padding for XS + .result-advanced { + @media (max-width: $screen-xs-max) { + padding-top: $breadcrumbs-advanced-results-top-padding; + } + } +} + +// When Breadcrumbs are added to header.phtml, +// it might be necessary to remove the breadcrumb bottom margin +.breadcrumb { + font-size: $font-size-breadcrumbs; + margin-bottom: $breadcrumbs-bottom-margin; + margin-top: $breadcrumbs-top-margin; + padding-left: $breadcrumbs-left-padding; + padding-top: $breadcrumbs-top-padding; + + // Divider (li + li::before) + Last element (li): + > li, + > li + li::before { + color: $breadcrumb-divider-color; + + // remove slash in result list after an advanced search for XS + @media (max-width: $screen-xs-max) { + display: $breadcrumbs-divider-display-xs; + } + } + + // color of links in breadcrumbs + a { + color: $breadcrumb-color; + + // more contrast: switch the colors + &:focus, + &:hover { + background-color: $breadcrumb-anchor-hover-bg; + color: $breadcrumb-anchor-hover-color; + } + } + + > .active a { + color: $breadcrumb-active-color; + } +} diff --git a/themes/finc/scss/components/_bulk-action-buttons.scss b/themes/finc/scss/components/_bulk-action-buttons.scss new file mode 100644 index 0000000000000000000000000000000000000000..9f313c846409b063f1d7e405e407d6c7cc71a537 --- /dev/null +++ b/themes/finc/scss/components/_bulk-action-buttons.scss @@ -0,0 +1,55 @@ +// Use this for bulk action styles +// If you want the bulk action buttons visible on XS devices, import the following file +// @import '../activate-on-demand/bulkaction-visible-xs'; + +// ***************************************************************** +// ************ Bulk Action Toolbar ******************************** +// ***************************************************************** + +.bulk-action-buttons, +.bulkActionButtons { + clear: $bulk-action-buttons-element-clear; + padding-bottom: $bulk-action-buttons-element-padding-bottom; + padding-left: $bulk-action-buttons-element-padding-left; + padding-top: $bulk-action-buttons-element-padding-top; + + .btn-group { + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + // truncate 'add to bookbag'-button on top of search results to make it fit + // -- see also '#updateCart' below, CK + margin-right: $bulk-action-buttons-element-btn-group-sm-margin-right; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { + margin-left: $bulk-action-buttons-element-btn-group-sm-md-margin-left; + } + } +} + +// 'Add to Bookbag' in bulk action buttons +// truncate 'add to bookbag'-button on top of search results +// to make it fit -- see also '.btn-group' in buttons.scss +#updateCart { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + max-width: $bulk-action-buttons-element-add-to-bookbag-sm-max-width; + } +} + + + +// ***************************************************************** +// ************ Check boxes for bulk actions *********************** +// ***************************************************************** + +.record-checkbox { + float: $bulk-action-record-view-checkbox-container-float; + margin-right: $bulk-action-record-view-checkbox-container-margin-right; + + input[type='checkbox'], + input[type='checkbox']:focus { + // make sure, input and label are aligned in acceptable manner between 100% and 200% zoom + margin-top: $bulk-action-record-view-checkbox-input-margin-top; + } +} + diff --git a/themes/finc/scss/components/_buttons.scss b/themes/finc/scss/components/_buttons.scss new file mode 100644 index 0000000000000000000000000000000000000000..62ce5e102b99a1098d87deb2c4575ab2f3db9826 --- /dev/null +++ b/themes/finc/scss/components/_buttons.scss @@ -0,0 +1,105 @@ +// Use this for all _general_ button styles +// For Search Form buttons see search.scss +// For Header Navigation buttons see header-navbar.scss +// For Add-to-Bookbag buttons see bookbag.scss +// For bulk-action-buttons see bulk-action-buttons.scss + +// ***************************************************************** +// ************ General buttons ************************************ +// ***************************************************************** + +// Buttons have a DEFAULT HEIGHT of 38px, the same +// as '$navigation-element-default-height' for select boxes and other elements + +// Default and secondary buttons +.btn-default.active, +.btn-secondary.active { + &:focus, + &:hover { + background-color: $btn-active-hover-bg; + } +} + +// Info buttons +.btn-info { + &:focus, + &:hover { + background-color: $btn-info-hover-bg; + } + + &.active { + &:focus, + &:hover { + background-color: $btn-info-active-hover-bg; + } + } +} + +// Primary buttons +.btn-primary, +.btn-primary.active { + &:focus, + &:hover { + background-color: $btn-primary-hover-bg; + color: $btn-primary-hover-color; + } + + // On datalightbox: + &[disabled="disabled"] { + color: $btn-primary-disabled-color; + } +} + +.btn-primary.disabled { + &:focus, + &:hover { + background-color: $btn-primary-disabled-hover-bg; + color: $btn-primary-disabled-hover-color; + } +} + +// Secondary buttons +.btn-secondary { + &:focus, + &:hover { + border: 1px solid $btn-secondary-border-color; + } +} + +// Transparent buttons (button with border and darker text on light or white BGs) +.btn-transparent { + @include button-variant($btn-transparent-color, $btn-transparent-bg, $btn-transparent-border); + + &:focus, + &:hover { + color: lighten($btn-transparent-color, 80%); + } + + &.active { + color: $btn-transparent-active-color; + + &:focus, + &:hover { + background-color: $btn-transparent-active-color; + color: $black; + } + } +} + +// Success buttons +.btn-success.active { + &:focus, + &:hover { + background-color: $btn-success-active-hover-bg; + } +} + +// Toolbar buttons, cart buttons ... +// for more on bookbag button in Record View (sidebar), see bookbag.scss +.toolbar-btn, +.record-nav .cart-add, +.record-nav .cart-remove, +.reset-filters-btn { + border-radius: $toolbar-button-radius; +} + diff --git a/themes/finc/scss/components/_carousel-similar-items-channels-slider.scss b/themes/finc/scss/components/_carousel-similar-items-channels-slider.scss new file mode 100644 index 0000000000000000000000000000000000000000..a8c5353b947497bf60f45ebd674ba6f0130c1b1c --- /dev/null +++ b/themes/finc/scss/components/_carousel-similar-items-channels-slider.scss @@ -0,0 +1,82 @@ +// Use this for all carousel and channel element styles + +// ***************************************************************** +// ************ Carousels ****************************************** +// ***************************************************************** + +.carousel-control { + color: $carousel-control-elements-color; + text-shadow: $carousel-control-elements-text-shadow; + + &.left, + &.right { + background-image: $carousel-control-elements-left-right-background-image; + + &:hover { + background-image: $carousel-control-elements-left-right-background-image-hover-background-image; + color: $carousel-control-elements-left-right-background-image-hover-color; + } + } +} + +// ID selector required for specificity +#similar-items-carousel .carousel-indicators li { + border-color: $similar-items-carousel-indicators-li-border-color; + box-shadow: $similar-items-carousel-indicators-li-box-shadow; + height: $similar-items-carousel-indicators-li-height; + width: $similar-items-carousel-indicators-li-width; +} + +.carousel-indicators .active { + box-shadow: $similar-items-carousel-indicators-active-box-shadow; +} + + +// ***************************************************************** +// ************ Channels ******************************************* +// ***************************************************************** +.slick-arrow:not(.slick-disabled).slick-next, +.slick-arrow:not(.slick-disabled).slick-prev { + &:focus::before, + &:hover::before { + @include outline(dotted, 2px, $input-border-focus); + + color: $slick-arrow-hover-color; + } +} + +.slick-next, +.slick-prev { + // increase size for accessibility, keep !important + height: $navigation-element-default-height !important; + width: $navigation-element-default-height !important; + // raise icon over book covers + z-index: 3000; + + // Icon size + &::before, + &::before { + // keep !important + font-size: $slick-next-prev-icon-font-size !important; + } +} + +.slick-next { + // keep !important + right: $slick-next-right-position !important; + + @media (max-width: $screen-sm-max) { + right: $slick-next-right-position-xs-sm !important; + } +} + +.slick-prev { + // keep !important + left: $slick-prev-left-position !important; + + + @media (max-width: $screen-sm-max) { + left: $slick-prev-left-position-xs-sm !important; + } +} + diff --git a/themes/finc/scss/components/_dropdowns.scss b/themes/finc/scss/components/_dropdowns.scss new file mode 100644 index 0000000000000000000000000000000000000000..6d9dee4293650e5512286d34c249090cc7acc9f5 --- /dev/null +++ b/themes/finc/scss/components/_dropdowns.scss @@ -0,0 +1,99 @@ +// Use this for all general dropdown styles +// For AMSL collapse-all-toggler see amsl.scss + +// ***************************************************************** +// ************ Dropdown-menu elements ***************************** +// ***************************************************************** + +// remove padding as it creates space above/below menu items (e.g. language selector) +// remove box shadow +.dropdown-menu { + box-shadow: $dropdown-box-shadow; + padding-bottom: $dropdown-bottom-padding; + padding-top: $dropdown-top-padding; + + + // HOVERABLE DROPDOWNS - make DDs hoverable + // Add the dropdown-on-hover class to the parent li container! + // OR use on all (see commented-out part below) + // Menu will show on hover + .dropdown-on-hover:hover & { + @media (min-width: $screen-sm-min) { + display: block; + } + } + + // OR use on ALL dropdowns like so + //.dropdown:hover .dropdown-menu { + // display: block; + //} +} + + +// Dropdown toggler color +// WARNING: Potential Breaking Change, e.g. bookbag delete button with dropdown +// - could result in white text on white background +// To manage, pls. use variable '$btn-transparent-active-color' in custom themes and define color there +// FIXME: MOVE TO buttons or dropdowns section +.open .btn-transparent.dropdown-toggle { + &[aria-expanded="true"] { + color: $btn-transparent-active-color; + } + + &:active, + &:focus, + &:hover { + color: $btn-transparent-active-color; + } +} + +// The 'delete'-dropdown in favorites list +.open > .dropdown-menu { + .template-dir-myresearch.template-name-mylist .result-links & { + float: none; + position: relative; + } +} + + +// ***************************************************************** +// ************ RVK Notation Dropdown ****************************** +// ***************************************************************** + +// See #18132 and data-rvkNotation.phtml; sample record: Record/0-021939896 or +// https://staging.finc.info/vufind2/de_540/Record/0-1350997935 +.toggle { + color: $dropdown-rvk-toggler-color; + text-decoration: $dropdown-rvk-toggler-text-decoration; + + &:active, + &:focus, + &:hover { + text-decoration: $dropdown-rvk-toggler-text-decoration-active-focus-hover; + } + + &::after { + content: $dropdown-rvk-toggler-icon-content; + font-family: $dropdown-rvk-toggler-icon-font-family; + padding-left: $dropdown-rvk-toggler-icon-padding-left; + } +} + +.notation ul { + padding-inline-start: $dropdown-rvk-list-padding-inline-start; + + @media (max-width: $screen-sm-max) { + padding-inline-start: $dropdown-rvk-list-padding-inline-start-xs-sm; + } + + li { + list-style-type: $dropdown-rvk-list-remove-bullet-points-list-style-type; + } + + li:nth-of-type(1)::before { + content: $dropdown-rvk-list-item-icon-content; + font-family: $dropdown-rvk-list-item-icon-font-family; + margin-right: $dropdown-rvk-list-item-icon-margin-right; + } +} + diff --git a/themes/finc/scss/components/_footer.scss b/themes/finc/scss/components/_footer.scss new file mode 100644 index 0000000000000000000000000000000000000000..206ce384c8f054fbb4b6ffd2ac67baf6251bb108 --- /dev/null +++ b/themes/finc/scss/components/_footer.scss @@ -0,0 +1,28 @@ +// Use this for footer-related styles +// For footer 'background-color', 'border' and 'padding-bottom' see finc.scss +footer { + border-top: $footer-border-top; + padding-top: $footer-padding-top; + + // Left-align footer lists to their headings + ul { + padding-left: $footer-ul-padding-left; + } +} + +.powered-by, +.poweredBy { + font-size: $footer-poweredby-font-size; + padding: $footer-poweredby-padding; + + img { + height: $footer-poweredby-img-height; + margin-left: $footer-poweredby-img-margin-left; + margin-right: $footer-poweredby-img-margin-right; + } + + a:hover { + @include outline(); + } +} + diff --git a/themes/finc/scss/components/_forms.scss b/themes/finc/scss/components/_forms.scss new file mode 100644 index 0000000000000000000000000000000000000000..41ee311236230a75488a483c7fa0f137efbf01cc --- /dev/null +++ b/themes/finc/scss/components/_forms.scss @@ -0,0 +1,328 @@ +// Use this file to style all forms-related elements +// Form elements such as labels that require special styling may be defined in the element-related style sheets + +// ***************************************************************** +// ************ Basic styles *************************************** +// ************ Styles applying to several elements **************** +// ***************************************************************** + +// See also 'select.form-control' below +.form-control { + box-shadow: $form-control-box-shadow; + height: $navigation-element-default-height; + + @media (max-width: $screen-xs-max) { + max-width: $form-control-xs-max-width; + } +} + + +// ***************************************************************** +// ************ Fieldsets ****************************************** +// ***************************************************************** + +fieldset { + border: $fielset-default-border; + + // Advanced Search - make fieldsets fit in a row (see last-of-type below) + .template-name-advanced & { + margin-top: $fieldset-advanced-srch-margin-top; + padding-bottom: $fieldset-advanced-srch-padding-bottom; + padding-left: $fieldset-advanced-srch-padding-left; + padding-right: $fieldset-advanced-srch-padding-right; + + // Advanced Search - Date Range fieldset + &.range { + margin-left: $fieldset-date-range-margin-left; + margin-right: $fieldset-date-range-margin-right; + width: $fieldset-date-range-width; + + @media (max-width: $screen-sm-max) { + margin-left: $fieldset-date-range-xs-sm-margin-left; + width: $fieldset-date-range-xs-sm-width; + } + } + } + + // Advanced search top-most fieldset (sits within '.adv-search' div) + // Keep this entry _below_ the general fieldset settings for adv. search for overwriting + .adv-search & { + border: $fieldset-advanced-srch-search-terms-border; + // keep !important + float: $fieldset-advanced-srch-search-terms-float; + margin-top: $fieldset-advanced-srch-search-terms-margin-top; + padding-bottom: $fieldset-advanced-srch-search-terms-padding-bottom; + padding-left: $fieldset-advanced-srch-search-terms-padding-left; + width: $fieldset-advanced-srch-search-terms-width; + + @media (max-width: $screen-xs-max) { + margin-bottom: $fieldset-advanced-srch-search-terms-xs-margin-bottom; + padding-bottom: $fieldset-advanced-srch-search-terms-xs-padding-bottom; + } + } + + // Edit favorites lists (...MyResearch/Edit) + &.list-edit-group { + border: $fieldset-edit-favorites-list-border; + padding: $fieldset-edit-favorites-list-padding; + + @media (max-width: $screen-xs-max) { + float: $fieldset-edit-favorites-list-xs-float; + } + } + + .form-edit-list & { + border: 0; + float: none; + padding: 0; + } +} + + +// ***************************************************************** +// ************ Legends ******************************************** +// ***************************************************************** + +// E.g.: 'Limit to' in Advanced search, recent acquisitons, edit favs +legend { + border: $legend-border; + font-size: $legend-font-size; + margin-bottom: $legend-margin-bottom; + padding-left: $legend-padding-left; + padding-right: $legend-padding-right; + width: $legend-width; + + // Recent acquisitions search form (search/NewItem) + .form-search-newitem & { + padding-left: $legend-padding-left-recent-acquisitions; + } + + // Edit favorites list + .form-edit-list & { + font-size: $fieldset-edit-favorites-list-legend-font-size; + margin-left: $fieldset-edit-favorites-list-legend-margin-left; + } +} + + +// ***************************************************************** +// ************ Labels ********************************************* +// ***************************************************************** +// For colored labels such as '.text-warning', see icons-and-status-colors.scss + +label { + form & { + font-weight: $label-font-weight; + } + + // remove padding in advanced search labels + #advSearchForm .mainbody & { + padding-left: $label-adv-search-form-padding-left; + padding-right: $label-adv-search-form-padding-right; + } + + // advanced search input and select labels + .adv-input &, + .adv-select & { + display: $label-adv-search-form-display; + } + + // Edit favorites list + .form-edit-list .radio & { + padding-left: $label-edit-favorites-list-padding-left; + } +} + + +// ***************************************************************** +// ************ Inputs ********************************************* +// ***************************************************************** + +input { + // Set max-width to make sure boxes don't bleed over + // the edge on XS (e.g. acquisitionpda, source_id:3) - same + // variable used on textarea and select + @media (max-width: $screen-xs-max) { + max-width: $input-select-textarea-xs-max-width; + } + + &[type='checkbox'], + &[type='radio'] { + margin-top: $input-top-margin; + + // Improve input accessibility + &:focus, + &:hover { + @include outline(2px); + } + } + + &[type='email'], + &[type='text'] { + border: $input-email-text-border; + padding: $input-email-text-padding; + } + + &[type='text'] { + &:focus, + &:hover { + @include outline($outline-default-style, 2px, $input-border-focus); + + // Searchform input and other header input fields + header &, + #searchForm & { + box-shadow: $input-header-search-form-box-shadow; + outline-offset: $input-header-search-form-border-outline-offset; + } + } + + // Searchform input and other header input fields + header &, + #searchForm & { + border: $input-header-search-form-border; + } + } + + // red-bordered input field, when empty, requires "form" for specifity, CK + // create enough space for red border + // show red border only when submitted empty or when in focus + &:invalid { + box-shadow: inherit; + // Same variable used for textarea as well! + margin-right: $input-textarea-invalid-margin-right; + } + + &:focus { + &:invalid, + &:required:invalid { + border-color: $input-invalid-focus-border-color; + box-shadow: $input-invalid-focus-box-shadow; + } + } + + // Edit favorites list + .form-edit-list .radio & { + margin-left: $input-radio-edit-favorites-list-margin-left; + position: $input-radio-edit-favorites-list-position; + } +} + + +// ***************************************************************** +// ************ Selects ******************************************** +// ***************************************************************** +// Select - for 'limit' and 'sort'-select widths see below + +// Remove browser styles on select boxes and add custom styles (below) +select, +select.form-control { + + @include select-element-custom-look; + + header & { + border: $select-in-header-border; + } + + &:focus, + &:hover { + @include outline($outline-default-style, 1px, $input-border-focus); + } +} + +select { + // Set max-width to make sure boxes don't bleed over + // the edge on XS (e.g. acquisitionpda, source_id:3) - same + // variable used on textarea and input + @media (max-width: $screen-xs-max) { + max-width: $input-select-textarea-xs-max-width; + } + + .searchForm & { + @media (min-width: $screen-sm-min) { + // prevent adv search button from breaking onto new line + max-width: $select-search-form-sm-max-width; + } + } + + // Remove down-arrow icons in 'limit to' (language, format) select boxes in adv. search + .limiter-boxes &, + // Remove down-arrow icons in 'new items'/'Neuerwerbungen select box + .form-search-newitem & { + background-image: $select-remove-standard-down-arrow-background-image; + height: $select-remove-standard-down-arrow-height; + } +} + +// ***************************************************************** +// ************ Checkboxes ***************************************** +// ***************************************************************** + + +// ***************************************************************** +// ************ Radio buttons ************************************** +// ***************************************************************** + +// Edit favorites list +.form-edit-list { + // container for radio buttons + .radio { + &.inline { + display: block; + } + } +} + + +// ***************************************************************** +// ************ Textarea ******************************************* +// ***************************************************************** + +textarea { + // Set max-width to make sure boxes don't bleed over + // the edge on XS (e.g. acquisitionpda, source_id:3) - same + // variable used on input and select + @media (max-width: $screen-xs-max) { + max-width: $input-select-textarea-xs-max-width; + } + + &:invalid { + // Same variable used for input as well! + margin-right: $input-textarea-invalid-margin-right; + } +} + + +// ***************************************************************** +// ************ Validator ****************************************** +// ***************************************************************** + +.has-error .control-label, +.has-error .help-block, +.sms-error .control-label, +.sms-error .help-block { + color: $validator-color; +} + + +// Required sign in forms +.required { + color: $brand-danger; +} + +// ***************************************************************** +// ************ Zend forms ***************************************** +// ***************************************************************** +// Fixme: Find examples and check, if default 'required' behaviour wouldn't be sufficient - CK +#dds-form { + + label[data-required='true'][for]::after { + content: ' *'; + } + + .form-group { + li { + color: $brand-danger; + } + } +} + diff --git a/themes/finc/scss/components/_functions.scss b/themes/finc/scss/components/_functions.scss new file mode 100644 index 0000000000000000000000000000000000000000..dfec5c0c1b5caaa0fad7836ac6cf322895e0d98c --- /dev/null +++ b/themes/finc/scss/components/_functions.scss @@ -0,0 +1,17 @@ +// Use this for functions such as em calculator etc. + +// em Calculator +//// How to: When calculator is active, just write the desired px +//// into the function call, like so: font-size: em(12) or em(12px) +@function em($pixels, $context: $font-size-base) { + @if (unitless($pixels)) { + $pixels: $pixels * 1px; + } + + @if (unitless($context)) { + $context: $context * 1px; + } + + @return $pixels / $context * 1em; +} + diff --git a/themes/finc/scss/components/_header-active-filters.scss b/themes/finc/scss/components/_header-active-filters.scss new file mode 100644 index 0000000000000000000000000000000000000000..11c7638ce982b7dda425c37c98c4063cf5e0627c --- /dev/null +++ b/themes/finc/scss/components/_header-active-filters.scss @@ -0,0 +1,146 @@ +// Use this file to style all active filters elements in header +// The structure is like so: +// <!-- Outer container --> +// <div class="active-filters"> +// <!-- Reset all button --> +// <a class="reset-filters-btn" .... +// <!-- Outer container for all individual filters --> +// <div class="filters"> +// <div class="title-value-pair"> +// <!-- Filtergroup title --> +// <span class="filters-title"> +// <!-- Individual filters' outer container --> +// <span class="filter-value filters-or"> +// <!-- Individual filter --> +// <a class="search-filter-remove" ... + +.active-filters { + // Outer container for all filters and reset button + // overwrite BS values where necessary + display: $active-filters-outer-container-display; + + + // Outer container for all individual filters + .filters { + padding-left: $active-filters-outer-filters-container-padding-left; + } + + // Individual filters' outer container + // Make this the same height as .reset-filters-btn and other navigation elements + // for better usability - keep '.filters' for specifity (overwriting BS3) + .filters .filter-value { + height: $navigation-element-default-height; + margin-bottom: $search-filters-margin-bottom; + padding: $search-filters-padding; + + &:focus, + &:hover { + background: $search-filter-remove-hover-bg; + color: $search-filter-remove-hover-color; + } + } +} + + +// This is the 'reset all'-button +.reset-filters-btn { + background-color: $search-filter-remove-all-bg; + color: $search-filter-remove-all-color; + + &:focus, + &:hover { + background: $sidebar-item-active-hover-bg; + color: $sidebar-facet-active-hover-color; + } +} + + +// Set focus and hover styles for individual filter items' +// outer container ('.filters .filter-value') and filter items ('.search-filter-remove') +.filters .filter-value, +.search-filter-remove { + color: $search-filter-values-remove-color; + text-decoration: $search-filter-remove-text-decoration; + + &:focus, + &:hover, + &:visited:focus, + &:visited:hover { + // Keep '.active-filters' for specifity to overwrite BS + .active-filters & { + background: $search-filter-remove-hover-bg; + color: $search-filter-remove-hover-color; + text-decoration: inherit; + + } + } + + + // Make this same color as un-visited item + &:visited { + // Keep '.active-filters' for specifity to overwrite BS + .active-filters & { + color: inherit; + text-decoration: inherit; + } + } +} + +// Set color of individual filters when focus is on outer container +.search-filter-remove, +.search-filter-remove:visited { + + .filters .filter-value:focus &, + .filters .filter-value:hover & { + background: $search-filter-remove-hover-bg; + color: $search-filter-remove-hover-color; + + + // x-icon color when outer container is focussed + &::after { + color: $search-filter-remove-icon-hover-color; + } + } +} + +// This is the colored, button-like element with the 'x' icon +.search-filter-remove { + // Align with text inside the element + vertical-align: $search-filter-remove-button-vertical-align; + + .text { + // Create ellipsis for overlong filter text + display: $search-filter-remove-button-text-display; + max-width: $search-filter-remove-button-text-max-width; + overflow: $search-filter-remove-button-text-overflow; + text-overflow: $search-filter-remove-button-text-text-overflow; + white-space: $search-filter-remove-button-text-white-space; + } + + + // Set icon content + &::after { + content: $search-filter-remove-icon; + vertical-align: $search-filter-remove-icon-vertical-align; + + // Move 'x' to the right of the buttons in dropdown + .search-filter-dropdown & { + position: $search-filter-remove-icon-in-dropdown-position; + right: $search-filter-remove-icon-in-dropdown-distance-from-right; + } + } +} + + +// Style for dropdowns (activated when more than n item(s) are set in filters.phtml): +// Unset the vertical alignment from BS to align dropdowns AND indiv. buttons +// The relevant definition is '.searchForm .btn {vertical-align: top;}' +.search-filter-dropdown { + + button[id^='dropdown-toggle-'] { + // set 'float' to 'none' to make vertical align work + float: $search-filter-dropdown-button-float; + vertical-align: $search-filter-dropdown-button-vertical-align; + } +} + diff --git a/themes/finc/scss/components/_header-navbar.scss b/themes/finc/scss/components/_header-navbar.scss new file mode 100644 index 0000000000000000000000000000000000000000..2cffc9ceda3d679eca5326d651e3047f62ee739e --- /dev/null +++ b/themes/finc/scss/components/_header-navbar.scss @@ -0,0 +1,331 @@ +// Use this file to style all your header and header navbar elements + +// HEADER and NAVBAR FIXED have the same width as rest of the page +// (the header can also run the full width of the viewport!) + +// Set header and navbar background color +header, +.navbar { + background-color: $header-bg-color; +} + +// Set header, navbar and breadcrumbs widths + placement +header, +.navbar, +.breadcrumbs { + // For a full-width header change '$header-navbar-breadcrumbs-margin' in customVariables + // and all the media queries below; + // if you're not using a fixed navbar, also change the settings for '.navbar' above + margin: $header-navbar-breadcrumbs-margin; + + // set width to width of BS container + @media (min-width: $screen-sm-min) { + width: $container-sm; + } + + @media (min-width: $screen-md-min) { + width: $container-md; + } + + @media (min-width: $screen-lg-min) { + width: $container-lg; + } +} + +// Define surrounding container for navbar as a flexible container +// '.banner' also contains the logo and the search container +// See also below: .'navbar-header ...' +.banner { + // important for subsequent flex elements, change variable for non-flex display + display: $container-for-navbar-display-as; + + // stack elements on top of each other on XS, change variable for non-flex display + @media (max-width: $screen-xs-max) { + flex-direction: $container-for-navbar-flex-direction-xs; + } +} + +// Define fixed Navbar features (Header): When using a fixed header + autocomplete, +// increase the z-index in autocomplete-results to a value higher than that of the fixed navbar (1030)! +.navbar-fixed { + background-color: $navbar-fixed-bg-color; +} + +// Keep navbar on left, when off-canvas is active, and give it the same appearance as ever +// the '.active' class is set on body when the user hits the button to display the sidebar +.navbar-fixed-top { + .offcanvas.active & { + margin-top: -$navbar-height-xs; + position: relative; + } +} + +// Navbar min-height is defined in BS (use customVariables to adjust); +// however, there is no definition for small devices, so we introduce it here +.navbar { + background-color: $navbar-bg-color; + // Navbar gets a minimum-height of 50px by default, + // activate and change '$navbar-height' in customVariables.scss; + // set height, instead of min-height, or navbar may spill over breadcrumbs when + // body -> padding-top would need to be taller than navbar minimum-height + min-height: $navbar-height; + + // Advanced search + .template-name-advanced & { + min-height: $navbar-min-height-adv-search; + } + + // Set min height for xs + @media (max-width: $screen-xs-max) { + min-height: $navbar-height-xs; + } + + // Set min height for navbar on small, allow for a stacked search box + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + min-height: $navbar-height-sm; + } + + // Style menu section with book bag, my account, language selector for XS + .navbar-nav { + @media (max-width: $screen-xs-max) { + // avoid BG bleed on small + margin-bottom: 0; + } + + @media (min-width: $screen-sm-min) { + margin-right: -$navbar-padding-horizontal; + } + } +} + +// ***************************************************************** +// ************ Library Name and Hamburger menu contents *********** +// ***************************************************************** + +.navbar-header { + flex: $hamburger-menu-flex; + + // ensure proper margins on XS in conjunction with 'flex-direction: column' on '.banner', see above + @media (max-width: $screen-xs-max) { + .container > & { + margin-left: $hamburger-menu-margin-left-xs; + margin-right: $hamburger-menu-margin-right-xs; + } + } +} + +// Library name - we give it the same padding as a button, so if fits in with the header +.navbar-brand { + float: $library-name-float; + padding: $library-name-padding; + + @media (max-width: $screen-xs-max) { + height: $library-name-height-xs; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + padding-right: $library-name-padding-right-sm; + } + + &:focus, + &:hover { + @include outline(); + outline-offset: $library-name-focus-hover-outline-offset; + } + + img { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + height: $library-name-img-height-sm; + max-width: $library-name-img-height-max-width-sm; + } + } +} + +// Hamburger menu toggler +.navbar-toggle { + margin-bottom: $hamburger-menu-margin-bottom; + margin-right: $hamburger-menu-margin-right; + margin-top: $hamburger-menu-margin-top; + + &:focus, + &:hover { + background-color: $navbar-default-toggle-hover-bg; + } + + // toggler icon size + .fa { + font-size: $hamburger-menu-icon-font-size; + } +} + + +// ***************************************************************** +// ************ Menu items on the right + Opened menu on XS ******** +// ***************************************************************** + +// Make right-hand header parts fit with search box on SM devices - see also .search.container +#header-collapse { + + @media (min-width: $screen-sm-min) { + // Make smaller to fit with search box and right-hand nav-elements + flex: $header-menu-flex-sm-up; + // position to the right! + order: $header-menu-flex-order-sm-up; + padding-top: $header-menu-padding-top-sm-up; + + // Make right-hand header parts full width for advanced search page since it doesn't use any left-hand header parts + .template-name-advanced & { + flex: $header-menu-flex-adv-srch-sm-up; + } + } +} + +// Define look in opened state +.navbar-collapse.collapse.in { + //// Define header navbar on mobile + &.collapse.in { + @media (max-width: $screen-xs-max) { + background-color: $navbar-bg-color-xs; + margin-bottom: $navbar-xs-openend-margin-bottom; + } + + @media (max-width: $screen-sm-max) { + // margin-top: -($navbar-height-xs) - 10px; // pull over searchbox + // position: $navbar-opened-on-xs-position; + // Use the following formula to allow for separately calculated $navbar-height in themes + // top: calc(#{$navbar-height-xs} * 1.5); + + a.btn { + text-align: left; + } + } + } +} + + + +// ***************************************************************** +// ************ Navbar button sizes ******************************** +// ***************************************************************** + +// applies to anchors with '.btn' class only (such as 'my account') (<a class="btn" ...>) +// - this is to make them look the same as the 'find' button +.nav > li > a.btn { + @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); + + // make buttons fit on SM + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + // 6px 2px + padding: $btn-header-nav-padding-sm; + } + + &:focus, + &:hover { + // Same hover-color must be applied to 'Advanced Search' button + background-color: $btn-header-nav-focus-hover-background-color; + color: $btn-header-nav-focus-hover-color; + outline: $btn-header-nav-focus-hover-outline; + } +} + + + +// ***************************************************************** +// ************ Bookbag/Cart/Clip board **************************** +// ***************************************************************** + +// Remove cart text to make search box fit on intermediate tablets +.cart-label { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + display: none; + } +} + + +// ***************************************************************** +// ************ Language selector ********************************** +// ***************************************************************** + +// Outer language menu container +.language { + &.dropdown { + margin-right: $language-selector-dropdown-margin-right; + } + + // Inner language menu container + .dropdown-menu { + background-color: $language-selector-dropdown-menu-background-color; + border: $language-selector-dropdown-menu-border; + min-width: $language-selector-dropdown-menu-min-width; + + // Language menu buttons + .btn:hover, + .btn:focus { + background-color: $btn-language-hover-bg; + color: $btn-language-hover-color; + } + + .active .btn:hover { + background-color: $btn-language-active-hover-bg; + color: $btn-language-hover-color; + } + + // Use this when you have language anchors with '.btn' classes (<a class="btn" ...>) + // see also '.oneLanguage .btn' below + a { + color: $language-selector-dropdown-menu-link-color; + + &.btn { + @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); + + padding-left: $language-selector-dropdown-menu-btn-padding-left; + padding-right: $language-selector-dropdown-menu-btn-padding-right; + + &:focus, + &:hover { + background-color: $language-selector-dropdown-menu-btn-focus-hover-background-color; + } + } + } + } +} + +// Language select when there is only one language +// keep '.dropdown-menu' for specifity +.oneLanguage.dropdown-menu { + display: $language-selector-one-language-only-display; + margin-top: $language-selector-one-language-only-margin-top; + position: $language-selector-one-language-only-position; + + .btn { + background: $language-selector-one-language-only-btn-background; + color: $language-selector-one-language-only-btn-color; + + @media (max-width: $screen-xs-max) { + background-color: $language-selector-one-language-only-btn-background-color-xs; + // Align with other entries + padding-left: $language-selector-one-language-only-btn-padding-left-xs; + text-align: $language-selector-one-language-only-btn-text-align-xs; + // Pull to full width on XS + width: $language-selector-one-language-only-btn-width-xs; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + float: $language-selector-one-language-only-btn-float-sm; + padding: $language-selector-one-language-only-btn-padding-sm; + } + + @media (min-width: $screen-md-min) and (max-width: $screen-md-max) { + float: $language-selector-one-language-only-btn-float-md; + padding: $language-selector-one-language-only-btn-padding-md; + } + } + + // Capitalize language short codes such as 'en' + .visible-sm-only, + .visible-sm-md-only { + text-transform: capitalize; + } +} + + diff --git a/themes/finc/scss/components/_headings.scss b/themes/finc/scss/components/_headings.scss new file mode 100644 index 0000000000000000000000000000000000000000..1ce00c00eec116930c90f871b8a81f3968f81eab --- /dev/null +++ b/themes/finc/scss/components/_headings.scss @@ -0,0 +1,91 @@ +// Use this file for all h1 to h6 elements only + +// ***************************************************************** +// ************ h1 ************************************************* +// ***************************************************************** +h1 { + font-size: $h1-font-size; + + //// remove top margin to align with sidebars etc., except advanced search on XS + &:first-of-type { + margin-top: $h1-first-of-type-margin-top; + } + + .template-dir-cart.template-name-cart .container &, + .template-dir-content.template-name-content &, + .template-dir-myresearch.template-name-editlist &, + .template-dir-search.template-name-newitem &, + .template-dir-search.template-name-reserves & { + @media (max-width: $screen-xs-max) { + padding-top: ($grid-gutter-width / 2); + } + } +} + + +// ***************************************************************** +// ************ h2 ************************************************* +// ***************************************************************** +h2 { + font-size: $h2-font-size; + + .sidebar & { + font-size: $h2-font-size-sidebar; + + @media (max-width: $screen-xs-max) { + margin-left: $h2-margin-left-sidebar-xs; + } + + @media (max-width: $screen-sm-max) { + font-size: $h2-font-size-sidebar-xs-sm; + margin-top: $h2-margin-left-sidebar-xs-sm-margin-top; + } + + @media (min-width: $screen-sm-min) { + &:first-of-type { + margin-top: $h2-first-of-type-margin-top-sidebar-sm-up; + } + } + } +} + + +// ***************************************************************** +// ************ h3 ************************************************* +// ***************************************************************** +h3 { + font-size: 1.5rem; + + // H3 for favorites lists in MyAccount; + // keep '#myresearch-sidebar '& for specifity + &.lists-heading { + #myresearch-sidebar & { + // Align with content in facet elements + margin-bottom: .25rem; + margin-left: 1.5rem; + } + } +} + + +// ***************************************************************** +// ************ h4 ************************************************* +// ***************************************************************** +h4 { + font-size: $h4-font-size; + + // H4 atop Sidebar - align to limit and sort + .sidebar & { + margin-top: $h4-margin-top-sidebar; + + @media (max-width: $screen-sm-max) { + text-align: $h4-text-align-sidebar-xs-sm; + } + + // keep level with left-hand content + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + font-size: $h4-font-size-sidebar-sm-only; + margin-top: $h4-margin-top-sidebar-sm-only; + } + } +} diff --git a/themes/finc/scss/components/_hierarchy-tree.scss b/themes/finc/scss/components/_hierarchy-tree.scss new file mode 100644 index 0000000000000000000000000000000000000000..0830d5587083e21758b56be73fb98085349ec73f --- /dev/null +++ b/themes/finc/scss/components/_hierarchy-tree.scss @@ -0,0 +1,129 @@ +// Use this for all hierarchy-tree element styles (record view) + +// ***************************************************************** +// ************ Tree-Selector (more than 1 tree) ******************* +// ***************************************************************** + +// '#treeSelector' is the ID of a container that will ONLY be rendered in record view +// when there is _more than one_ tree, e.g. search for 'Allgemeine Staatengeschichte' +// and/or check 'Record/0-1118241363' +#treeSelector { + border-bottom: $jstree-tree-selector-border-bottom; + margin-bottom: $jstree-tree-selector-margin-bottom; + padding-bottom: $jstree-tree-selector-padding-bottom; + + // this is to style the individual items in a tree selector + .item { + margin-right: $jstree-tree-selector-item-margin-right; + + &:last-of-type { + margin-right: $jstree-tree-selector-item-last-of-type-margin-right; + } + } +} + +// ***************************************************************** +// ************ Tree as such *************************************** +// ***************************************************************** + +// 'hierarchyTreeHolder' is the container that holds each individual tree +#hierarchyTreeHolder { + border-right: $jstree-tree-holder-border-right; +} + +// 'hierarchyTree' is the direct child of 'hierarchyTreeHolder' +#hierarchyTree { + // 'jstree-container-ul' wraps the top-most tree list element, like so + // <div id="hierarchyTree" ... + // <ul class="jstree-container-ul" ... + // <li class="jstree-node jstree-open jstree-last" ... + // <i class="jstree-icon jstree-ocl" ... + // <a class"jstree-anchor" ... + // <ul class="jstree-children" ... + // <li class="jstree-node jstree-leaf" ... + + // keep 'hierarchyTree' parent for specifity + .jstree-container-ul { + padding-left: $jstree-container-ul-padding-left; + } + + // This styles the top-most open/close icon; + // in finc, we make dropdown-icon wider for usability. + // Keep parent classes for specifity + .jstree-closed > .jstree-ocl, + .jstree-open > .jstree-ocl { + &::before { + float: $jstree-topmost-icon-float; + font-size: $jstree-topmost-icon-font-size; + margin-left: $jstree-topmost-icon-margin-left; + margin-top: $jstree-topmost-icon-margin-top; + min-width: $jstree-topmost-icon-min-width; + padding: $jstree-topmost-icon-padding; + width: $jstree-topmost-icon-width; + } + + &:hover::before, + &:focus::before { + color: $jstree-topmost-icon-hover-before-color; + } + } + + // This styles the actual top-most open/close icon; + // in most cases, this may only be visible when the context tree is opened in a modal + .jstree-open > .jstree-ocl::before { + content: $jstree-topmost-icon-open-content; + } + + .jstree-closed > .jstree-ocl::before { + content: $jstree-topmost-icon-closed-content; + } + + + li.jstree-facet, + li.jstree-node { + line-height: $jstree-facet-node-line-height; + list-style: $jstree-facet-node-list-style; + padding-left: $jstree-facet-node-padding-left; + } + + // Align child elements under open/close toggle + .jstree-children { + margin-top: $jstree-children-ul-margin-top; + padding-left: $jstree-children-ul-padding-left; + } + + // Children's icon + // Jstree-leaf items' icon + li li::before { + // FontAwesome Unicode fa-file-o + content: $jstree-children-li-before-icon-content; + display: $jstree-children-li-before-icon-display; + font-family: $jstree-children-li-before-icon-font-family; + // same as padding-left set on li, above + margin-left: -$jstree-children-li-left-padding; + //same as padding-left set on li, above + width: $jstree-children-li-left-padding; + } + + // '.jstree-anchor' is used on ALL anchors inside the tree -- i.e. top-most and child elements + // prevent tree LIs from overspilling on small! + .jstree-anchor { + hyphens: $jstree-anchor-hyphens; + padding-left: $jstree-anchor-padding-left; + white-space: $jstree-anchor-white-space; + } + + // Current item + .jstree-clicked { + color: $jstree-clicked-color; + padding-bottom: $jstree-clicked-padding-bottom; + padding-right: $jstree-clicked-padding-right; + padding-top: $jstree-clicked-padding-top; + + &:hover, + &:focus { + color: $jstree-clicked-hover-color; + } + } +} + diff --git a/themes/finc/scss/components/_home-page.scss b/themes/finc/scss/components/_home-page.scss new file mode 100644 index 0000000000000000000000000000000000000000..07f8f6878db435355ebeed01aa71f50fc454aa12 --- /dev/null +++ b/themes/finc/scss/components/_home-page.scss @@ -0,0 +1,45 @@ +// Use this for all home page styles + +// Add top margin for better looks +.searchHomeContent { + margin-top: $home-page-margin-top; + + // Wells are containers that wrap content such as Wikipedia entries + .well { + @media (max-width: $screen-xs-max) { + background-color: $home-page-well-xs-background-color; + border: $home-page-well-xs-border; + box-shadow: $home-page-well-xs-box-shadow; + min-height: $home-page-well-xs-min-height; + padding: $home-page-well-xs-padding; + } + } +} + + + + +// ***************************************************************** +// ************ Browsing section *********************************** +// ***************************************************************** +.browse-container { + padding-top: $browse-container-padding-top; + + @media (max-width: $screen-xs-max) { + margin-top: $browse-container-xs-margin-top; + } +} + +// Browsing suggestion lists on home page (prevent torn-apart look when 2 columns only) +.browsesuggest.columns + .columns:last-child { + float: $home-page-browse-suggest-2-columns-only-last-child-float; +} + +.browse-item.active { + &:focus, + &:hover { + background-color: $home-page-browse-item-active-focus-hover-background-color; + text-decoration: $home-page-browse-item-active-focus-hover-text-decoration; + } +} + diff --git a/themes/finc/scss/components/_hotfixes.scss b/themes/finc/scss/components/_hotfixes.scss new file mode 100644 index 0000000000000000000000000000000000000000..8fd5ee2e03b4c67d520fd5311f80bdcd2f334191 --- /dev/null +++ b/themes/finc/scss/components/_hotfixes.scss @@ -0,0 +1 @@ +// Use for hotfixes only and remove them again asap diff --git a/themes/finc/scss/components/_media-styled-based-account-icons-and-status-colors.scss b/themes/finc/scss/components/_media-styled-based-account-icons-and-status-colors.scss new file mode 100644 index 0000000000000000000000000000000000000000..b0bf49f46569431616afbddc02e0661e5a85d23e --- /dev/null +++ b/themes/finc/scss/components/_media-styled-based-account-icons-and-status-colors.scss @@ -0,0 +1,253 @@ +// Use this for: +// style-based icons, +// media type-sprites, +// status indicators, +// my account-icons and +// coloured labels styles + + +// ***************************************************************** +// ************ Style-based Icons ********************************** +// ***************************************************************** + +.record, +.result { + .media-left, + .media-right { + &.record-icon img { + display: inline-block; + max-width: none; + width: $thumbnail-width-small; + } + } + + // push COVER down on SM and XS + .media-left { + @media (max-width: $screen-sm-max) { + margin-top: 15px; + } + } + + // cover + .ajaxcover .spinner { + height: 0; + position: absolute; + } + + .cover-container { + min-width: 6em; + } + // cover - END +} + + +// ***************************************************************** +// ************ Sprites for Media Icons **************************** +// ***************************************************************** + +.sprite-media-icon { + background: url('../../../finc/images/sprite-mediaicons.png') 0 0 repeat; + float: left; + height: 54px; + width: 50px; +} + +////// 1st row of sprite +.book { + background-position: 0 0; +} + +.thesis { + background-position: -50px 0; +} + +.ebook { + background-position: -100px 0; +} + +.sets { + background-position: -150px 0; +} + +.electronic { + background-position: -250px 0; +} + +////// 2nd row of sprite +.journal { + background-position: 0 -55px; +} + +.electronicjournal { + background-position: -50px -55px; +} + +.newspaper { + background-position: -100px -55px; +} + +.manuscript { + background-position: -150px -55px; +} + +.braille { + background-position: -200px -55px; +} + +.microfilm { + background-position: -250px -55px; +} + +////// 3rd row of sprite +.software { + background-position: 0 -110px; +} + +.software-set { + background-position: -100px -110px; +} + +.kit { + background-position: -100px -110px; +} + +.image { + background-position: -150px -110px; +} + +.slide { + background-position: -200px -110px; +} + +.globe { + background-position: -250px -110px; +} + +////// 4th row of sprite +.film { + background-position: 0 -165px; +} + +.dvd { + background-position: -50px -165px; +} + +.video { + background-position: -100px -165px; +} + +.video-set { + background-position: -100px -165px; +} + +.map { + background-position: -200px -165px; +} + +.unknown { + background-position: -250px -165px; +} + +////// 5th row of sprite +.audio { + background-position: 0 -220px; +} + +.cd { + background-position: -50px -220px; +} + +.audiotape { + background-position: -100px -220px; +} + +.musicalscore { + background-position: -150px -220px; +} + +.chart { + background-position: -200px -220px; +} + +.physicalobject { + background-position: -250px -220px; +} + +////// 6th row of sprite +.audio-set { + background-position: 0 -275px; +} + + + + +// ***************************************************************** +// ************ Status-indicating colors *************************** +// ***************************************************************** +// For icons on status-colored BG see below +.text-danger, +.text-success { + padding: .5em; +} + +.text-success { + background-color: $state-success-bg; + margin-bottom: 0; +} + +.text-danger { + background-color: $state-danger-bg; +} + + + + +// ***************************************************************** +// ************ Icons & numbers in MyAccount facets & Login link *** +// ***************************************************************** + +// Warning signs in MyAccount sidebar and warning sign in front of Login/MyAccount in header: +// bell, clock, exclamation triangle +.fa-bell, +.fa-clock-o, +.fa-exclamation-triangle { + + &.text-danger, + &.text-success, + &.text-warning { + // make BG of all icons transparent and render icon itself + // in respective colors (see individual icons below) + background: $sidebar-my-account-danger-success-warning-background; + padding: $sidebar-my-account-danger-success-warning-padding; + + // icon in header navbar on Hover + .btn:focus &, + .btn:hover & { + color: inherit; + } + } +} + + + +// Warning signs in MyAccount sidebar +.fa-bell, +.fa-clock-o { + &.text-success { + color: $icon-with-text-success-color; + } + + &.text-warning { + color: $icon-with-text-warning-color; + } +} + +// Warning sign in front of Login/MyAccount in header +.fa-exclamation-triangle { + &.text-danger { + color: $icon-with-text-warning-color; + // keep padding at 0 or alignment in header nav will be messed up + padding: $icon-exclamation-triangle-in-header-padding; + } +} + diff --git a/themes/finc/scss/components/_modal.scss b/themes/finc/scss/components/_modal.scss new file mode 100644 index 0000000000000000000000000000000000000000..ffaccb591718a296a5e609a2729ae6a9c3581ea4 --- /dev/null +++ b/themes/finc/scss/components/_modal.scss @@ -0,0 +1,73 @@ +// Use this for modal styles + +.modal-dialog { + // Set modal width to make better use of the screen + @media (max-width: $screen-xs-max) { + // Reduce size of modal to keep close-button visible + max-width: $modal-dialog-max-width-xs; + } + + @media (min-width: $screen-sm-min) { + max-width: $modal-dialog-max-width-sm-up; + min-width: $modal-dialog-min-width-sm-up; + width: $modal-dialog-width-sm-up; + + // Login form and others: + // keep !important or rewrite using '#modal ...', CK + input.form-control { + max-width: $modal-dialog-input-max-width-sm-up !important; + } + } + + @media (min-width: $screen-md-min) { + // Automatically set modal's width for larger viewports + width: $modal-dialog-width-md-up; + } + + @media (min-width: $screen-lg-min) { + max-width: $modal-dialog-max-width-lg-up; + } + + .mainbody.left { + // Prevent transparent lightbox bug - keep !important to override! + float: $modal-dialog-mainbody-left-float !important; + } +} + +// fix modal adding padding-right #13625 +.modal-open { + overflow: $modal-dialog-open-overflow; +} + +// Modal close button (keep parent classes for specifity) +#modal .modal-content > .close, +#modal .modal-content > .adv-term-remove { + background: $modal-close-bg; + border: $modal-dialog-close-button-border; + border-radius: $modal-close-br; + color: $modal-close-color; + left: $modal-dialog-close-button-left; + opacity: $modal-dialog-close-button-opacity; + right: $modal-dialog-close-button-right; + + @media (min-width: $screen-sm-min) { + right: $modal-dialog-close-button-right-sm-up; + + body.rtl & { + left: $modal-dialog-close-button-left-sm-up-rtl; + } + } + + &:focus, + &:hover { + background-color: $modal-close-bg-hover; + color: $modal-close-color-hover; + opacity: $modal-dialog-close-button-focus-hover-opacity; + } +} + +// #21993 +.modal td[data-title="openURL:"], +.modal .table-resp-data td { + word-break: break-word; +} diff --git a/themes/finc/scss/components/_offcanvas.scss b/themes/finc/scss/components/_offcanvas.scss new file mode 100644 index 0000000000000000000000000000000000000000..f7c083d9fa74697094c633595c5c652aae9dfc41 --- /dev/null +++ b/themes/finc/scss/components/_offcanvas.scss @@ -0,0 +1,121 @@ +// Use this for all off-canvas-related styles + + +// ***************************************************************** +// ************ Off-canvas Icons *********************************** +// ***************************************************************** +// For variables, see customVariables.scss + +.offcanvas-left { + // Back-button in opened off-canvas sidebar + .close-offcanvas::after { + content: $off-canvas-left-close-icon-content; + } + + // Off-canvas toggler button in normal pages + .search-filter-toggle::before { + content: $off-canvas-left-open-icon-content; + } +} + +.offcanvas-right { + // Back-button in opened off-canvas sidebar + .close-offcanvas::before { + content: $off-canvas-right-close-icon-content; + } + + .search-filter-toggle::after { + content: $off-canvas-right-open-icon-content; + } +} + + + + +// ***************************************************************** +// ************ Off-canvas in Sidebar ****************************** +// ***************************************************************** + +// Off-Canvas button in sidebar (record view) +.close-offcanvas { + // make same as toolbar padding, below + margin-right: $record-view-toolbar-button-padding; + text-align: left; + width: 100%; + + @media (max-width: $screen-xs-max) { + display: block; + } + + // Make outline look identical on all sides + &:focus { + outline-offset: 0; + } +} + + +.offcanvas-left { + .record { + .media-left.img-col { + // push record view icon to right (Desktop) on left-aligned sidebars - pls NOTE: required offcanvas = true + @media (min-width: $screen-sm-min) { + float: $off-canvas-left-media-icon-on-right-sm-up-float; + margin-left: $off-canvas-left-media-icon-on-right-sm-up-margin-left; + margin-right: $off-canvas-left-media-icon-on-right-sm-up-margin-right; + padding-right: $off-canvas-left-media-icon-on-right-sm-up-padding-right; + } + } + } +} + + +// make 100% wide to avoid conflict with other elements, for back-toggler see below +.offcanvas-toggler, +.offcanvas-toggler button { + width: 100%; +} + +.offcanvas-toggler { + @media print { + display: none; + } +} + + + +// ***************************************************************** +// ************ Sidebar with offcanvas active ********************** +// ***************************************************************** + +// keep '.container' for specifity +.offcanvas.active .container .sidebar { + &.left { + // make room for button left-side borders + padding-right: $off-canvas-active-sidebar-padding; + } + + &.right { + // make room for button right-side borders + padding-left: $off-canvas-active-sidebar-padding; + } +} + + + +// ***************************************************************** +// ************ Tabs View in Result List *************************** +// ***************************************************************** + +// Result list with 'tabs view' on +// When item is clicked in 'Tabs View', the item is displayed +// as a tab - but erroneously an offcanvas-button appears on XS +// To test this, turn on +// [List] +// view=tabs +// in searches.ini + +.template-name-results .tab-content .offcanvas-toggler { + display: none; +} + + diff --git a/themes/finc/scss/components/_pagination.scss b/themes/finc/scss/components/_pagination.scss new file mode 100644 index 0000000000000000000000000000000000000000..db46743ca7c050ad55596e8c36a597c736051836 --- /dev/null +++ b/themes/finc/scss/components/_pagination.scss @@ -0,0 +1,46 @@ +// Use this for all pagination/pager element styles (result list AND record view) + +// ***************************************************************** +// ************ Pagination for result list ************************* +// ***************************************************************** + +// Pagination & Searchtools get the same centered display +.pagination { + @include content-centered-display-as-table; + + @media print { + display: none; + } + +// Fix pagination on XS to display fewer items and remove first and last + @media (max-width: $screen-xs-max) { + li { + // hide last and first page buttons + &.first, + &.last, + // select 7th to 11th child + &:nth-child(n+10):nth-child(-n+13) { + display: none; + } + } + } +} + + +// ***************************************************************** +// ************ Pagination/pager in record/details view ************ +// ***************************************************************** +// The pager occurs in record/view but also in collection/view +.pager { + margin-bottom: $pager-margin-bottom; + + // make enough room, so pager doesn't get pulled under the header + @media (max-width: $screen-xs-max) { + margin-top: $pager-margin-top-xs; + } + + .disabled { + display: none; + } +} + diff --git a/themes/finc/scss/components/_record-tabs.scss b/themes/finc/scss/components/_record-tabs.scss new file mode 100644 index 0000000000000000000000000000000000000000..2daa26546bab6b48d84714b1f487c007227861ef --- /dev/null +++ b/themes/finc/scss/components/_record-tabs.scss @@ -0,0 +1,69 @@ +// Use this for all record tabs element styles (record view) + +// ***************************************************************** +// ************ Record tabs general ******************************** +// ***************************************************************** + +// Add padding-left to tabs area +.record-tabs { + padding-left: $record-tabs-padding-left; + + @media (max-width: $screen-xs-max) { + padding-left: $record-tabs-padding-left-xs; + } +} + +// Add left and right borders +.tab-content { + border-bottom: 1px solid $nav-tabs-border-color; + border-left: 1px solid $nav-tabs-border-color; + border-right: 1px solid $nav-tabs-border-color; + padding: $record-tabs-tab-content-padding; + + // make long ai-Links break to prevent overspilling content on XS + a { + word-break: $record-tabs-tab-content-a-word-break; + } + + // Tab-content active needs to display as 'inline-block' + // to show the border correctly (or content will bleed over) + > .active { + display: $record-tabs-tab-content-a-active-display; + // make tables inside tabs use full width + width: $record-tabs-tab-content-a-active-width; + } +} + +// ***************************************************************** +// ************ Holdings/access tab ******************************** +// ***************************************************************** + +.holdings-tab { + // Collapsible element - branch info, opening hours etc + .collapse.in { + padding-left: $record-tabs-holdings-collapse-padding-left; + } + + // increase 'line height' for links to improve readability + &.tab-pane { + ul { + margin-top: $record-tabs-holdings-tab-pane-ul-margin-top; + padding-left: $record-tabs-holdings-tab-pane-ul-padding-left; + } + + li { + margin-bottom: $record-tabs-holdings-tab-pane-li-margin-bottom; + } + } +} + +// Format items in availability column (td) of the tables in the holdings tab for XS +.availability-column { + a { + @media (max-width: $screen-sm-max) { + display: $record-tabs-availability-column-a-xs-sm-display; + margin-top: $record-tabs-availability-column-a-xs-sm-margin-top; + word-break: $record-tabs-availability-column-a-xs-sm-word-break; + } + } +} diff --git a/themes/finc/scss/components/_resolver-links.scss b/themes/finc/scss/components/_resolver-links.scss new file mode 100644 index 0000000000000000000000000000000000000000..f3632846d40f62d329150ab40b936341b101ab14 --- /dev/null +++ b/themes/finc/scss/components/_resolver-links.scss @@ -0,0 +1,81 @@ +// Use this for all resolver-link-related styles + +// Redi link-resolver traffic lights +.resolver .show-availability { + display: $resolver-links-show-availability-display; +} + +.traffic-light { + display: $traffic-light-display; + padding: $traffic-light-padding; + vertical-align: $traffic-light-vertical-align; + + // This defines the look of the squares inside the traffic lights bar + // This first definition is general will be overridden by the definitions below + span { + // temporary color, will be overwritten by first, second, third items below + background-color: $traffic-light-inner-items-background-color; + border-top: $traffic-light-inner-items-border-top; + display: $traffic-light-inner-items-display; + height: $traffic-light-inner-items-height; + margin: $traffic-light-inner-items-margin; + padding-right: $traffic-light-inner-items-padding-right; + width: $traffic-light-inner-items-width; + + &.last { + margin-right: $traffic-light-inner-items-last-margin-right; + } + } + + // Override general settings with 'access denied' traffic light settings + &.access-denied { + span { + &.first { + background-color: $traffic-light-access-denied-inner-first-background-color; + } + + &.second { + background-color: $traffic-light-access-denied-inner-second-background-color; + } + + &.last { + background-color: $traffic-light-access-denied-inner-last-background-color; + } + } + } + + // Override general settings with 'access limited' traffic light settings + &.access-limited { + span { + &.first { + background-color: $traffic-light-access-limited-inner-first-background-color; + } + + &.second { + background-color: $traffic-light-access-limited-inner-second-background-color; + } + + &.last { + background-color: $traffic-light-access-limited-inner-last-background-color; + } + } + } + + // Override general settings with 'access open' traffic light settings + &.access-open { + span { + + &.first { + background-color: $traffic-light-access-open-inner-first-background-color; + } + + &.second { + background-color: $traffic-light-access-open-inner-second-background-color; + } + + &.last { + background-color: $traffic-light-access-open-inner-last-background-color; + } + } + } +} diff --git a/themes/finc/scss/components/_result-list.scss b/themes/finc/scss/components/_result-list.scss new file mode 100644 index 0000000000000000000000000000000000000000..ef3a5621faedae3836a3c739578550ffb427bf0d --- /dev/null +++ b/themes/finc/scss/components/_result-list.scss @@ -0,0 +1,151 @@ +// Use this for result list styles +// For Check boxes for bulk actions and bulk action buttons, see bulk-action-buttons.scss +// Pls. note that the result elements are also used in several myaccount views, such as 'Favorites' + + +// ***************************************************************** +// ************ Media container ************************************ +// ***************************************************************** + +.record, +.result { + // make sure that results items use max. available space on XS + .media { + @media (max-width: $screen-sm-max) { + padding-left: $result-list-record-result-media-container-xs-sm-padding-left; + padding-right: $result-list-record-result-media-container-xs-sm-padding-right; + } + } +} + +.result { + hyphens: $result-list-result-hyphens; + // Results padding + // Do not add margin-top or -bottom as it would result in uneven heights + padding-bottom: $result-list-result-padding-bottom; + padding-left: $result-list-result-padding-left; + // make full width, otherwise ugly look + width: $result-list-result-width; + + // remove padding for print + @media print { + padding: 0; + } + + // Alternating colors in search results + &:nth-of-type(2n) { + background-color: $result-list-result-nth-of-type-2-background-color; + } + + .title { + // overwrites VF-BS-Theme setting + font-size: $result-list-result-title-font-size; + // overwrites VF-BS-Theme setting + font-weight: $result-list-result-title-font-weight; + + // reset font size for xs + @media (max-width: $screen-xs-max) { + font-size: $result-list-result-title-xs-font-size; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + font-size: $result-list-result-title-sm-font-size; + } + } + + // Make add-to-cart icon same color as link + .cart-link-icon { + color: inherit; + } +} + + +// Record entry-related links in result list, incl. QR codes, public favorites lists etc. +.result-links { + // make sure icons ('add to bookbag', 'add to favs', 'context') align + // align 'add to favs' with 'bookbag' (note: bookbag icon is rendered via '.btn-type-add::before') + .fa-fw { + text-align: $result-links-fa-icons-text-align; + } + + // Favorites list in MyAccount: Edit favorites entry + .edit.tool, + .del-button { + a { + margin-top: $result-links-favorites-edit-delete-tool-a-margin-top; + + @media (max-width: $screen-sm-max) { + margin-top: $result-links-favorites-edit-delete-tool-a-xs-sm-margin-top; + } + } + + // This is the delete-dropdown with 'yes' or 'no' selectors + li a { + margin-top: $result-links-favorites-edit-delete-tool-a-submenu-margin-top; + } + } + + // Delete from favorites + // Harmonize behaviour to the edit button + .dropdown-toggle { + &:focus { + @include outline(1px); + } + } +} + +// 'Saved in Favorites' box in result list +.savedLists { + &.alert { + color: $result-links-saved-in-alert-color; + } + + ul { + list-style: $result-links-saved-in-ul-list-style; + list-style-position: $result-links-saved-in-ul-list-style-position; + padding: $result-links-saved-in-ul-padding; + + a { + color: $result-links-saved-in-ul-a-color; + } + } +} + +// Access icons +// make space for icons +.media-left { + text-align: $result-list-access-icons-container-text-align; + width: $result-list-access-icons-container-width; + + //// remove left padding for print + @media print { + padding-left: 0; + } +} + + +.access-icon { + color: $result-list-access-icon-color; + text-align: $result-list-access-icon-text-align; + + i { + font-size: $result-list-access-icon-i-font-size; + width: $result-list-access-icon-i-width; + } + + span { + font-size: $result-list-access-icon-inner-font-size; + padding-top: $result-list-access-icon-inner-padding-top; + text-align: $result-list-access-icon-inner-text-align; + width: $result-list-access-icon-inner-width; + + @media screen and (max-width: $screen-xs-max) { + display: $result-list-access-icon-inner-display-xs; + } + } +} + +// Favorites lists: list description on top +.list-desc { + margin-top: $favorites-list-desc-margin-top; +} diff --git a/themes/finc/scss/components/_search-control-elements.scss b/themes/finc/scss/components/_search-control-elements.scss new file mode 100644 index 0000000000000000000000000000000000000000..325866385d2a8c08de65c113f6b07e4c8ca420d9 --- /dev/null +++ b/themes/finc/scss/components/_search-control-elements.scss @@ -0,0 +1,156 @@ +// Use this for all search control element styles +// Search controls are the elements on top of the result list, including limit and sort by + +// ***************************************************************** +// ************ Search controls for result list ******************** +// ***************************************************************** + +// set display to flex or other +.search-header { + display: $search-controls-container-display-type; +} + +.search-stats { + flex-grow: $search-stats-flex-grow; +} + +//// Push hits count down to align with limit and sort select boxes +.hits-count { + padding-top: $hits-count-top-padding; +} + +// The container structure for limit and sort is +// .search-controls +// .limit +// form.limit-form +// .limit-inner +// select#limit +// .sort.right +// form.search-sort.text-right +// .sort-inner +// select#sort_options_... + +.limit, +.sort { + margin-bottom: $limit-sort-bottom-margin; + + // the following are important for identical labels' positioning + label { + line-height: $limit-sort-label-line-height; + margin-bottom: $limit-sort-label-margin-bottom; + padding-right: $limit-sort-label-padding-right; + } + + select { + margin-bottom: $limit-sort-select-margin-bottom; + } + + @media (max-width: $screen-sm-max) { + button, + label, + select { + display: $limit-sort-button-label-select-display-xs-sm; + } + + select { + // overwrite bootstrap3 + margin-bottom: $limit-sort-select-margin-bottom-xs-sm; + margin-right: $limit-sort-select-margin-right-xs-sm; + } + } +} + +// For INLINE, left-floated 'hits per page' and 'sort by' selectors use 'float: left' here +// Note that the outer sort container is right-floated while the inner (form) one gets left-floated +.limit, +.search-sort { + float: $limit-search-sort-float; +} + +.limit { + margin-right: $limit-margin-right; + + @media (max-width: $screen-xs-max) { + margin-right: $limit-margin-right-xs; + } + + @media (max-width: $screen-sm-max) { + float: $limit-float-xs-sm; + } + + label { + text-align: $limit-label-text-align; + } +} + +.sort { + float: $sort-container-float; + + @media (max-width: $screen-sm-max) { + float: $sort-container-float; + } + + // limit the width of the select field if necessary - for more select details, see FORMS scss + select { + max-width: $sort-select-max-width; + + @media only screen and (max-width: 410px) { + max-width: $sort-select-max-width-below-410px; + } + } +} + +// inner sort by-form element +.search-sort { + display: $sort-inner-form-display; +} + +// 'limit-inner' and 'sort-inner' contain the select elements _and_ buttons for '.limit' and '.sort' +.limit-inner, +.sort-inner { + display: $limit-sort-inner-element-display; + + // Place the refresh button next to the select box + @media (max-width: $screen-sm-max) { + display: $limit-sort-inner-element-display-xs-sm-display; + } + + // top align buttons and select boxes + .btn { + vertical-align: top; + } +} + +// ***************************************************************** +// ************ Search tools for result list *********************** +// ***************************************************************** +// This is the ribbon with RSS, mail etc, which is hidden in many instances +// see search/results.phtml for more + +// Pagination & Searchtools get the same centered display +.searchtools { + @include content-centered-display-as-table; + + @media print { + display: none; + } + + // Display searchtools-links as buttons on XS, SM + a { + @media (max-width: $screen-sm-max) { + background-color: $search-tools-links-background-color-xs-sm; + border: $search-tools-links-border-xs-sm; + color: $search-tools-links-color-xs-sm; + // align on top of each other + display: $search-tools-links-display-xs-sm; + margin-bottom: $search-tools-links-margin-bottom-xs-sm; + padding: $search-tools-links-padding-xs-sm; + text-align: $search-tools-links-text-align-xs-sm; + touch-action: $search-tools-links-touch-action-xs-sm; + vertical-align: $search-tools-links-vertical-align-xs-sm; + white-space: $search-tools-links-white-space-xs-sm; + } + } +} + + diff --git a/themes/finc/scss/components/_search.scss b/themes/finc/scss/components/_search.scss new file mode 100644 index 0000000000000000000000000000000000000000..528aeefad137439f4c6971311114de56e41227f1 --- /dev/null +++ b/themes/finc/scss/components/_search.scss @@ -0,0 +1,328 @@ +// This file is for all search and search-box related elements, +// incl. advanced search, bulk action buttons, search tools (limit/sort ...) and search results + + +// ***************************************************************** +// ************ Search box ***************************************** +// ***************************************************************** + +// Make more white space when Hamburger is open on XS; keep .nav for specifity +.nav.searchbox { + @media (max-width: $screen-xs-max) { + margin-bottom: $search-container-margin-bottom-xs; + } +} + +// Make search box container flexible -- values should complement those +// of #header-collapse (i.e. the right-hand side navigation elements) +.search.container { + padding-right: $search-container-right-padding; + + // Make wider to fit searchbox and right-hand nav-elements + @media (min-width: $screen-sm-min) { + flex: $search-container-sm-up-flex; + padding-top: $button-top-padding; + width: $search-container-sm-up-width; + } + + // Float 'Find' button next to searchbox on XS and SM + @media (max-width: $screen-sm-max) { + padding-left: $search-container-sm-max-left-padding; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + flex: $search-container-sm-only-flex; + } +} + +// '.searchForm' is the class of the entire form itself +.searchForm { + @media (max-width: $screen-sm-max) { + display: $search-form-display; + flex-wrap: $search-form-flex-wrap; + justify-content: $search-form-justify-content; + margin-bottom: $search-form-margin-bottom; + margin-top: $search-form-margin-top; + padding-top: $search-form-padding-top; + } + + @media (min-width: $screen-sm-min) { + width: $search-form-sm-up-width; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + display: $search-form-sm-only-display-no-flex; + } + + // Searchform input field + 'search for media type' field + input:not([type='checkbox']), + select.searchForm_type { + @media (max-width: $screen-sm-max) { + display: $search-form-input-type-sm-max-display; + flex-grow: $search-form-input-type-sm-max-flex-grow; + flex-shrink: $search-form-input-type-sm-max-flex-shrink; + } + } + + input:not([type='checkbox']) { + @media (max-width: $screen-sm-max) { + flex-basis: $search-form-input-only-sm-max-flex-basis; + } + } + + select.searchForm_type { + @media (max-width: $screen-sm-max) { + flex-basis: $search-form-select-only-sm-max-flex-basis; + } + } + + // Shards checkboxes: To apply identical styles to checkboxes + // on HOME and all other pages, we use '.searchForm' as parent + // Since shards are mostly relevant for development only, we do not introduce variables here + .checkbox { + clear: both; + display: block; + float: none; + padding-top: ($grid-gutter-width / 4); + + @media (max-width: $screen-sm-max) { + // make this correspond to .searchForm @media (max-width: $screen-sm-max) {}, requires "flex-wrap: wrap" on parent + display: flex; + margin-top: .7rem; + padding-top: 0; + width: 100%; + } + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + position: static; + } + + // actual checkbox box + input { + margin-right: .25rem; + // make sure, input and label are aligned in acceptable manner between 100% and 200% zoom + margin-top: $input-top-margin; + } + } + + // BUTTONS, inputs and selects in search form + .btn, + .form-control { + // align 'search type' and search button: + // Pls. beware that the active-filters' buttons will also be affected by this (reset in 'header-active-filters.scss' -> button[id^='dropdown-toggle-']) + vertical-align: $search-form-btn-form-control-vertical-align; + + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + float: $search-form-btn-form-control-float-xs; + } + } + + // make advanced search button discernible + .btn-transparent { + @media (max-width: $screen-sm-max) { + border: $search-form-advanced-search-button-border; + } + } +} + + +// ***************************************************************** +// ************ Advanced Search ************************************ +// ***************************************************************** +// Pls. note: +// In most instance we have MOVED the advanced search TERMS INTO the BREADCRUMBS area +// in order to keep the header ok on small devices. If this is the case in your instance, +// you can ignore the '.adv_search_terms' settings. + +// Outer container for search box and adv search links +.navbar-form { + margin-bottom: 0; // overwrites BS theme + + .result-advanced & { + // create sufficient space for header menu + @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { + max-width: $adv-search-links-sm-to-md-max-max-width; + } + } +} + +// Advanced search terms in header (visible only after an advanced search was run) +// i.e. the search terms entered +.adv_search_terms { + background-color: $adv-search-terms-bg; + border: $adv-search-terms-border; + margin-bottom: $adv-search-terms-bottom-margin; + padding: $adv-search-terms-padding; + width: $adv-search-terms-width; +} + +// Advanced search links in header (visible only after an advanced search was run) +// i.e. 'Edit advanced search' | 'New advanced search' ... +.adv_search_links { + border: $adv-search-links-border; + list-style: $adv-search-links-list-style; + margin: $adv-search-links-margin; + padding: $adv-search-links-padding; + + @media (min-width: $screen-sm-min) { + margin-top: $adv-search-links-top-margin-sm; + } + + a { + border-top: $adv-search-links-anchor-top-border; + display: $adv-search-links-anchor-display; + padding-left: $adv-search-links-anchor-padding-left; + + &:last-of-type { + border-bottom: 0; + } + + // 786px and above as inline list + @media (min-width: $screen-sm-min) { + border-bottom: $adv-search-links-anchor-border-bottom-sm-up; + border-right: $adv-search-links-anchor-border-right-sm-up; + display: $adv-search-links-anchor-display-sm-up; + padding-right: $adv-search-links-anchor-padding-right-sm-up; + + &:last-of-type { + border-right: 0; + } + } + } +} + +////////////// TODO: Review ///////////////////////////// +// .tab-content.adv-search container wraps advanced search terms and links +.adv-search { + @media (max-width: $screen-xs-max) { + margin-bottom: 0; + margin-left: -($half-gutter); + } + + // keep this parent for overwriting specifity + // input field + .adv-term-input { + // overwrite finc + @media (min-width: $screen-sm-min) { + width: 99%; + } + } + + // keep this parent for overwriting specifity + // search type select + .adv-term-type { + + // overwrite finc + @media (min-width: $screen-sm-min) { + max-width: 99%; + } + } + + // keep this parent for overwriting specifity + // Suchfeld remove-X-Buttons + .adv-term-remove { + // overwrite finc + height: unset; + margin-top: 26px; + opacity: unset; + width: 44px; + + @media (max-width: $screen-xs-max) { + border: $border-default-styles; + line-height: 1.5; + margin-top: 29px; + } + + @media (min-width: $screen-md-min) { + margin-left: 2px; + } + } +} + +.adv-input, +.adv-select { + float: left; + + @media (max-width: $screen-xs-max) { + padding-left: $half-gutter; + } +} + +.adv-input { + @media (max-width: $screen-xs-max) { + width: 100%; + } + + @media (min-width: $screen-sm-min) { + width: 59%; + } + + @media (min-width: $screen-md-min) { + width: 62%; + } +} + +.adv-select { + + @media (min-width: $screen-sm-min) { + max-width: unset; + width: 28%; + } +} + +////// Set colors for remove search field buttons +.adv-term-remove { + color: $alert-danger-color; + + &:focus, + &:hover { + color: $brand-danger; + opacity: 1; + } +} + +.add_search_link, +.adv-group-close { + margin-bottom: 4px; + padding-bottom: $button-top-padding; + padding-top: $button-top-padding; + + @media (max-width: $screen-xs-max) { + margin-top: $half-gutter; + } +} + +// this is the "remove search group" button +.adv-group-close { + color: $state-danger-text; + + @media (min-width: $screen-sm-min) { + float: right; + } + + i { + color: $brand-danger; + } +} + +////// Swap find and clear buttons for consistency +.adv-submit { + .fnd-btn { + float: right; + } + + .clear-btn { + float: left; + } +} + + +// ***************************************************************** +// ************ Autocomplete *************************************** +// ***************************************************************** + +// When using a fixed header + autocomplete, increase the z-index in +// autocomplete-results to a value higher than the fixed navbar (1030)! +.autocomplete-results { + z-index: $zindex-navbar-fixed + 1; +} diff --git a/themes/finc/scss/components/_sidebar.scss b/themes/finc/scss/components/_sidebar.scss new file mode 100644 index 0000000000000000000000000000000000000000..784e9013abeaabafb26cd739e774470409eba17f --- /dev/null +++ b/themes/finc/scss/components/_sidebar.scss @@ -0,0 +1,349 @@ +// Use this for sidebar styles incl facets + +// ***************************************************************** +// ************ General ******************************************** +// ***************************************************************** + +// Sidebar width, when off-canvas is _off_; +// For more on off-canvas see offcanvas.scss +.sidebar { + body:not(.offcanvas) & { + @media (max-width: $screen-xs-max) { + width: 100%; + } + } + + // #13625 avoid ugly linebreaks with overlong words, RL + -moz-hyphens: auto; + -ms-hyphens: auto; + -o-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; + // Add top padding to sidebar and '.mainbody' for better looks + // (see also result-list.scss); + padding-top: $mainbody-sidebar-top-padding; + + @media (max-width: $screen-xs-max) { + padding-top: $mainbody-sidebar-top-padding-xs; + } + + // pull content to right border for sidebar right + &.right { + @include pull-sidebar-to-right($margin-right-width); + + // Remove right-hand borders for right-side facets only, + // ONLY works when sidebar butts right border + .facet-group .facet, + .facet-group .title, + .facet-group .collapse, + .facet-group .collapsing, + .facet-group > .facet { + @include right-border-on-sidebar($border-right-width); + } + } + + // Selected/active filters on top + .facet.active, + // 'a.active' is for My Account + a.active { + background-color: $brand-warning; + color: $sidebar-facet-active-color; + + // color the icon too + .fa { + color: inherit; + } + + // for :visited states see visitedLinks.scss + } + + // try to have word breaks for better looks + .facet { + -ms-word-break: $sidebar-facet-word-break; + // fallback for hyphens: auto (Chrome on Desktop and bug on Mozilla for capitalized words) + word-break: $sidebar-facet-word-break; + + a { + text-decoration: $sidebar-facet-link-text-decoration; + } + + a, + .text { + -ms-word-break: $sidebar-facet-word-break; + word-break: $sidebar-facet-word-break; + + // special case: on sm-size AND search result facets + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + -ms-word-break: $sidebar-facet-word-break-sm; + word-break: $sidebar-facet-word-break-sm; + } + } + + // descriptive text inside active facet; + // push in to align with other items + &.active { + .text { + padding-left: $sidebar-facet-active-text-inside-padding-left; + } + } + + } +} + + +// Style result list facets and myresearch-menu facets to have the same height; +// both have a very similar structure: +// +// <ul ... class="facet-group" +// <li class="facet' +// +// or +// +// <div ... class="facet-group" +// <a class="title" +// +// keep '.facet-group' for specifity +.facet-group { + // RESET PADDING and apply it to facet inner items + .facet, + // '.title' applies to the h3 headings + .title { + // Apply padding to elements _inside_ the facet for useability + // See also '.myresearch-menu' below + // RESET padding to '0' here! + padding: $sidebar-facet-title-padding; + } + + + // Re-APPLY PADDING HERE to facet inner items + a, + .text, + .badge, + .title, + .help-link { + // Add facet item padding to items + padding: $sidebar-item-padding; + } + + // Focus and hover behaviour for facet titles + .title { + background-color: $sidebar-facet-title-background-color; + + &:hover, + &:focus { + @include outline($outline-default-size); + + background-color: $sidebar-facet-title-background-color-focus-hover; + } + } + + // Active facets on top of standard facets + .facetOR.active { + // Style active facets to get proper left padding + .text { + text-indent: $sidebar-or-facet-text-indent; + } + } + + // Style myaccount sidebar menues to get the same look as facets + .facet { + .myresearch-menu & { + padding: $sidebar-facet-my-account-padding; + } + + a:not(.exclude) { + width: $sidebar-facet-my-account-link-width; + + &:not(:last-of-type) { + // overwrite values from BS with more sensible values + padding: $sidebar-item-padding; + } + } + } +} + +// Create hover-effect +.facet { + &:hover:not(.button), + &:focus:not(.button) { + background: $sidebar-item-hover-bg; + + .active-filters & { + background: $sidebar-item-active-hover-bg; + } + } + + // Active facet hover color + &.active:hover:not(.button), + &.active:focus:not(.button) { + color: $sidebar-facet-active-hover-color; + background: $sidebar-item-active-hover-bg; + + // color icon too + .fa { + color: inherit; + } + } +} + + +// ***************************************************************** +// ************ Exclude-Facets + badges + item counts ************** +// ***************************************************************** + +// Style the Exclude Icons and Links; +// the structure is +// +// <a class="exclude" +// <i class="fa ..."> +// <span class="sr-only"> +// +.exclude { + // Accessibility: Apply negative 'margin-left' and + // overwrite 'padding-left' below for propper tappable item + margin-left: $sidebar-exclude-facet-margin-left; + // Should be 38px tall for tapping; + // requires re-application of facet padding on facet inner items, see above + min-height: $sidebar-exclude-facet-min-height; + + // Remove standard left-padding for better tabbability + // keep '.facet' for specifity + .facet & { + padding-left: $sidebar-exclude-facet-padding-left; + } + + &:focus, + &:hover { + @include outline(1px); + + color: $badge-link-hover-color; + } + + // x item + i { + color: $badge-link-color; + + &:focus, + &:hover { + color: $badge-link-hover-color; + } + } + +} + +// '.itemCount' is used instead of the newer '.badge' syntax in oder myaccount menus +.itemCount { + &:focus, + &:hover { + color: $badge-link-hover-color; + + .active & { + color: $brand-secondary; + } + } +} + +// '.badge' is used in result list for the number of items in facets +// and in newer myaccount menu items +.badge { + font-size: $sidebar-facet-badge-font-size; + // remove right padding to align with accordion arrows + padding-right: $sidebar-facet-badge-padding-right; + + &.btn-danger, + &.btn-info, + &.btn-success, + &.btn-transparent, + &.btn-warning { + &:focus, + &:hover { + color: $btn-badge-hover-color; + } + } + + // Numbers in MyAccount sidebar + // Here's an example for _three items_ next to each other in the 'checked-out items' facet + // <span class="checkedout-status status"> + // <span class="badge ok" data-toggle="tooltip" title="" data-original-title="Ausgeliehene Medien">5</span> + // <span class="badge warn" data-toggle="tooltip" title="" data-original-title="Bald fällige Ausleihen">1</span> + // <span class="badge overdue" data-toggle="tooltip" title="" data-original-title="Ãœberfällige Ausleihen">2</span> + // </span> + &.ok, + &.overdue, + &.warn { + .myresearch-menu & { + color: $sidebar-facet-active-hover-color; + display: $sidebar-facet-badge-my-research-icons-display; + font-size: $sidebar-facet-badge-my-research-icons-font-size; + // unset height attribute from .facet .badge {} definition for better looks + max-height: $sidebar-facet-badge-my-research-icons-max-height; + padding: $sidebar-facet-badge-my-research-icons-padding; + } + } + + &.ok { + .myresearch-menu .active .status & { + background-color: $state-success-bg; + } + } + + &.overdue { + .myresearch-menu .active .status & { + background-color: $state-danger-bg; + } + } + + + // light color on dark, when active (Facets + My Account) - we need the parent for specificity + .facet.active > &, + .sidebar a.active > & { + background-color: $sidebar-facet-badge-background-color; + color: $sidebar-facet-active-hover-color; + } +} + +// special item count displays with colored backgrounds +.has-error .form-control, +.sms-error .form-control { + border: $sidebar-facet-badge-has-error-border; + + &:focus, + &:hover { + border: $sidebar-facet-badge-has-error-border-focus-over; + } +} + + +// ***************************************************************** +// ************ More-Toggle **************************************** +// ***************************************************************** + +// More-Toggle (for variable, see customVariables.scss) +.narrow-toggle { + text-align: $narrow-toggle-text-alignment; +} + + +// ***************************************************************** +// ************ Toolbar in Record view / Detail view *************** +// ***************************************************************** +// For Offcanvas back button in sidebar see offcanvas.scss + +.nav-stacked.nav > li > a { + @media (min-width: $screen-sm-min) { + padding-right: 0; + } +} + +// Override record.scss value here to keep it consistent with .nav .btn-bookbag-toggle a +.record-nav > li > a, +.record-nav > li a { + padding: $record-view-toolbar-button-padding; +} + +.record-nav > li > a { + @media (max-width: $screen-xs-max) { + text-align: left; + } +} + diff --git a/themes/finc/scss/components/_tables.scss b/themes/finc/scss/components/_tables.scss new file mode 100644 index 0000000000000000000000000000000000000000..8d2a843f6b2ea7d4d31ca4f90a80b506b5c50008 --- /dev/null +++ b/themes/finc/scss/components/_tables.scss @@ -0,0 +1,66 @@ +// Use this for all tables-related styles + +// ***************************************************************** +// ************ General styles ************************************* +// ***************************************************************** + +.table { + // add border to tables -- otherwise we'd have to rewrite all tables with table-bordered + border: $table-border; + // make columns adapt to content + table-layout: $table-layout; + + // Remove top borders (_tables.scss) + thead, + tbody, + tfoot { + > tr { + > th, + > td { + border-top: $table-elements-border-top; + } + } + } + + // Tables in Tabs, Record View etc + .tab-content & { + width: $table-in-tabs-width; + } + +} + +// ***************************************************************** +// ************ Responsive data tables ***************************** +// ***************************************************************** +// 1. for small resolutions (e.g. search history table) +@media screen and (max-width: $screen-xs-max) { + // Force table around, exc. solrMarc staffview + .table-resp-data:not(.citation) { + display: flex; + max-width: 90vw !important; + } + + .table-resp-data { + border: 0; + display: block; + } + + // .table-resp-data (default) -- is applied to XS + .table-resp-data { + @include table-resp-data; + } + + // limit width for staff view + .tab-pane.details-tab.active { + max-width: 80vw; + overflow-x: auto; + } +} + +// 2. for medium (md) resolutions (e.g. fines table) +@media screen and (max-width: $screen-md-max) { + .table-resp-data-md { + @include table-resp-data; + } +} + diff --git a/themes/finc/scss/finc.scss b/themes/finc/scss/finc.scss new file mode 100644 index 0000000000000000000000000000000000000000..d0a058160466211b5307ba4da23eb3d0e879a027 --- /dev/null +++ b/themes/finc/scss/finc.scss @@ -0,0 +1,183 @@ +// New Stylesheet for finc (VF6.x up) +// HOW TO: Create a theme-named SCSS for your house and import that file into compiled.scss + +// ***************************************************************** +// **************************** Imports **************************** +// ***************************************************************** + +// Make sure imports are grouped correctly so variables can be loaded! +@import 'components/functions'; +@import 'customVariables'; + +// Import Bootstrap here +@import '../../bootstrap3/scss/bootstrap'; +@import '../../finc-accessibility/scss/compiled'; +@import 'customMixins'; +@import 'common'; +@import 'components/accordions-collapse-elements'; +@import 'components/alerts'; +@import 'components/bookbag'; +@import 'components/breadcrumbs'; +@import 'components/bulk-action-buttons'; +@import 'components/buttons'; +@import 'components/carousel-similar-items-channels-slider'; +@import 'components/dropdowns'; +@import 'components/footer'; +@import 'components/forms'; +@import 'components/header-active-filters'; +@import 'components/header-navbar'; +@import 'components/headings'; +@import 'components/hierarchy-tree'; +@import 'components/home-page'; +@import 'components/hotfixes'; +@import 'components/media-styled-based-account-icons-and-status-colors'; +@import 'components/modal'; +@import 'components/offcanvas'; +@import 'components/pagination'; +@import 'components/record-tabs'; +@import 'components/resolver-links'; +@import 'components/result-list'; +@import 'components/search'; +@import 'components/search-control-elements'; +@import 'components/sidebar'; +@import 'components/tables'; +// @import 'components/admin-panel'; +// @import 'components/amsl'; + + +// Activate the following imports in your library's theme: +// 1. To make bulk action buttons visible on small devices (Accessibility) +// @import 'activate-on-demand/bulkaction-visible-xs'; +// 2. To make visited links use a different color, use the import in anchors.scss +// 3. .............. + +// ***************************************************************** +// ********** HTML basics ****************************************** +// ***************************************************************** +// Use this section to style your document body/html +// including top-padding to accommodate for navbar height + +// Allow us to use sections that act as rows +section { + @include make-row; +} + +// Prevent content from jumping when changing tabs in record view +html { + overflow-y: scroll; +} + +body { + // fix modals adding a padding-right #13625 + padding-right: 0 !important; + + // Add top-padding to body to accommodate fixed navbar; -- padding should equal navbar-height -- + // the home page, however, has a lower navbar because there is no breadcrumbs there + // for mobile + padding-top: $navbar-height-xs; + + // for XS + @media (max-width: $screen-xs-max) { + padding-top: 8.5rem; + } + + // for XS make exception for adv. search results + &.template-name-advanced { + @media (max-width: $screen-xs-max) { + padding-top: 3.25rem; + } + } + + // for tablet + @media (min-width: $screen-sm-min) { + padding-top: $navbar-height-sm; + } + + // for large + @media (min-width: $screen-md-min) { + padding-top: $navbar-height; + } + + // for print + @media print { + padding-top: 0; + } +} + +// ***************************************************************** +// ********** Main Content Container (.main, .container, footer ...* +// ***************************************************************** + +.main .container, +.main .container-fluid, +footer { + // sets background and adds border for main content, also applies to footer + background-color: $main-bg; + border: $border-default-styles; + padding-bottom: $grid-gutter-width / 1.5; + + @media print { + border: 0; + padding: 0; + } +} + +// ***************************************************************** +// ************ .mainbody container ******************************** +// ***************************************************************** +// .mainbody is the container for the entire search results block, +// the structure is +// .main +// #content.container +// .mainbody.search-results-col +// it does _not_ include the sidebar +.mainbody { + // Add top padding to sidebar and .mainbody content + // for better looks, for sidebar see sidebar.scss; + padding-top: $mainbody-sidebar-top-padding; + + @media (max-width: $screen-xs-max) { + padding-top: $mainbody-sidebar-top-padding-xs; + width: 100%; + } +} + +#content { + // Push content below header + @media (max-width: $screen-xs-max) { + padding-top: $content-xs-padding-top; + } +} + + +// ***************************************************************** +// ********** Document Delivery Service (DDS) ********************** +// ***************************************************************** +// Error field +.error-field { + color: $brand-danger; + // font-size: ($font-size-base * .95); + font-weight: 500; + line-height: 200%; +} + +// ***************************************************************** +// ********** xxxx) ********************** +// ***************************************************************** + + + + + + + + + + + + + + + + + diff --git a/themes/finc/templates/ContentBlock/FacetList.phtml b/themes/finc/templates/ContentBlock/FacetList.phtml index 886e12940c21c41f8308e42e8ef6f7d1f9a0ea37..72432fe61356b5ba3033a3a1b64c23163f39f712 100644 --- a/themes/finc/templates/ContentBlock/FacetList.phtml +++ b/themes/finc/templates/ContentBlock/FacetList.phtml @@ -1,14 +1,14 @@ <!-- finc: ContentBlock - FacetList --> <?php - // Load search actions and settings (if any): - $options = $this->searchOptions($searchClassId); - $basicSearch = $options->getSearchAction(); - $advSearch = $options->getAdvancedSearchAction(); - $noJsSupport = $this->config()->nonJavascriptSupportEnabled(); + // Load search actions and settings (if any): + $options = $this->searchOptions($searchClassId); + $basicSearch = $options->getSearchAction(); + $advSearch = $options->getAdvancedSearchAction(); + $noJsSupport = $this->config()->nonJavascriptSupportEnabled(); ?> - <?php if (!empty($facetList)): ?> <div class="search-home-facets"> + <?php /* finc-specific variables, #15579 */ ?> <?php $itemSum = $fieldSum = 0; $maxColumns = 4; @@ -17,46 +17,45 @@ $fieldSum += 1; } ?> - <?php foreach ($facetList as $field => $details): ?> - <?php if ($isHierarchy = in_array($field, $hierarchicalFacets ?? [])): - $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); - $this->headScript()->appendFile('facets.js'); - $sort = $hierarchicalFacetSortOptions[$field] ?? ''; - $script = <<<JS + <?php foreach ($facetList as $field => $details): ?> + <?php if ($isHierarchy = in_array($field, $hierarchicalFacets ?? [])): + $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); + $this->headScript()->appendFile('facets.js'); + $sort = $hierarchicalFacetSortOptions[$field] ?? $hierarchicalFacetSortOptions['*'] ?? 'all'; + $script = <<<JS $(document).ready(function() { $('#facet_{$this->escapeHtml($field)}_container').removeClass('hide'); initFacetTree($('#facet_{$this->escapeHtml($field)}'), false); }); JS; - echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); - - ?> + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + ?> <div id="facet_<?=$this->escapeHtml($field)?>_container" class="home-facet <?=$this->escapeHtmlAttr($field)?> hide"> <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> <div id="facet_<?=$this->escapeHtml($field)?>" class="jstree-facet" - data-source="<?=$this->escapeHtml($this->searchClassId)?>" - data-facet="<?=$this->escapeHtml($field)?>" - data-path="<?=$this->url($basicSearch)?>" - data-exclude="0" - data-operator="AND" - data-exclude-title="<?=$this->transEsc('exclude_facet')?>" - data-sort="all"> + data-source="<?=$this->escapeHtml($this->searchClassId)?>" + data-facet="<?=$this->escapeHtml($field)?>" + data-path="<?=$this->url($basicSearch)?>" + data-exclude="0" + data-operator="AND" + data-exclude-title="<?=$this->transEsc('exclude_facet')?>" + data-sort="<?=$sort?>"> </div> </div> <noscript> - <?php if (!$noJsSupport): ?> - <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> - <?=$this->transEsc('Please enable JavaScript.')?> - <?php endif; ?> - <?php endif; ?> - <?php if (!$isHierarchy || $noJsSupport): // do we need regular display? ?> - <?php $sortedList = $this->sortFacetList($results, $field, $details['list'], $basicSearch); ?> + <?php if (!$noJsSupport): ?> + <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> + <?=$this->transEsc('Please enable JavaScript.')?> + <?php endif; ?> + <?php endif; ?> + <?php if (!$isHierarchy || $noJsSupport): // do we need regular display? ?> + <?php $sortedList = $this->sortFacetList($results, $field, $details['list'], $basicSearch); ?> <div class="home-facet <?=$this->escapeHtmlAttr($field) ?>"> <h2><?=$this->transEsc('home_browse') . ' ' . $this->transEsc($details['label'])?></h2> <div class="home-facet-container"> <ul class="home-facet-list"> <?php - + // finc: use $currentListLength for calculation of $maxListLength #15579 // Convenience variable: $currentListLength = count($sortedList); @@ -64,35 +63,34 @@ JS; $maxListLength = $field == 'callnumber-first' || ($currentListLength * 2 >= $itemSum && $fieldSum < $maxColumns) // #15579 ... or outnumbered facets ? $columnSize * 2 : $columnSize; - - // Special case: custom URLs for collections... - $moreUrl = $field == 'hierarchy_top_title' - ? $this->url('collections-home') : $this->url($advSearch); - - ?> - <?php $i = 0; foreach ($sortedList as $url => $value): - // Special case: custom URLs for collections... - if ($field == 'hierarchy_top_title') { - $url = $this->url('collections-bytitle') . '?title=' . urlencode($value); - } - ?> - <li><a href="<?=$url?>"><?=$this->escapeHtml(empty($value) ? '-' : $value)?></a></li> - <?php if (++$i >= $currentListLength) break; // end of list? bail out! ?> - <?php if ($i >= $maxListLength): // list too long? show more link! ?> - <li><a href="<?=$moreUrl?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li> - <?php break; ?> - <?php elseif ($i % $columnSize === 0): // end of column? insert break! ?> - </ul><ul class="home-facet-list"> - <?php endif; ?> - <?php endforeach; ?> + + // Special case: custom URLs for collections... + $moreUrl = $field == 'hierarchy_top_title' + ? $this->url('collections-home') : $this->url($advSearch); + ?> + <?php $i = 0; foreach ($sortedList as $url => $value): + // Special case: custom URLs for collections... + if ($field == 'hierarchy_top_title') { + $url = $this->url('collections-bytitle') . '?title=' . urlencode($value); + } + ?> + <li><a href="<?=$url?>"><?=$this->escapeHtml(empty($value) ? '-' : $value)?></a></li> + <?php if (++$i >= $currentListLength) break; // end of list? bail out! ?> + <?php if ($i >= $maxListLength): // list too long? show more link! ?> + <li><a href="<?=$moreUrl?>"><strong><?=$this->transEsc("More options")?>...</strong></a></li> + <?php break; ?> + <?php elseif ($i % $columnSize === 0): // end of column? insert break! ?> + </ul><ul class="home-facet-list"> + <?php endif; ?> + <?php endforeach; ?> </ul> </div> </div> - <?php endif; ?> - <?php if ($isHierarchy): // close tag opened in matching if above ?> + <?php endif; ?> + <?php if ($isHierarchy): // close tag opened in matching if above ?> </noscript> - <?php endif; ?> - <?php endforeach; ?> + <?php endif; ?> + <?php endforeach; ?> </div> <?php endif; ?> <!-- finc: ContentBlock - FacetList - END --> \ No newline at end of file diff --git a/themes/finc/templates/Helpers/ils-offline.phtml b/themes/finc/templates/Helpers/ils-offline.phtml index 3183b2e28d229537ab7c014ac9c25b0fffef209f..13b394e58f39473c129a5fc9756297831e32efa5 100644 --- a/themes/finc/templates/Helpers/ils-offline.phtml +++ b/themes/finc/templates/Helpers/ils-offline.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - Helpers - ils-offline --> -<?php /* copied from bootstrap3 - h2 becomes h1 - #17596 - HR */?> +<?php /* finc uses h1 instead of h2 - #17596 */?> <div class="alert alert-warning"> <h1><?=$this->transEsc('ils_offline_title')?></h1> diff --git a/themes/finc/templates/Helpers/openurl.phtml b/themes/finc/templates/Helpers/openurl.phtml index f1daccea96259036864aa2d74be9bf443f7706ad..7005998d80ea9a2f365c5403e422ddf48dac13b2 100644 --- a/themes/finc/templates/Helpers/openurl.phtml +++ b/themes/finc/templates/Helpers/openurl.phtml @@ -18,7 +18,8 @@ <span class="openUrlControls<?php if ($this->openUrlEmbed): ?> openUrlEmbed<?php if ($this->openUrlEmbedAutoLoad): ?> openUrlEmbedAutoLoad<?php endif; ?><?php endif; ?>"> <?php if (!$this->openUrlImageBasedSrc || $this->openUrlImageBasedMode == 'both'): ?> <a href="<?=$this->escapeHtmlAttr($this->resolverUrl)?>"<?=$class?> data-search-class-id="<?=$this->escapeHtmlAttr($this->searchClassId) ?>"> - <?php /* put the openUrl here in a span (COinS almost) so we can retrieve it later */ ?> + <?php /* put the openUrl here in a span (COinS almost) so we can retrieve it later */ + /* finc: keep 'inputOpenUrl' - see #16136 */ ?> <span title="<?=$this->escapeHtmlAttr($this->inputOpenUrl)?>" class="openUrl"></span> <?php /* Custom finc, in #5334 (support for multiple resolver drivers) - CK */ ?> <span title="<?=$this->escapeHtmlAttr($this->resolvertype)?>" class="resolvertype"></span> @@ -41,6 +42,7 @@ <?php endif; ?> <?php if ($this->openUrlImageBasedSrc): ?> + <?php /* Custom finc: bugfix in openUrl handling; keep 'inputOpenUrl' - see #16136 */ ?> <?php $ibOpenUrl = $this->openUrlImageBasedOverride ? $this->openUrlImageBasedOverride : $this->inputOpenUrl; ?> <a href="<?=$this->escapeHtmlAttr($this->openUrlBase . '?' . $ibOpenUrl)?>"<?=$class_ib?>> <span title="<?=$this->escapeHtmlAttr($ibOpenUrl)?>" class="openUrl"></span> @@ -50,6 +52,7 @@ </span> <?php if ($this->openUrlEmbed): ?> +<?php /* finc: keep ' ' */ ?> <div class="resolver hidden"><?=$this->transEsc('Loading')?> ...</div> <?php endif; ?> <!-- finc: helpers - openurl - END --> diff --git a/themes/finc/templates/Recommend/CollectionSideFacets.phtml b/themes/finc/templates/Recommend/CollectionSideFacets.phtml index 1bce266a5e1fc4c4351c72987b18c994b4b5ae5b..857aaebe70290cb3a96990151b81fb19478e9bc0 100644 --- a/themes/finc/templates/Recommend/CollectionSideFacets.phtml +++ b/themes/finc/templates/Recommend/CollectionSideFacets.phtml @@ -1,45 +1,8 @@ <!-- finc: recommend - collectionSideFacets --> <?php + // finc changes 'Filter Collection' to 'In This Collection' $this->overrideSideFacetCaption = 'In This Collection'; + $this->baseUriExtra = $this->recommend->getResults()->getParams()->getCollectionId(); ?> -<?php if ($this->recommend->keywordFilterEnabled()): ?> - <?php - $keywordFilter = $this->recommend->getKeywordFilter(); - if (!empty($keywordFilter)) { - $this->extraSideFacetFilters = [ - 'Keyword' => [ - [ - 'value' => $keywordFilter, - 'displayText' => $keywordFilter, - 'specialType' => 'keyword', - 'operator' => 'OR' - ] - ] - ]; - } - ?> - <?php ob_start() ?> - <div class="panel panel-default"> - <div class="panel-heading"> - <h4 class="panel-title"> - <?=$this->transEsc('Keyword Filter')?> - </h4> - </div> - <div class="panel-body"> - <form class="form-inline" role="form" method="get" name="keywordFilterForm" id="keywordFilterForm"> - <input id="keywordFilter_lookfor" type="text" name="lookfor" value="<?=$this->escapeHtmlAttr($keywordFilter)?>" class="form-control"/> - <?php foreach ($this->recommend->getResults()->getParams()->getFilterList(true) as $field => $filters): ?> - <?php foreach ($filters as $filter): ?> - <input type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($filter['field'])?>:"<?=$this->escapeHtmlAttr($filter['value'])?>"" /> - <?php endforeach; ?> - <?php endforeach; ?> - <input class="btn btn-transparent" type="submit" name="submit" value="<?=$this->transEsc('Set')?>"/> - </form> - </div> - </div> - <?php $this->sideFacetExtraControls = ob_get_contents(); ?> - <?php ob_end_clean(); ?> -<?php endif; ?> -<?php $this->baseUriExtra = $this->recommend->getResults()->getParams()->getCollectionId(); ?> <?=$this->render('Recommend/SideFacets.phtml')?> <!-- finc: recommend - collectionSideFacets - END --> diff --git a/themes/finc/templates/Recommend/ExpandFacets.phtml b/themes/finc/templates/Recommend/ExpandFacets.phtml index 1b0ee79dd003b3ec602a3f0ae3b6fb8473e6d2aa..92a24d68ffa33d6fa9e7ff53180e879c4ffe45a8 100644 --- a/themes/finc/templates/Recommend/ExpandFacets.phtml +++ b/themes/finc/templates/Recommend/ExpandFacets.phtml @@ -1,6 +1,6 @@ <!-- finc - templates - Recommend - ExpandFacets --> -<?php /* copied from bootstrap3 - h4 becomes h2 - #17607 - HR */?> -<?php /* add a back button #17601 - HR */?> +<?php /* finc turns h4 into h2 - #17607 */ + /* adds back button #17601 */?> <?php $expandFacetSet = $this->recommend->getExpandedSet(); diff --git a/themes/finc/templates/Recommend/SideFacets.phtml b/themes/finc/templates/Recommend/SideFacets.phtml index 1d1cf7105f04dae99e81838ceead34bcdb6f5e54..2f054bfd36fbfe3164257b376f1030d72bfaa23e 100644 --- a/themes/finc/templates/Recommend/SideFacets.phtml +++ b/themes/finc/templates/Recommend/SideFacets.phtml @@ -1,73 +1,76 @@ <!-- finc: recommend - sidefacets --> <?php -$this->headScript()->appendFile('facets.js'); + $this->headScript()->appendFile('facets.js'); -// Save results/options to $this so they are available to sub-templates: -$this->results = $results = $this->recommend->getResults(); + // Save results/options to $this so they are available to sub-templates: + $this->results = $results = $this->recommend->getResults(); $this->options = $options = $results->getOptions(); + $collapsedFacets = $this->recommend->getCollapsedFacets(); + $forceUncollapsedFacets = []; -$hierarchicalFacets = $this->recommend->getHierarchicalFacets(); -if ($hierarchicalFacets) { - // jstree.min.js used to be injected by hierarchical-facet.js, but with deferred - // processing it's called too late to append anything to the headers. - $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); -} + // Make sure facets with active selections are not collapsed: + $filterList = $results->getParams()->getFilterList(true); + foreach ($filterList as $field => $filters) { + foreach ($filters as $filter) { + $index = isset($filter['field']) ? array_search($filter['field'], $collapsedFacets) : false; + if ($index !== false) { + unset($collapsedFacets[$index]); // Open if we have a match + $forceUncollapsedFacets[] = $filter['field']; + } + } + } + + $hierarchicalFacets = $this->recommend->getHierarchicalFacets(); + if ($hierarchicalFacets) { + // jstree.min.js used to be injected by hierarchical-facet.js, but with deferred + // processing it's called too late to append anything to the headers. + $this->headScript()->appendFile('vendor/jsTree/jstree.min.js'); + } ?> +<?php /* finc changes btn-link to btn-primary */ ?> <button class="close-offcanvas btn btn-primary" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button> <?php if ($results->getResultTotal() > 0): ?> <h2><?=$this->transEsc(isset($this->overrideSideFacetCaption) ? $this->overrideSideFacetCaption : 'Narrow Search')?></h2> <?php endif; ?> -<?php $checkboxFilters = $results->getParams()->getCheckboxFacets(); ?> +<?php $checkboxFilters = $this->recommend->getCheckboxFacetSet(); ?> <?php $checkboxesShown = false; ?> <?php if (count($checkboxFilters) > 0): - foreach ($checkboxFilters as $current) { - if ($results->getResultTotal() > 0 || $current['selected'] || $current['alwaysVisible']) { - $checkboxesShown = true; - break; + foreach ($checkboxFilters as $current) { + if ($results->getResultTotal() > 0 || $current['alwaysVisible']) { + $checkboxesShown = true; + break; + } } - } ?> - <?php if ($checkboxesShown):?> - <div class="checkboxFilter"> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/checkbox-filters.phtml', ['checkboxFilters' => $checkboxFilters, 'results' => $results]);?> - </div> -<?php endif; ?> -<?php endif; ?> -<?php $extraFilters = isset($this->extraSideFacetFilters) ? $this->extraSideFacetFilters : []; ?> -<?php $collapsedFacets = $this->recommend->getCollapsedFacets() ?> -<?php $filterList = array_merge($results->getParams()->getFilterList(true), $extraFilters); ?> -<?php if (!empty($filterList)): ?> - <?=$this->context($this)->renderInContext('Recommend/SideFacets/filter-list.phtml', [ - 'collapsedFacets' => $collapsedFacets, - 'extraFilters' => $extraFilters, - 'filterList' => $filterList, - ]);?> + <?php if ($checkboxesShown): ?> + <div class="checkboxFilter"> + <?=$this->context($this)->renderInContext('Recommend/SideFacets/checkbox-filters.phtml', ['checkboxFilters' => $checkboxFilters, 'results' => $results]); ?> + </div> + <?php endif; ?> <?php endif; ?> -<?=isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : ''?> -<?php -/* finc-specific line to show allowed facet values only: sidefacet()->displayAllowedFacetValues ..., #7624 - CK */ /* Original line is: -<?php $sideFacetSet = $this->recommend->getFacetSet(); ?> - */ -?> +<?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> <?php $sideFacetSet = $this->sideFacet()->displayAllowedFacetValues($this->recommend->getFacetSet()); ?> <?php $hierarchicalFacets = $this->recommend->getHierarchicalFacets() ?> <?php $hierarchicalFacetSortOptions = $this->recommend->getHierarchicalFacetSortOptions() ?> <?php if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> <?php foreach ($sideFacetSet as $title => $cluster): ?> - <div class="facet-group" id="side-panel-<?=$this->escapeHtmlAttr($title)?>"> - <?php /* add h3, #19406 - VE */ ?> + <div class="facet-group" id="side-panel-<?=$this->escapeHtmlAttr($title) ?>"> + <?php /* finc adds h3 and aria coding; also uses a instead of button for W3C validation - #19695 */ ?> <h3> - <a <?php if(in_array($title, $collapsedFacets)): ?>class="title collapsed" aria-expanded="false"<?php else: ?>class="title" aria-expanded="true"<?php endif ?> data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > + <a <?php if (in_array($title, $collapsedFacets)): ?>class="title collapsed" aria-expanded="false"<?php else: ?>class="title" aria-expanded="true"<?php endif ?> data-toggle="collapse" href="#side-collapse-<?=$this->escapeHtmlAttr($title) ?>" > + <?php /* finc: add span #19934 */ ?> <?=$this->transEsc($cluster['label'])?> <span class="sr-only"><?=$this->transEsc('facet_select_hint') ?></span> </a> </h3> - <ul id="side-collapse-<?=$this->escapeHtmlAttr($title)?>" class="collapse<?php if (!in_array($title, $collapsedFacets)): ?> in<?php endif ?>"> + <?php /* finc: show site facet groups as listings #17934 */ ?> + <ul id="side-collapse-<?=$this->escapeHtmlAttr($title) ?>" class="collapse<?php if (!in_array($title, $collapsedFacets)): ?> in<?php endif ?>"<?php if (in_array($title, $forceUncollapsedFacets)): ?> data-force-in="1"<?php endif ?>> <?=$this->context($this)->renderInContext( 'Recommend/SideFacets/facet.phtml', [ 'facet' => $title, 'cluster' => $cluster, - 'collapsedFacets' => $collapsedFacets ] + 'collapsedFacets' => $collapsedFacets + ] ); ?> </ul> </div> diff --git a/themes/finc/templates/Recommend/SideFacets/range-slider.phtml b/themes/finc/templates/Recommend/SideFacets/range-slider.phtml index d68f84ff8e5bebd4dfeadf6d90e3034deca56854..669b72de5c171c72aa55e12cce932c49eed3f95c 100644 --- a/themes/finc/templates/Recommend/SideFacets/range-slider.phtml +++ b/themes/finc/templates/Recommend/SideFacets/range-slider.phtml @@ -4,12 +4,15 @@ * slider-container element */ ?> +<?php /* finc changes div into li for compatibility with facet list structure */ ?> <li class="facet"> + <?php /* finc adds landmarks for active facets */ ?> <?php if (!empty($this->facet['values'][0])): ?> <?php $this->sideFacet()->setAppliedFacet($this->transEsc('Skip to facet', ['%%filter_name%%' => $this->transEsc('Year of Publication')]), $this->escapeHtmlAttr($this->title) . 'from')?> <?php elseif (!empty($this->facet['values'][1])): ?> <?php $this->sideFacet()->setAppliedFacet($this->transEsc('Skip to facet', ['%%filter_name%%' => $this->transEsc('Year of Publication')]), $this->escapeHtmlAttr($this->title) . 'to')?> <?php endif; ?> + <?php /* finc adds landmarks for active facets - END */ ?> <form name="<?=$this->escapeHtmlAttr($this->title)?>Filter" id="<?=$this->escapeHtmlAttr($this->title)?>Filter"> <?=$results->getUrlQuery()->asHiddenFields(['page' => "/./", 'filter' => "/^{$this->title}:.*/"])?> <input type="hidden" name="<?=$this->escapeHtmlAttr($this->facet['type'])?>range[]" value="<?=$this->escapeHtmlAttr($this->title)?>"/> @@ -17,6 +20,7 @@ <?php /* finc adds 'max="'.(date('Y')+1).' to prevent dates beyond the year + 1 to be entered; maxlength was causing w3c issues */ ?> <?php $extraInputAttribs = ($this->facet['type'] == 'date') ? 'max="'.(date('Y')+1).'" ' : ''; ?> <div class="date-from"> + <?php /* finc adds label ids */ ?> <label id="from-label" for="<?=$this->escapeHtmlAttr($this->title)?>from"> <?=$this->transEsc('date_from')?>: </label> @@ -32,12 +36,15 @@ </div> </div> <?php if ($this->facet['type'] == 'date'): ?> - <?php /* next line finc specific, adds aria-label */?> + <?php /* finc adds aria-label */?> <div class="slider-container"> <input type="text" class="hidden" id="<?=$this->escapeHtmlAttr($this->title)?><?=$this->escapeHtml($this->facet['type'])?>Slider" aria-label="<?=$this->transEsc('Range-from-to')?>"/></div> <?php endif; ?> <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Set')?>"/> </form> +</li> + +<?php /* finc adds aria-labelledby to script below */?> <?php if ($this->facet['type'] == 'date'): ?> <?php $this->headScript()->appendFile('vendor/bootstrap-slider.min.js'); ?> <?php $this->headLink()->appendStylesheet('vendor/bootstrap-slider.min.css'); ?> @@ -64,7 +71,7 @@ $(document).ready(function() { }) .on('change', fillTexts) .data('slider'); - + // finc: set aria-labelledby by id #18936 $(".slider-handle.min-slider-handle").attr("aria-labelledby", "from-label"); $(".slider-handle.max-slider-handle").attr("aria-labelledby", "to-label"); }); @@ -81,10 +88,8 @@ $('#{$this->escapeHtmlAttr($this->title)}from, #{$this->escapeHtmlAttr($this->ti true ); }); - JS; ?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> <?php endif; ?> -</li> <!-- finc - recommend - sidefacets - rangeslider - END --> diff --git a/themes/finc/templates/Recommend/SideFacets/single-facet.phtml b/themes/finc/templates/Recommend/SideFacets/single-facet.phtml index 19a4e9d85ea922fc6b11fac9da6e4e341c524c68..b798a0513aae1e3506a7a25e4bcf645cc8f85326 100644 --- a/themes/finc/templates/Recommend/SideFacets/single-facet.phtml +++ b/themes/finc/templates/Recommend/SideFacets/single-facet.phtml @@ -13,6 +13,12 @@ if ($this->facet['isApplied']) { $classList[] = 'active'; } + if ($this->facet['operator'] == 'OR') { + $classList[] = 'facetOR'; + } + if ($this->facet['operator'] == 'AND') { + $classList[] = 'facetAND'; + } $displayText = '-'; if (!empty($this->facet['displayText'])) { @@ -21,7 +27,7 @@ $displayText = $this->escapeHtml($this->facet['value']); } - // #18509 decorate special untranslated terms with language tags + // #18509 mark special untranslated terms with language tags $displayText = $this->sideFacet()->getLanguageTag($displayText, 'span', $this->layout()->userLang); if ($this->facet['operator'] == 'OR') { @@ -30,6 +36,7 @@ . $displayText; } ?> +<?php /* finc uses list structure and adds skip-to feature + accessibility code; see #17934 and #18933 */ ?> <?php if ($hasSubLinks): ?> <li class="<?=implode(' ', $classList) ?>"> <?php else: ?> @@ -42,10 +49,12 @@ data-count="<?=$this->facet['count'] ?>" title="<?php if($this->facet['isApplied']): ?><?=$this->transEsc('applied_filter')?> - <?=$this->transEsc('page_reload_on_deselect_hint', ['%%filter_name%%' => $this->facet['displayText']])?><?php else: ?><?=$this->transEsc('page_reload_on_select_hint', ['%%filter_name%%' => $this->facet['displayText']])?><?php endif;?>" data-lightbox-ignore> -<?php endif; ?> + <?php endif; ?> - <?php if ($hasSubLinks): ?> - <a class="text" + <?php if ($hasSubLinks): ?> + <?php /* finc: add landmarks for active facets; add linebreaks #18993 */ + /* finc: add span inside a-element for explanatory sr-only text to facets #19934 */ ?> + <a class="text" href="<?=$toggleUrl ?>" data-lightbox-ignore data-title="<?=$this->escapeHtmlAttr($this->facet['displayText']) ?>" @@ -58,25 +67,27 @@ </a> <?php else: ?> <span class="text"> + <?php /* finc: add explanatory sr-only text to facets #19934 */ ?> <?=$displayText ?><span class="sr-only"><?php if($this->facet['isApplied']): ?>(<?=$this->transEsc('applied_filter')?> - <?=$this->transEsc('page_reload_on_deselect_hint', ['%%filter_name%%' => $this->facet['displayText']])?>)<?php else: ?>(<?=$this->transEsc('page_reload_on_select_hint', ['%%filter_name%%' => $this->facet['displayText']])?>)<?php endif;?></span> </span> <?php endif; ?> <?php if (!$this->facet['isApplied']): ?> <span class="badge"> + <?php /* finc: add span #18993 */ ?> <?=$this->localizedNumber($this->facet['count']) ?><span class="sr-only"><?=$this->transEsc('results') ?></span> </span> <?php endif; ?> + <?php /* finc: simplify if by using $hasSubLinks #17934 */ ?> <?php if ($hasSubLinks): ?> <?php $excludeURL = $this->urlBase . $this->url->addFacet($this->group, $this->facet['value'], 'NOT'); ?> + <?php /* finc: Barf for exclude facets #19392 */ ?> <a href="<?=$excludeURL ?>" data-lightbox-ignore class="exclude" title="<?= $this->transEsc('exclude_filter', ['%%filter_name%%' => $this->facet['displayText']]) ?>, <?= $this->transEsc('page_reload_on_exclude_hint', ['%%filter_name%%' => $this->facet['displayText']]) ?>"> <i class="fa fa-times" aria-hidden="true"></i> - <span class="sr-only"> - <?= $this->transEsc('exclude_filter', ['%%filter_name%%' => $this->facet['displayText']]) ?>, <?= $this->transEsc('page_reload_on_exclude_hint', ['%%filter_name%%' => $this->facet['displayText']]) ?> - </span> + <span class="sr-only"><?=$this->transEsc('exclude_filter', ['%%filter_name%%' => $this->facet['displayText']]) ?>, <?= $this->transEsc('page_reload_on_exclude_hint', ['%%filter_name%%' => $this->facet['displayText']]) ?></span> </a> <?php endif; ?> - +<?php /* finc: use lists for facet groups #17934 */ ?> <?=$hasSubLinks ? '</li>' : '</a></li>'; ?> -<!-- finc - Recommend - SideFacets - single-facet - END --> \ No newline at end of file +<!-- finc - Recommend - SideFacets - single-facet - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/collection-info.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/collection-info.phtml index 92ecdad6b01d42a23dcdaf4ba0f0bcfcbdaae3e8..4954642870cee2bc67ad5796441fc4386e806314 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/collection-info.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/collection-info.phtml @@ -1,19 +1,22 @@ <!-- finc: recordDriver - DefaultRecord - collection-info --> <?php $this->headScript()->appendFile('collection_record.js'); ?> <div class="media"> - <?php $QRCode = $this->record($this->driver)->getQRCode("core"); - $coverDetails = $this->record($this->driver)->getCoverDetails('collection-info', 'medium', $this->record($this->driver)->getThumbnail('large')); - $cover = $coverDetails['html']; - $preview = $this->record($this->driver)->getPreviews(); ?> + <?php + $QRCode = $this->record($this->driver)->getQRCode("core"); + $coverDetails = $this->record($this->driver)->getCoverDetails('collection-info', 'medium', $this->record($this->driver)->getThumbnail('large')); + $cover = $coverDetails['html']; + $preview = $this->record($this->driver)->getPreviews(); + ?> <?php if ($QRCode || $cover || $preview): ?> <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?>"> <?php /* Display thumbnail if appropriate: */ ?> - <?php if ($cover): ?> + <?php if($cover): ?> <?=$cover?> <?php endif; ?> <?php /* Display qrcode if appropriate: */ ?> - <?php if ($QRCode): ?> + <?php if($QRCode): ?> + <?php /* finc removes span + class for accessibility */ ?> <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> <?php endif; ?> @@ -25,30 +28,29 @@ <?php /* finc: we want to get rid of trailing special chars in the title and limit its length to 100 chars */ ?> <h2><?=$this->escapeHtml(preg_replace('/(\s[\/\.:]\s*)*$/', '', $this->truncate($this->driver->getShortTitle() . ' ' . $this->driver->getSubtitle() . ' ' . $this->driver->getTitleSection(), 100)))?></h2> - <?php $summary = $this->driver->getSummary(); - $summary = $summary[0] ?? false; ?> + <?php $summary = $this->driver->getSummary(); $summary = $summary[0] ?? false; ?> <?php if ($summary): ?> <p><?=$this->escapeHtml($summary)?></p> <?php endif; ?> <?php /* Display the lists that this record is saved to */ ?> - <p class="savedLists hidden alert alert-info" id="savedLists" role="alert"> + <?php /* finc changes div to p for alerts; adds aria-live */ ?> + <p class="savedLists" id="savedLists" aria-live="polite"> <strong><?=$this->transEsc("Saved in")?>:</strong> </p> - <?php /* finc-specific snippet - Begin - CK */ - /* Do not set to 'hidden', make table below collapse in; adapt collection_record.js to keep accordion open - CK */ ?> + <?php /* finc removes 'hidden' and make table below 'collapse in'; requires adaptation of collection_record.js to keep accordion open - CK */ ?> <a id="moreInfoToggle" href="#" class="accordion-toggler collapsed hidden" data-toggle="collapse" data-target="#collectionInfo" aria-expanded="true"> <?=$this->transEsc('Description')?> </a> <?php /* Display Main Details */ ?> <?php - $formatter = $this->recordDataFormatter(); - $fields = $formatter->getData($driver, $formatter->getDefaults('collection-info')); + $formatter = $this->recordDataFormatter(); + $fields = $formatter->getData($driver, $formatter->getDefaults('collection-info')); ?> <?php if (!empty($fields)): ?> - <?php /* finc: we use 'collapse in' to initially hide the content, - we also include responsive data table - CK */ ?> + <?php /* finc uses 'collapse in' to initially hide the content, + finc also adds code for responsive data table here - CK */ ?> <table id="collectionInfo" class="table table-striped table-resp-data collapse in"> <caption><span class="sr-only"><?=$this->transEsc('Bibliographic Details')?></span></caption> <?php foreach ($fields as $current): ?> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/collection-record.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/collection-record.phtml index d6bed8b7ffb3b6681eec400f79466cbb06a4491d..0e5bb5d2d2cf8a9ce46189b00c93b86def5de253 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/collection-record.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/collection-record.phtml @@ -7,9 +7,9 @@ $fields = $formatter->getData($driver, $formatter->getDefaults('collection-record')); ?> <?php if (!empty($fields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> - <caption><span class="sr-only"><?=$this->transEsc('Bibliographic Details')?></span></caption> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> <?php foreach ($fields as $current): ?> <tr><th><?=$this->transEsc($current['label'])?>:</th><td data-title="<?= $this->transEsc($current['label']) ?>:"><?=$current['value']?></td></tr> <?php endforeach; ?> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/core.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/core.phtml index 9abc699ae869985f0db0ae5a4d0b8cf35fb6c73d..29ef0a26d87e18a1ffa087a22038980785df9954 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/core.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/core.phtml @@ -1,69 +1,84 @@ <!-- finc: RecordDriver - DefaultRecord - core --> -<div class="media" vocab="http://schema.org/" resource="#record" typeof="<?= $this->driver->getSchemaOrgFormats() ?> Product"> - <?php /* finc: use VF5.1 offcanvas toggler here as well if you have a custom sidebar - CK */ ?> +<?php $this->metadata()->generateMetatags($this->driver);?> +<div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> + <?php /* Use VF5.1 offcanvas toggler here, if you have a custom sidebar - CK */ ?> <?= $this->render('RecordDriver/DefaultRecord/offcanvas-toggler'); ?> <?php - $QRCode = $this->record($this->driver)->getQRCode("core"); - $coverDetails = $this->record($this->driver)->getCoverDetails('core', 'medium', $this->record($this->driver)->getThumbnail('large')); - $cover = $coverDetails['html']; - $preview = $this->record($this->driver)->getPreviews(); + $QRCode = $this->record($this->driver)->getQRCode("core"); + $coverDetails = $this->record($this->driver)->getCoverDetails('core', 'medium', $this->record($this->driver)->getThumbnail('large')); + $cover = $coverDetails['html']; + $preview = ($this->previewOverride ?? false) + ? $this->previewOverride : $this->record($this->driver)->getPreviews(); ?> <?php if ($QRCode || $cover || $preview): ?> - <div class="media-left <?= $this->escapeHtmlAttr($coverDetails['size']) ?> img-col"> + <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"> <?php /* Display thumbnail if appropriate: */ ?> - <?php if ($cover): ?> + <?php if($cover): ?> <?=$cover?> <?php endif; ?> - <?php /* Display qrcode if appropriate: */ ?> - <?php if ($QRCode): ?> - <br/><img alt="<?= $this->transEsc('QR Code') ?>" class="qrcode" src="<?= $this->escapeHtmlAttr($QRCode); ?>"/> + <?php /* Display qrcode if appropriate: */ + /* finc removes span + class for accessibility */ ?> + <?php if($QRCode): ?> + <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> <?php endif; ?> <?php // if you have a preview tab but want to move or remove the preview link - // from this area of the record view, this can be split into - // getPreviewData() (should stay here) and - // getPreviewLink() (can go in your desired tab)?> + // from this area of the record view, this can be split into + // getPreviewData() (should stay here) and + // getPreviewLink() (can go in your desired tab) ?> <?php if ($preview): ?> <div class="record-previews"> - <?= $preview ?> + <?=$preview?> </div> <?php endif; ?> </div> + <?php /* finc uses special code for style-based icons or covers here*/ ?> <?php elseif ($this->record($this->driver)->showStyleBasedIcons()): ?> <?php /* BOF - finc-specific StyleBasedIcons */ ?> <div class="media-left <?= $this->escapeHtmlAttr($coverDetails['size']) ?> img-col"> <?= $this->record($this->driver)->getRecordIcon() ?> </div> <?php endif; ?> - <div class="media-body"> <?php /* finc: We want to get rid of trailing special chars in the title and limit its length to 100 chars; - in finc: keep schema name tag here!! #13861 - CK - */ - ?> - <?php /* finc: add schema tags for title #13850 - VE */ ?> + in finc: keep schema name tag here!! #13861 - CK + finc adds schema tag for title #13850 - VE */ ?> <h1 property="name" lang=""><?=$this->record($this->driver)->getTitleHtml()?></h1> - <?php /* #18307 remove summary from core */ ?> + <?php if(!empty($this->extraControls)): ?> + <?=$this->extraControls['actionControls'] ?? ''?> + <?=$this->extraControls['availabilityInfo'] ?? ''?> + <?php endif; ?> + + <?php /* #18307 finc removes summary from core */ /* + <?php $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> + <?php if ($summary): ?> + <p><?=$this->truncate($summary, 300)?></p> + + <?php if(strlen($summary) > 300): ?> + <p><a href='<?=$this->recordLink()->getTabUrl($this->driver, 'Description')?>#tabnav'><?=$this->transEsc('Full description')?></a></p> + <?php endif; ?> + <?php endif; ?> + */ ?> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php /* Display the lists that this record is saved to */ ?> - <p class="savedLists hidden alert alert-info"> - <strong><?= $this->transEsc("Saved in") ?>:</strong> + <p class="savedLists" aria-live="polite"> + <strong><?=$this->transEsc("Saved in")?>:</strong> </p> <?php endif; ?> <?php /* Display Main Details */ ?> <?php - $formatter = $this->recordDataFormatter(); - $coreFields = $formatter->getData($driver, $formatter->getDefaults('core')); + $formatter = $this->recordDataFormatter(); + $coreFields = $formatter->getData($driver, $formatter->getDefaults('core')); ?> <?php if (!empty($coreFields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> - <caption><span class="sr-only"><?= $this->transEsc('Bibliographic Details') ?></span></caption> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> <?php foreach ($coreFields as $current): ?> <tr> <th><?= $this->transEsc($current['label']) ?>:</th> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-authors.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-authors.phtml index 7fd72a9503a1776656d50eee812ec001bfd6620f..804c5edc41c4b5f4f17121f425f8d27006e58818 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-authors.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-authors.phtml @@ -18,6 +18,7 @@ $types = [ 'corporate_secondary' => 'contributor' ]; ?> +<?php /* finc adds 'foreach' for $types + schema code */ ?> <?php foreach ($types as $type => $schemaLabel): ?> <?php if (!empty($data[$type])): ?> <?php foreach ($data[$type] as $author => $roles): ?> @@ -46,12 +47,13 @@ $types = [ ?> </span> <?php -// Strip whitespace before close tags to avoid spaces in front of commas: + // Strip whitespace before close tags to avoid spaces in front of commas: $formattedAuthors[] = trim(preg_replace('/\s+<\//', '</', ob_get_contents())); ob_end_clean(); ?> <?php endforeach; ?> <?php endif; ?> <?php endforeach; ?> +<?php /* finc adds $separator, see #18292 */ ?> <?=implode($separator ?? ', ', $formattedAuthors)?> <!-- finc: RecordDriver - DefaultRecord - data-authors - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml index eb5d8f54d3a56038382988d43813a4314ca734d2..524d217eb497ce827b3be0e121b486ed08e1f8da 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-publicationDetails.phtml @@ -4,6 +4,7 @@ <span property="publisher" typeof="Organization"> <?php $pubPlace = $field->getPlace(); if (!empty($pubPlace)): ?> + <?php /* finc adds schema code */ ?> <span property="location" typeof="Place"> <span property="name"><?=$this->escapeHtml($pubPlace)?></span> </span> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/data-transEscCommaSepLang.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/data-transEscCommaSepLang.phtml index 6a7ad4457838b650205d78c9ac619bd2c3b6ed2c..edf294db8ef401a421a4aee0ee523c74f3ebebb7 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/data-transEscCommaSepLang.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/data-transEscCommaSepLang.phtml @@ -1,7 +1,7 @@ <!-- finc: RecordDriver - DefaultRecord - data-transEscCommaSepLang --> <?php if (!empty($data)): ?> <?php if (is_array($data)): ?> - <?php /* finc: add schema tags for language #13850 - VE */ ?> + <?php /* finc adds schema tags for language #13850 - VE */ ?> <span property="inLanguage"> <?=implode('</span>, <span property="inLanguage">', array_map(array($this, 'transEsc'), $data));?> </span> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/format-list.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/format-list.phtml index b70be7192cb570010bced119d1eb8391fb35ca14..6203c7cc2e163a9be3f0eb34bcaf7ebc0dbb2e4c 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/format-list.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/format-list.phtml @@ -1,3 +1,4 @@ +<!-- finc: RecordDriver - DefaultRecord - format-list --> <?php $formats = $this->driver->getFormats(); $translated_formats = []; foreach ($formats as $format): ?> @@ -5,11 +6,11 @@ foreach ($formats as $format): ?> $translated_formats[] = $translated_format = $this->transEsc($format); $formatClass = $this->record($this->driver)->getFormatClass($format); ?> - <?php /* @deprecated: use SideFacets Helper to decorate language tags, remove when there's a VuFind core solution */ ?> + <?php /* @deprecated: use SideFacets Helper to style language tags, remove when there's a VuFind core solution */ ?> <?=$this->sideFacet()->getLanguageTag($translated_format, 'span', $this->layout()->userLang, ["class" => "format $formatClass"]);?> <?php endforeach; ?> -<?php /* New genre string to display nxt to format in RESULT LIST and RECORD/Detail View, #11703, limit to SID 0 CK */ ?> -<?php /* Removes (uncommented) new genre string to display with k10plus index in RESULT LIST and RECORD/Detail View, #14912 FM */ ?> +<?php /* New genre string to display nxt to format in RESULT LIST and RECORD/Detail View, #11703, limit to SID 0 CK */ + /* Removes (uncommented) new genre string to display with k10plus index in RESULT LIST and RECORD/Detail View, #14912 FM */ ?> <?php /* $sourceID = $this->driver->tryMethod('getSourceID'); ?> <?php if ($sourceID == "0"): ?> <?php $genres = $this->driver->tryMethod('getGenreFacet'); ?> @@ -24,3 +25,4 @@ foreach ($formats as $format): ?> <?php endforeach; ?> <?php endif; ?> <?php endif; */ ?> +<!-- finc: RecordDriver - DefaultRecord - format-list - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml index 2d07543c0f738edbcb4136bb2217e3ba985a6731..f4e2c30343ff92bfc573938293db511978f75c5f 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/list-entry.phtml @@ -1,23 +1,24 @@ <!-- finc: RecordDriver - DefaultRecord - list-entry --> <?php -// Set up some convenience variables: -$id = $this->driver->getUniqueId(); -$source = $this->driver->getSourceIdentifier(); -if (isset($this->list) && is_object($this->list)) { + // Set up some convenience variables: + $id = $this->driver->getUniqueId(); + $source = $this->driver->getSourceIdentifier(); + if (isset($this->list) && is_object($this->list)) { $list_id = $this->list->id; $user_id = $this->list->user_id; -} else { + } else { $list_id = null; $user_id = $this->user ? $this->user->id : null; -} -// finc: next line finc-specific; required to display public favorites lists, #12052, see also below - CK -$isEditable = $this->user && $this->user->id === $user_id; -// Thumbnail -$coverDetails = $this->record($this->driver)->getCoverDetails('list-entry', 'medium', $this->recordLink()->getUrl($this->driver)); -$cover = $coverDetails['html']; -$thumbnail = false; -$thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list'); + } + // finc: next line required to display public favorites lists, #12052, see also below - CK + $isEditable = $this->user && $this->user->id === $user_id; + // Thumbnail + $coverDetails = $this->record($this->driver)->getCoverDetails('list-entry', 'medium', $this->recordLink()->getUrl($this->driver)); + $cover = $coverDetails['html']; + $thumbnail = false; + $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list'); ?> +<?php /* finc uses li structure + its own image/cover loading mechanism + adds aria */ ?> <li class="result<?php if ($this->driver->supportsAjaxStatus()): ?> ajaxItem<?php endif ?>"> <?php if ($cover): ob_start(); ?> @@ -40,24 +41,30 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' <header> <div class="resultItemLine1"> <?php $missing = $this->driver instanceof \VuFind\RecordDriver\Missing; ?> - <?php if ($missing && $this->driver->isCachedRecord()): ?> - <?php $describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId(); ?> - <span id="<?=$describedById?>" class="title" lang=""><?=$this->record($this->driver)->getTitleHtml()?></span> - <p class="alert alert-info"> + <?php /* finc: restore missing favorites #17375 */ ?> + <?php if ($missing && $this->driver->isCachedRecord()): ?> + <?php /* finc: add aria-label and aria-describedby #18019 */ ?> + <?php $describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId(); ?> + <span id="<?=$describedById?>" class="title" lang=""><?=$this->record($this->driver)->getTitleHtml()?></span> + <?php /* finc uses <p> and aria for alerts */ ?> + <p class="alert alert-info" aria-live="polite"> <?= $this->translate('record_from_cache')?> <?php if ($queryParams = $this->record($this->driver)->getAdvancedSearchQueryParams()): ?> <?php /* finc: add h2 for record title #22158 */ ?> <h2> <a href="<?=$this->url('search-results', [], ['query' => $queryParams])?>"><?=$this->transEsc('search_cached_record', ['%%title_full%%' => $this->driver->getTitle()])?></a> </h2> - <?php endif; ?> - </p> - <?php elseif (!$missing): ?> - <?php $describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId(); ?> - <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"> - <span id="<?=$describedById?>" class="title" lang=""><?=$this->record($this->driver)->getTitleHtml()?></span> - </a> - <?php endif;?> + <?php endif; ?> + </p> + <?php elseif (!$missing): ?> + <?php /* finc adds aria-lable and aria-describedby #18019 */ ?> + <?php $describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId(); ?> + <a href="<?=$this->recordLink()->getUrl($this->driver)?>" class="getFull" data-view="<?=$this->params->getOptions()->getListViewOption() ?>"> + <?php /* finc: change span to h2 element #22158 */ ?> + <h2 id="<?=$describedById?>" class="title" lang=""><?=$this->record($this->driver)->getTitleHtml()?></h2> + </a> + <?php endif; ?> + <?php /* finc: restore missing favorites #17375 - END */ ?> </div> <div class="resultItemLine2"> @@ -88,8 +95,8 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' if (false && !empty($summInCollection)): ?> <?php foreach ($summInCollection as $collId => $collText): ?> <div> - <b><?=$this->transEsc("in_collection_label")?></b> - <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <strong><?=$this->transEsc("in_collection_label")?></strong> + <a class="collectionLinkText" href="<?=$this->record($this->driver)->getLink('collection', $collid)?>"> <?=$this->escapeHtml($collText)?> </a> </div> @@ -113,7 +120,7 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' <?php $listTags = ($this->usertags()->getMode() !== 'disabled') ? $this->driver->getTags( null === $list_id ? true : $list_id, // get tags for all lists if no single list is selected $user_id, 'tag' - ) : []; + ) : []; ?> <?php if (count($listTags) > 0): ?> <strong><?=$this->transEsc('Your Tags')?>:</strong> @@ -131,6 +138,7 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' <?php endforeach; ?> <?php endif; ?> + <?php /* finc: avoid call of count() on null #16122 */ ?> <?php if (!empty($this->lists)): ?> <strong><?=$this->transEsc('Saved in')?>:</strong> <?php $i = 0; @@ -152,54 +160,52 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' </span> <div class="locationDetails"></div> <?php else: ?> - <?php $summCallNo = $this->driver->getCallNumber(); - if (!empty($summCallNo)): ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> <?php endif; ?> <?php endif; ?> </div> <?php /* 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->openUrl($this->driver, 'results'); - $openUrlActive = $openUrl->isActive(); - $doi = $this->doi($this->driver, 'results'); - $doiActive = $doi->isActive(); - // Account for replace_other_urls setting - $urls = $this->record($this->driver)->getLinkDetails($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->openUrl($this->driver, 'results'); + $openUrlActive = $openUrl->isActive(); + $doi = $this->doi($this->driver, 'results'); + $doiActive = $doi->isActive(); + // Account for replace_other_urls setting + $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); - if ($openUrlActive || $doiActive || !empty($urls)): - ?> + if ($openUrlActive || $doiActive || !empty($urls)): + ?> <?php if ($openUrlActive): ?> - <br/> - <?=$openUrl->renderTemplate()?> - <?php endif; ?> + <br/> + <?=$openUrl->renderTemplate()?> + <?php endif;?> <?php if ($doiActive): ?> - <br/> - <?=$doi->renderTemplate()?> - <?php endif; ?> + <br/> + <?=$doi->renderTemplate()?> + <?php endif; ?> - <?php if (!is_array($urls)) { - $urls = []; - } - if (!$this->driver->isCollection()): - foreach ($urls as $current): ?> + <?php if (!is_array($urls)) { $urls = []; } + if(!$this->driver->isCollection()): + foreach ($urls as $current): ?> + <?php /* finc: add external link view helper #20869 */ ?> <?= $this->externalLink( - $this->escapeHtmlAttr($this->proxyUrl($current['url'])), - '<i class="fa fa-external-link" aria-hidden="true"></i>' - . ($current['url'] == $current['desc'] ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])), - ['class' => 'fulltext'], - true - ) ?> - <?php endforeach; ?> + $this->escapeHtmlAttr($this->proxyUrl($current['url'])), + '<i class="fa fa-external-link" aria-hidden="true"></i>' + . ($current['url'] == $current['desc'] ? $this->transEsc('Get full text') : $this->escapeHtml($current['desc'])), + ['class' => 'fulltext'], + true + ) ?> + <?php endforeach; ?> + <?php endif; ?> <?php endif; ?> - <?php endif; ?> <br/> - <?=$this->record($this->driver)->getFormatList()?> + <?=$this->record($this->driver)->getFormatList() ?> <?php if (!$openUrlActive && empty($urls) && $this->driver->supportsAjaxStatus()): ?> <span class="status ajax-availability hidden"><?=$this->transEsc('Loading')?>...</span> @@ -210,27 +216,27 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' </div> <div class="result-links hidden-print"> - <?php /* finc: next line finc-specific; required to display public favorites lists, #12052, see also above - CK */ ?> - <?php if ($isEditable): ?> - <i class="fa fa-fw fa-edit" aria-hidden="true"></i> <a - href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&source=<?=urlencode($source)?><?php if (null !== $list_id): ?>&list_id=<?=urlencode($list_id)?><?php endif; ?>" - class="edit tool" aria-label="<?=$this->transEsc('Edit').': '.$this->record($this->driver)->getTitleHtml()?>"><?=$this->transEsc('Edit')?></a><br/> + <?php /* finc: next line required to display public favorites lists, #12052, see also above - CK */ ?> + <?php if ($isEditable): ?> + <i class="fa fa-fw fa-edit" aria-hidden="true"></i> + <a href="<?=$this->url('myresearch-edit')?>?id=<?=urlencode($id)?>&source=<?=urlencode($source)?><?php if (null !== $list_id):?>&list_id=<?=urlencode($list_id)?><?php endif; ?>" class="edit tool" + <?php /* finc: add aria-label #17993 */ ?> + aria-label="<?=$this->transEsc('Edit').': '.$this->record($this->driver)->getTitleHtml()?>"><?=$this->transEsc('Edit')?></a><br/> <?php /* Use a different delete URL if we're removing from a specific list or the overall favorites: */ - $deleteUrl = null === $list_id - ? $this->url('myresearch-favorites') - : $this->url('userList', ['id' => $list_id]); - $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&source=' . urlencode($source); - - /* #17712 not necessary to fetch items after deleting by ajax in controller */ - $deleteUrl .= '?layout=lightbox'; - $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id); + $deleteUrl = null === $list_id + ? $this->url('myresearch-favorites') + : $this->url('userList', ['id' => $list_id]); + $deleteUrlGet = $deleteUrl . '?delete=' . urlencode($id) . '&source=' . urlencode($source); + + /* #17712 not necessary to fetch items after deleting by ajax in controller */ + $deleteUrl .= '?layout=lightbox'; + $dLabel = 'delete-label-' . preg_replace('[\W]', '-', $id); ?> <div class="dropdown"> - <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> - <a class="dropdown-toggle del-button" id="<?= $dLabel ?>" - role="button" data-toggle="dropdown" - href="<?= $deleteUrlGet ?>" aria-label="<?=$this->transEsc('Delete').': '.$this->record($this->driver)->getTitleHtml()?>"> - <?= $this->transEsc('Delete') ?> + <i class="fa fa-fw fa-trash-o" aria-hidden="true"></i> <a class="dropdown-toggle del-button" id="<?=$dLabel ?>" role="button" data-toggle="dropdown" href="<?=$deleteUrlGet ?>" + <?php /* finc: add aria-label #17993 */ ?> + aria-label="<?=$this->transEsc('Delete').': '.$this->record($this->driver)->getTitleHtml()?>"> + <?=$this->transEsc('Delete') ?> </a> <ul class="dropdown-menu" role="menu" aria-labelledby="<?= $dLabel ?>"> <li> @@ -242,12 +248,9 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' } VuFind.lightbox.setOrigin(next); $.post( - '<?= $deleteUrl ?>', - { - 'delete':'<?= $this->escapeJs($id) ?>', - 'source':'<?= $this->escapeJs($source) ?>', - 'confirm':true - }, function(){ + '<?=$deleteUrl?>', + {'delete':'<?=$this->escapeJs($id) ?>','source':'<?=$this->escapeJs($source) ?>','confirm':true}, + function(){ $('input[value=<?=$id?>]').parent().remove(0); <?php /* reset needed for possible old MyResearchController::mylistAction in instances withot clearMessages */ ?> <?php if (null === $list_id) :?> @@ -258,25 +261,24 @@ $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('list' }).fail(function(data) { $('.fa-spinner.fa-spin').removeClass('fa-spinner fa-spin').addClass('fa-trash-o'); VuFind.lightbox.alert( - '<?= $this->transEsc('Delete') . ' ' . $this->transEsc('of') . ' ' . htmlspecialchars($this->record($this->driver)->getTitleHtml()) . ': ' . $this->transEsc('errorcode_error')?>', + '<?=$this->transEsc('Delete') . ' ' . $this->transEsc('of') . ' ' . htmlspecialchars($this->record($this->driver)->getTitleHtml()) . ': ' . $this->transEsc('errorcode_error')?>', 'danger' ) }); $(this).closest('.dropdown').find('.fa-trash-o').removeClass('fa-trash-o').addClass('fa-spinner fa-spin');"> - <?= $this->transEsc('confirm_dialog_yes') ?> + <?=$this->transEsc('confirm_dialog_yes')?> </a> </li> <li><a href="javascript:document.getElementById('<?=$dLabel?>').focus();"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> - - <?=$this->driver->supportsCoinsOpenUrl() ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>' : ''?> + + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> <?php endif; ?> </div> </div> - <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> - <?=$thumbnail?> + <?=$thumbnail ?> <?php endif; ?> </div> </li> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml index e444addd9f72f6d878e6286bedba3e239ad40853..b38be85280c4e81562a4c53ad0a70d4b1d889167 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/result-list.phtml @@ -5,6 +5,7 @@ $coverDetails = $this->record($this->driver)->getCoverDetails('result-list', 'me $cover = $coverDetails['html']; $thumbnail = false; $thumbnailAlignment = $this->record($this->driver)->getThumbnailAlignment('result'); + /* finc adds $describedById #18737 */ $describedById = $driver->getSourceIdentifier() . '|' . $driver->getUniqueId(); if ($cover): ob_start(); ?> @@ -35,7 +36,8 @@ if ($cover): <?php /* finc: add header element for title and author; use h2 for record title; #19396 */ ?> <header> <h2> - <a id="<?=$describedById?>" href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull" data-view="<?=$this->params->getOptions()->getListViewOption()?>" lang=""> + <?php /* finc adds aria and lang code */ ?> + <a id="<?=$describedById?>" href="<?=$this->recordLink()->getUrl($this->driver)?>" class="title getFull" data-view="<?=$this->params->getOptions()->getListViewOption()?>" lang=""> <?=$this->record($this->driver)->getTitleHtml()?> </a> </h2> @@ -45,8 +47,7 @@ if ($cover): <div> <?=implode('<br>', array_map([$this, 'escapeHtml'], $this->driver->getSummary())); ?> <?php else: ?> - <?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); - if (!empty($summAuthors)): ?> + <?php $summAuthors = $this->driver->getPrimaryAuthorsWithHighlighting(); if (!empty($summAuthors)): ?> <?=$this->transEsc('by')?> <?php $authorCount = count($summAuthors); foreach ($summAuthors as $i => $summAuthor): ?> @@ -67,19 +68,18 @@ if ($cover): <?php $containerID = $this->driver->getContainerRecordID(); ?> <?php /* TODO: handle highlighting more elegantly here: */ ?> <a href="<?=($containerID ? $this->recordLink()->getUrl("$containerSource|$containerID", $journalTitle, 'JournalTitle') : $this->record($this->driver)->getLink('journaltitle', str_replace(['{{{{START_HILITE}}}}', '{{{{END_HILITE}}}}'], '', $journalTitle)))?>"><?=$this->highlight($journalTitle) ?></a> - <?php /* finc-specific: nxt line #8639 - CK */ ?> + <?php /* finc removes [0] in nxt line #8639 - CK */ ?> <?=!empty($summDate) ? ' (' . $this->escapeHtml($summDate) . ')' : ''?> <?php elseif (!empty($summDate)): ?> <?php /* finc: remove linebreak #19396 */ ?> - <?php /* finc-specific: nxt line #8639 - CK */ ?> + <?php /* finc removes [0] in nxt line #8639 - CK */ ?> <?=$this->transEsc('Published') . ' ' . $this->escapeHtml($summDate)?> <?php endif; ?> - <?php $summInCollection = $this->driver->getContainingCollections(); - if (!empty($summInCollection)): ?> + <?php $summInCollection = $this->driver->getContainingCollections(); if (!empty($summInCollection)): ?> <?php foreach ($summInCollection as $collId => $collText): ?> <div> <strong><?=$this->transEsc("in_collection_label")?></strong> - <a class="collectionLinkText" href="<?=$this->url('collection', ['id' => $collId])?>?recordID=<?=urlencode($this->driver->getUniqueID())?>"> + <a class="collectionLinkText" href="<?=$this->record($this->driver)->getLink('collection', $collId)?>"> <?=$this->escapeHtml($collText)?> </a> </div> @@ -99,35 +99,61 @@ if ($cover): <?php endif; ?> <?php endif; ?> + <?php + /* Display information on duplicate records if available */ + /* finc: remove dedupData */ /* + if ($dedupData = $this->driver->getDedupData()): ?> + <div class="dedupInformation"> + <?php + $i = 0; + foreach ($dedupData as $source => $current) { + if (++$i == 1) { + ?><span class="currentSource"><a href="<?=$this->recordLink()->getUrl($this->driver)?>"><?=$this->transEsc("source_$source", [], $source)?></a></span><?php + } else { + if ($i == 2) { + ?> <span class="otherSources">(<?=$this->transEsc('Other Sources')?>: <?php + } else { + ?>, <?php + } + ?><a href="<?=$this->recordLink()->getUrl($current['id'])?>"><?=$this->transEsc("source_$source", [], $source)?></a><?php + } + } + if ($i > 1) { + ?>)</span><?php + }?> + </div> + <?php endif; ?> + */ ?> + + <?php /* finc adds ' ' for correct ellipsis formatting */ ?> <div class="callnumAndLocation ajax-availability hidden"> <?php if ($this->driver->supportsAjaxStatus()): ?> <strong class="hideIfDetailed"><?=$this->transEsc('Call Number')?>:</strong> <span class="callnumber ajax-availability hidden"> - <?=$this->transEsc('Loading')?> ...<br/> - </span> + <?=$this->transEsc('Loading')?> ...<br/> + </span> <strong><?=$this->transEsc('Located')?>:</strong> <span class="location ajax-availability hidden"> - <?=$this->transEsc('Loading')?> ... - </span> + <?=$this->transEsc('Loading')?> ... + </span> <div class="locationDetails"></div> <?php else: ?> - <?php $summCallNo = $this->driver->getCallNumber(); - if (!empty($summCallNo)): ?> + <?php $summCallNo = $this->driver->getCallNumber(); if (!empty($summCallNo)): ?> <strong><?=$this->transEsc('Call Number')?>:</strong> <?=$this->escapeHtml($summCallNo)?> <?php endif; ?> <?php endif; ?> </div> <?php /* 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->openUrl($this->driver, 'results'); - $openUrlActive = $openUrl->isActive(); - $doi = $this->doi($this->driver, 'results'); - $doiActive = $doi->isActive(); - // Account for replace_other_urls setting - $urls = $this->record($this->driver)->getLinkDetails($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->openUrl($this->driver, 'results'); + $openUrlActive = $openUrl->isActive(); + $doi = $this->doi($this->driver, 'results'); + $doiActive = $doi->isActive(); + // Account for replace_other_urls setting + $urls = $this->record($this->driver)->getLinkDetails($openUrlActive); if ($openUrlActive || $doiActive || !empty($urls)): ?> <?php if ($openUrlActive): ?> @@ -135,12 +161,14 @@ if ($cover): <?=$openUrl->renderTemplate()?> <?php endif; ?> <?php if ($doiActive): ?> - <br/> - <?=$doi->renderTemplate()?> - <?php endif; ?> + <br/> + <?=$doi->renderTemplate()?> + <?php endif; ?> + <?php /* finc adds external link view helper */ ?> <?php if (!is_array($urls)) $urls = []; - if (!$this->driver->isCollection()): - foreach ($urls as $current): ?> + if(!$this->driver->isCollection()): + foreach ($urls as $current): ?> + <?php /* finc: add external link view helper */ ?> <?= $this->externalLink( $this->escapeHtmlAttr($this->proxyUrl($current['url'])), '<i class="fa fa-external-link" aria-hidden="true"></i>' @@ -154,13 +182,13 @@ if ($cover): <?php endif; ?> <div class="result-formats"> - <?=$this->record($this->driver)->getFormatList()?> + <?=$this->record($this->driver)->getFormatList() ?> - <?php /* nxt line finc-specific: #5737 removed '(!$openUrlActive && empty($urls) &&' - CK */ ?> + <?php /* #5737 removes '(!$openUrlActive && empty($urls) &&' - CK */ ?> <?php if ($this->driver->supportsAjaxStatus()): ?> <span class="status ajax-availability hidden"> - <span class="label label-default"><?=$this->transEsc('Loading')?> ...</span> - </span> + <span class="label label-default"><?=$this->transEsc('Loading')?> ...</span> + </span> <?php endif; ?> </div> @@ -175,9 +203,10 @@ if ($cover): // Add JS Variables for QrCode $this->jsTranslations()->addStrings(['qrcode_hide' => 'qrcode_hide', 'qrcode_show' => 'qrcode_show']); ?> - <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> + <?php /* finc removes span + class for accessibility */ ?> + <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> <div class="qrcode hidden"> - <script type="text/template" class="qrCodeImgTag"> + <script class="qrCodeImgTag"> <img alt="<?=$this->transEsc('QR Code')?>" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> </script> </div> @@ -185,31 +214,36 @@ if ($cover): <?php endif; ?> <?php if ($this->cart()->isActiveInSearch() && $this->params->getOptions()->supportsCart() && $this->cart()->isActive()): ?> - <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]);?><br/> + <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?><br/> <?php endif; ?> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> - <?php /* Add to favorites; finc: keep Icon inside link - CK */ ?> + <?php /* Add to favorites; + finc: keep Icon inside link + finc adds accessibility code */ ?> <a href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>?refreshOnClose=false" data-lightbox class="save-record result-link-label" data-id="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" aria-label="<?=$this->transEsc('Add to favorites')?>"> - <i class="fa fa-fw fa-star" aria-hidden="true"></i> <span><?=$this->transEsc('Add to favorites')?></span> + <i class="fa fa-fw fa-star" aria-hidden="true"></i> + <span><?=$this->transEsc('Add to favorites')?></span> </a><br/> <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> <?=$block?> <?php endif; ?> <?php /* Saved lists */ ?> - <p class="savedLists alert alert-info hidden" role="alert"> + <p class="savedLists" aria-live="polite"> <strong><?=$this->transEsc("Saved in")?>:</strong> </p> <?php endif; ?> - <?php /* Hierarchy tree link; finc: keep Icon inside link - CK */ ?> + <?php /* Hierarchy tree link; + finc: keep Icon inside link; + finc adds its own styles and formatting - CK */ ?> <?php $trees = $this->driver->tryMethod('getHierarchyTrees'); if (!empty($trees)): ?> <?php foreach ($trees as $hierarchyID => $hierarchyTitle): ?> <div class="hierarchyTreeLink"> - <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId"/> - <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchyID)?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab')?>?hierarchy=<?=urlencode($hierarchyID)?>" data-lightbox-post="tab=hierarchytree"> + <input type="hidden" value="<?=$this->escapeHtmlAttr($hierarchyID)?>" class="hiddenHierarchyId" /> + <a class="hierarchyTreeLinkText result-link-label" data-lightbox href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchyID])?>#tabnav" title="<?=$this->transEsc('hierarchy_tree')?>" data-lightbox-href="<?=$this->recordLink()->getTabUrl($this->driver, 'AjaxTab', ['hierarchy' => $hierarchyID])?>" data-lightbox-post="tab=hierarchytree"> <i class="result-link-icon fa fa-fw fa-sitemap" aria-hidden="true"></i> <span><?=$this->transEsc('hierarchy_view_context')?></span><?php if (count($trees) > 1): ?>: <?=$this->escapeHtml($hierarchyTitle)?><?php endif; ?> </a> @@ -217,11 +251,11 @@ if ($cover): <?php endforeach; ?> <?php endif; ?> - <?=$this->driver->supportsCoinsOpenUrl() ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>' : ''?> + <?=$this->driver->supportsCoinsOpenUrl()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenUrl()) . '"></span>':''?> </div> </div> <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> - <?=$thumbnail?> + <?=$thumbnail ?> <?php endif ?> </div> <!-- finc: recordDriver - DefaultRecord - result-list - END --> diff --git a/themes/finc/templates/RecordDriver/DefaultRecord/toolbar.phtml b/themes/finc/templates/RecordDriver/DefaultRecord/toolbar.phtml index 4c5e138851d32ffff6f1932891bfa04b40e8a1f1..b7a05b12b7666b1266eef683f277ddf19b8ab408 100644 --- a/themes/finc/templates/RecordDriver/DefaultRecord/toolbar.phtml +++ b/themes/finc/templates/RecordDriver/DefaultRecord/toolbar.phtml @@ -5,23 +5,20 @@ $this->headScript()->appendFile('https://s7.addthis.com/js/250/addthis_widget.js?pub=' . urlencode($addThis)); } - // Set up some variables for convenience: + // finc sets up some variables for bookbag button: $cart = $this->cart(); $cartId = $this->driver->getSourceIdentifier() . '|' . $this->driver->getUniqueId(); ?> -<?php /* finc: add toggler-off button, CK */ ?> -<button class="close-offcanvas btn btn-primary" data-toggle="offcanvas"><?= $this->transEsc('navigate_back') ?></button> -<?php /* finc: we use the sr-only description, CK */ ?> -<?php /* finc: we use nav-stacked for display in sidebar, CK */ - /* Added label instead of heading and removed duplicate aria on ul - CK */ -?> <nav class="record-nav hidden-print" aria-label="<?= $this->transEsc('Toolbar') ?>"> + <?php /* finc adds toggler-off button, CK */ ?> + <button class="close-offcanvas btn btn-primary" data-toggle="offcanvas"><?= $this->transEsc('navigate_back') ?></button> + <?php /* finc: we use nav-stacked for display in sidebar, CK */ + /* should we remove role="none" in li-elements?? */ + ?> <ul class="nav nav-pills nav-stacked hidden-print"> <?php if (count($this->driver->getCitationFormats()) > 0): ?> - <li> - <a class="cite-record" data-lightbox href="<?= $this->recordLink()->getActionUrl($this->driver, 'Cite') ?>" rel="nofollow"><i class="fa fa-asterisk" aria-hidden="true"></i> <?= $this->transEsc('Cite this') ?></a> - </li> + <li><a class="cite-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Cite')?>" rel="nofollow"><i class="fa fa-asterisk" aria-hidden="true"></i> <?=$this->transEsc('Cite this')?></a></li> <?php endif; ?> <?php if ($this->tabs['Details'] && $this->config()->get('config')->Site->showStaffViewInLightbox ?? false): ?> @@ -35,47 +32,47 @@ <?=$this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'staff_view_button.js', 'SET');?> </li> <?php endif; ?> - <?php /* finc: we don't use sms, CK */ - /* - <?php if ($this->accountCapabilities()->getSmsSetting() !== 'disabled'): ?> - <li><a class="sms-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'SMS')?>" rel="nofollow"><i class="fa fa-mobile" aria-hidden="true"></i> <?=$this->transEsc('Text this')?></a></li> - <?php endif; ?> - */ ?> - <li><a class="mail-record" data-lightbox href="<?= $this->recordLink()->getActionUrl($this->driver, 'Email') ?>" rel="nofollow"><i class="fa fa-envelope" aria-hidden="true"></i> <?= $this->transEsc('Email this') ?></a></li> - + <?php /* finc: we don't use sms, CK + <?php if ($this->accountCapabilities()->getSmsSetting() !== 'disabled'): ?> + <li role="none"><a class="sms-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'SMS')?>" rel="nofollow"><i class="fa fa-mobile" aria-hidden="true"></i> <?=$this->transEsc('Text this')?></a></li> + <?php endif; ?> + */ ?> + <li role="none"><a class="mail-record" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Email')?>" rel="nofollow"><i class="fa fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Email this')?></a></li> <?php $exportFormats = $this->export()->getFormatsForRecord($this->driver); ?> - <?php if (count($exportFormats) > 0): ?> - <li class="dropdown"> - <a class="export-toggle dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" href="<?= $this->recordLink()->getActionUrl($this->driver, 'Export') ?>" rel="nofollow" aria-expanded="false" aria-controls="export-options"><i class="fa fa-list-alt" aria-hidden="true"></i> <?= $this->transEsc('Export Record') ?></a> + <?php if(count($exportFormats) > 0): ?> + <li role="none" class="dropdown"> + <?php /* finc: remove classname toolbar-btn */ ?> + <a class="export-toggle btn-type-export dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>" rel="nofollow" aria-expanded="false" aria-controls="export-options"><?=$this->transEsc('Export Record') ?></a> <ul class="dropdown-menu" id="export-options" role="menu"> <?php foreach ($exportFormats as $exportFormat): ?> - <li> - <a <?php if ($this->export()->needsRedirect($exportFormat)): ?>target="<?= $this->escapeHtmlAttr($exportFormat) ?>Main" - <?php endif; ?>href="<?= $this->recordLink()->getActionUrl($this->driver, 'Export') ?>?style=<?= $this->escapeHtmlAttr($exportFormat) ?>" rel="nofollow" role="menuitem"> - <?= $this->transEsc('Export to') ?><?= $this->transEsc($this->export()->getLabelForFormat($exportFormat)) ?> + <li role="none"> + <a <?php if ($this->export()->needsRedirect($exportFormat)): ?>target="<?=$this->escapeHtmlAttr($exportFormat)?>Main" <?php endif; ?>href="<?=$this->recordLink()->getActionUrl($this->driver, 'Export')?>?style=<?=$this->escapeHtmlAttr($exportFormat)?>" rel="nofollow" role="menuitem"> + <?=$this->transEsc('Export to')?> <?=$this->transEsc($this->export()->getLabelForFormat($exportFormat))?> </a> </li> <?php endforeach; ?> </ul> </li> <?php endif; ?> - + <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php /* finc: we use title=... in link below, CK */ ?> - <li> + <?php /* finc: title was set in #14450 and removed in #15055 */ ?> + <li role="none"> <?php if ($this->permission()->allowDisplay('feature.Favorites')): ?> - <a class="save-record" data-lightbox href="<?= $this->recordLink()->getActionUrl($this->driver, 'Save') ?>" rel="nofollow"><i class="fa fa-star" aria-hidden="true"></i> <?= $this->transEsc('Add to favorites') ?> - </a> + <?php /* finc: remove classname toolbar-btn */ ?> + <a class="save-record btn-type-save" data-lightbox href="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" rel="nofollow"><?=$this->transEsc('Add to favorites')?></a> <?php elseif ($block = $this->permission()->getAlternateContent('feature.Favorites')): ?> - <?= $block ?> + <?=$block?> <?php endif; ?> </li> <?php endif; ?> <?php if (!empty($addThis)): ?> - <li><a class="addThis addthis_button" href="https://www.addthis.com/bookmark.php?v=250&pub=<?= urlencode($addThis) ?>"><i class="fa fa-bookmark" aria-hidden="true"></i> <?= $this->transEsc('Bookmark') ?></a></li> + <li role="none"><a class="addThis addthis_button" href="https://www.addthis.com/bookmark.php?v=250&pub=<?=urlencode($addThis)?>"><i class="fa fa-bookmark" aria-hidden="true"></i> <?=$this->transEsc('Bookmark')?></a></li> <?php endif; ?> - <li class="bookbag-menu"> - <?= $this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?> + <?php /* finc adds bookbag feature */ ?> + <li role="none" class="bookbag-menu"> + <?=$this->render('record/cart-buttons.phtml', ['id' => $this->driver->getUniqueId(), 'source' => $this->driver->getSourceIdentifier()]); ?> </li> </ul> </nav> diff --git a/themes/finc/templates/RecordDriver/SolrAI/core.phtml b/themes/finc/templates/RecordDriver/SolrAI/core.phtml index 359a61233d04241c69c4871c4a512a080a7aac9e..14671cc288a798c203e54e6961c634188ecf62e1 100644 --- a/themes/finc/templates/RecordDriver/SolrAI/core.phtml +++ b/themes/finc/templates/RecordDriver/SolrAI/core.phtml @@ -1,7 +1,7 @@ <!-- finc: recordDriver - SolrAI - core --> <?php /* Created in 4826, based on DefaultRecord/core, compare with DefaultRecord/core during updates! */ ?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?= $this->driver->getSchemaOrgFormats() ?> Product"> - <?php /* finc: use VF5.1 offcanvas toggler here as well if you have a custom sidebar - CK */ ?> + <?php /* Use VF5.1 offcanvas toggler here, if you have a custom sidebar - CK */ ?> <?= $this->render('RecordDriver/DefaultRecord/offcanvas-toggler'); ?> <?php $QRCode = $this->record($this->driver)->getQRCode("core"); @@ -16,7 +16,8 @@ <?=$cover?> <?php endif; ?> - <?php /* Display qrcode if appropriate: */ ?> + <?php /* Display qrcode if appropriate: */ + /* finc removes span + class for accessibility */ ?> <?php if ($QRCode): ?> <br/><img alt="<?= $this->transEsc('QR Code') ?>" class="qrcode" src="<?= $this->escapeHtmlAttr($QRCode); ?>"/> <?php endif; ?> @@ -31,6 +32,7 @@ </div> <?php endif; ?> </div> + <?php /* finc uses special code for style-based icons or covers here*/ ?> <?php elseif ($this->record($this->driver)->showStyleBasedIcons()): ?> <?php /* BOF - finc-specific StyleBasedIcons */ ?> <div class="media-left <?= $this->escapeHtmlAttr($coverDetails['size']) ?> img-col"> @@ -40,17 +42,24 @@ <div class="media-body"> <?php /* finc: We want to get rid of trailing special chars in the title and limit its length to 100 chars; - remove schema name tag here but keep in description, CK - */ - ?> - <?php /* finc: add schema tags for title #13850 - VE */ ?> + in finc: keep schema name tag here!! #13861 - CK + finc adds schema tag for title #13850 - VE */ ?> <h1 property="name" lang=""><?=$this->record($this->driver)->getTitleHtml()?></h1> + + <?php /* #18307 finc removes summary from core */ /* + <?php $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> + <?php if ($summary): ?> + <p><?=$this->truncate($summary, 300)?></p> - <?php /* #18307 remove summary from core */ ?> + <?php if(strlen($summary) > 300): ?> + <p><a href='<?=$this->recordLink()->getTabUrl($this->driver, 'Description')?>#tabnav'><?=$this->transEsc('Full description')?></a></p> + <?php endif; ?> + <?php endif; ?> + */ ?> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php /* Display the lists that this record is saved to */ ?> - <p class="savedLists hidden alert alert-info"> + <p class="savedLists" aria-live="polite"> <strong><?= $this->transEsc("Saved in") ?>:</strong> </p> <?php endif; ?> @@ -61,9 +70,9 @@ $coreFields = $formatter->getData($driver, $formatter->getDefaults('core-ai')); ?> <?php if (!empty($coreFields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> - <caption><span class="sr-only"><?= $this->transEsc('Bibliographic Details') ?></span></caption> + <caption class="sr-only"><?= $this->transEsc('Bibliographic Details') ?></caption> <?php foreach ($coreFields as $current): ?> <?php if ($current['label'] == null): ?> <?= $current['value'] ?> diff --git a/themes/finc/templates/RecordDriver/SolrAI/data-jTitle.phtml b/themes/finc/templates/RecordDriver/SolrAI/data-jTitle.phtml index 3d22ec4d5be98a477657be64e84d2a39267f2184..76e2a0ec7883a5c4db87c89469247891fe963cec 100644 --- a/themes/finc/templates/RecordDriver/SolrAI/data-jTitle.phtml +++ b/themes/finc/templates/RecordDriver/SolrAI/data-jTitle.phtml @@ -26,7 +26,7 @@ foreach ($methods as $method) { } } ?> -<?php /* finc: add schema tags for parent publication #13850 - VE */ ?> +<?php /* finc adds schema tag for parent publication #13850 - VE */ ?> <span property="isPartOf" typeof="Periodical" resource="#periodical"> <span property="name"> <?=implode(', ', $jtitle)?> diff --git a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml index 5c6d087734902e76972133d50d5f854754cf88c9..e8a4f8dd13ca87118de6041a0e013c3f3840eebe 100644 --- a/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml +++ b/themes/finc/templates/RecordDriver/SolrAI/result-list.phtml @@ -189,7 +189,7 @@ if ($cover): ?> <i class="fa fa-fw fa-qrcode" aria-hidden="true"></i> <a href="<?=$this->escapeHtmlAttr($QRCode);?>" class="qrcodeLink"><?=$this->transEsc('qrcode_show')?></a> <span class="qrcode hidden"> - <script type="text/template" class="qrCodeImgTag"> + <script class="qrCodeImgTag"> <img alt="<?=$this->transEsc('QR Code')?>" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> </script> </span> @@ -210,7 +210,7 @@ if ($cover): <?=$block?> <?php endif; ?> <?php /* Saved lists */ ?> - <p class="savedLists alert alert-info hidden"> + <p class="savedLists" aria-live="polite"> <strong><?=$this->transEsc("Saved in")?>:</strong> </p> <?php /* endif; */ ?> diff --git a/themes/finc/templates/RecordDriver/SolrLido/core.phtml b/themes/finc/templates/RecordDriver/SolrLido/core.phtml index c9be4f471cf0df95583da70f102eb81fd170a4a2..f071513b6f84ba0e1056f26e9c23e2446e440628 100644 --- a/themes/finc/templates/RecordDriver/SolrLido/core.phtml +++ b/themes/finc/templates/RecordDriver/SolrLido/core.phtml @@ -3,7 +3,7 @@ /* based on DefaultRecord/core, compare with DefaultRecord/core during updates! // keep schema tagging here in finc - CK, #13861 */ ?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> - <?php /* finc: use VF5.1 offcanvas toggler here as well if you have a custom sidebar - CK */ ?> + <?php /* Use VF5.1 offcanvas toggler here, if you have a custom sidebar - CK */ ?> <?=$this->render('RecordDriver/DefaultRecord/offcanvas-toggler'); ?> <?php if ($loggedin = $this->auth()->isLoggedIn()) { @@ -30,7 +30,7 @@ $preview = $this->record($this->driver)->getPreviews(); ?> <?php if ($QRCode || $cover || $preview): ?> - <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"?>"> + <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"?> <?php /* Display thumbnail if appropriate: */ ?> <?php if ($cover): ?> <?=$cover?> @@ -40,7 +40,8 @@ <?php /* EOF - finc-specific StyleBasedIcons */ ?> <?php endif; ?> - <?php /* Display qrcode if appropriate: */ ?> + <?php /* Display qrcode if appropriate: */ + /* finc removes span + class for accessibility */ ?> <?php if ($QRCode): ?> <br/><img alt="<?=$this->transEsc('QR Code')?>" class="qrcode" src="<?=$this->escapeHtmlAttr($QRCode);?>"/> <?php endif; ?> @@ -59,8 +60,8 @@ <div class="media-body"> <?php /* finc: We want to get rid of trailing special chars in the title and limit its length to 100 chars; - in finc: keep schema name tag here!! #13861 CK - */ ?> + in finc: keep schema name tag here!! #13861 - CK + finc adds schema tag for title #13850 - VE */ ?> <h1 property="name" lang=""><?=$this->record($this->driver)->getTitleHtml()?></h1> <?php $summary = $this->driver->getSummary(); @@ -75,7 +76,7 @@ <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php /* Display the lists that this record is saved to */ ?> - <p class="savedLists hidden alert alert-info"> + <p class="savedLists" aria-live="polite"> <strong><?=$this->transEsc("Saved in")?>:</strong> </p> <?php endif; ?> @@ -86,9 +87,9 @@ $coreFields = $formatter->getData($driver, $formatter->getDefaults('core-lido')); ?> <?php if (!empty($coreFields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> - <caption><span class="sr-only"><?=$this->transEsc('Bibliographic Details')?></span></caption> + <caption class="sr-only"><?=$this->transEsc('Bibliographic Details')?></caption> <?php foreach ($coreFields as $current): ?> <?php if ($current['label'] == null): ?> <?=$current['value']?> diff --git a/themes/finc/templates/RecordDriver/SolrMarc/core.phtml b/themes/finc/templates/RecordDriver/SolrMarc/core.phtml index 84d81c2f2238955e2edfb43de1fcd7e4e4098146..3cf2987092bda6858b6bd5e9af34e3ea0f4ace92 100644 --- a/themes/finc/templates/RecordDriver/SolrMarc/core.phtml +++ b/themes/finc/templates/RecordDriver/SolrMarc/core.phtml @@ -1,7 +1,7 @@ <!-- finc: RecordDriver - solrMarc - core --> <?php /* based on DefaultRecord/core, compare with DefaultRecord/core during updates! */ ?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?= $this->driver->getSchemaOrgFormats() ?> Product"> - <?php /* finc: use VF5.1 offcanvas toggler here as well if you have a custom sidebar - CK */ ?> + <?php /* Use VF5.1 offcanvas toggler here, if you have a custom sidebar - CK */ ?> <?= $this->render('RecordDriver/DefaultRecord/offcanvas-toggler'); ?> <?php $QRCode = $this->record($this->driver)->getQRCode("core"); @@ -16,7 +16,8 @@ <?=$cover?> <?php endif; ?> - <?php /* Display qrcode if appropriate: */ ?> + <?php /* Display qrcode if appropriate: */ + /* finc removes span + class for accessibility */ ?> <?php if ($QRCode): ?> <br/><img alt="<?= $this->transEsc('QR Code') ?>" class="qrcode" src="<?= $this->escapeHtmlAttr($QRCode); ?>"/> <?php endif; ?> @@ -31,6 +32,7 @@ </div> <?php endif; ?> </div> + <?php /* finc uses special code for style-based icons or covers here*/ ?> <?php elseif ($this->record($this->driver)->showStyleBasedIcons()): ?> <?php /* BOF - finc-specific StyleBasedIcons */ ?> <div class="media-left <?= $this->escapeHtmlAttr($coverDetails['size']) ?> img-col"> @@ -41,16 +43,23 @@ <?php /* finc: We want to get rid of trailing special chars in the title and limit its length to 100 chars; in finc: keep schema name tag here!! #13861 - CK - */ - ?> - <?php /* finc: add schema tags for title #13850 - VE */ ?> + finc adds schema tag for title #13850 - VE */ ?> <h1 property="name" lang=""><?=$this->record($this->driver)->getTitleHtml()?></h1> + + <?php /* #18307 finc removes summary from core */ /* + <?php $summary = $this->driver->getSummary(); $summary = isset($summary[0]) ? $this->escapeHtml($summary[0]) : false; ?> + <?php if ($summary): ?> + <p><?=$this->truncate($summary, 300)?></p> - <?php /* #18307 remove summary from core */ ?> + <?php if(strlen($summary) > 300): ?> + <p><a href='<?=$this->recordLink()->getTabUrl($this->driver, 'Description')?>#tabnav'><?=$this->transEsc('Full description')?></a></p> + <?php endif; ?> + <?php endif; ?> + */ ?> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> <?php /* Display the lists that this record is saved to */ ?> - <p class="savedLists hidden alert alert-info"> + <p class="savedLists" aria-live="polite"> <strong><?= $this->transEsc("Saved in") ?>:</strong> </p> <?php endif; ?> @@ -61,9 +70,9 @@ $coreFields = $formatter->getData($driver, $formatter->getDefaults('core-marc')); ?> <?php if (!empty($coreFields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> - <caption><span class="sr-only"><?= $this->transEsc('Bibliographic Details') ?></span></caption> + <caption class="sr-only"><?= $this->transEsc('Bibliographic Details') ?></caption> <?php foreach ($coreFields as $current): ?> <?php if ($current['label'] == null): ?> <?= $current['value'] ?> diff --git a/themes/finc/templates/RecordTab/acquisitionpda.phtml b/themes/finc/templates/RecordTab/acquisitionpda.phtml index e069017a899ee0ec5596fda3bb820e5e9df2dbb3..86a3ceee54c9364b90c76bb8e95365651bdf931a 100644 --- a/themes/finc/templates/RecordTab/acquisitionpda.phtml +++ b/themes/finc/templates/RecordTab/acquisitionpda.phtml @@ -12,7 +12,7 @@ $controllerClass = 'controller:SolrMarcFincPDA'; ?> <h2><?=$this->transEsc('PDA::Acquisition')?></h2> <p><?=$this->transEsc('PDA::pda_initial_text')?></p> -<p class="alert alert-info"><?=$this->transEsc('PDA::pda_restriction_text')?></p> +<p class="alert alert-info" aria-live="polite"><?=$this->transEsc('PDA::pda_restriction_text')?></p> <div class="btn-group"> <?php /* Leave title in here - it is used for the tooltip! - CK */ ?> <?= $this->externalLink( diff --git a/themes/finc/templates/RecordTab/description.phtml b/themes/finc/templates/RecordTab/description.phtml index 0e39ff89ba9bad658a112800f42272aa2d5adde1..92b169d11fe8efa88d93f763aa6a17fbb07cbd43 100644 --- a/themes/finc/templates/RecordTab/description.phtml +++ b/themes/finc/templates/RecordTab/description.phtml @@ -1,19 +1,22 @@ <!-- finc: recordtab - description --> <?php -// Set page title. -$this->headTitle($this->translate('Description') . ': ' . $this->driver->getBreadcrumb()); + // Set page title. + $this->headTitle($this->translate('Description') . ': ' . $this->driver->getBreadcrumb()); -$formatter = $this->recordDataFormatter(); -$mainFields = $formatter->getData($driver, $formatter->getDefaults('description')); + $formatter = $this->recordDataFormatter(); + $mainFields = $formatter->getData($driver, $formatter->getDefaults('description')); ?> -<?php /* finc: remove schema tag here but keep in core, include responsive data table - CK */ ?> +<?php /* finc removes schema tag here but keeps it in core, + finc adds code for responsive data table + plus accessibility code here - CK */ ?> <table class="table table-striped table-resp-data"> <?php - /* Table gets filled via recordDriver/.../data-... templates as referenced in RecordDataFormatterFactory */ ?> - <caption><span class="sr-only"><?=$this->transEsc('Description')?></span></caption> + /* finc: this table gets filled via recordDriver/.../data-... templates as referenced in RecordDataFormatterFactory */ ?> + <caption class="sr-only"><?=$this->transEsc('Description')?></caption> <?php if (!empty($mainFields)): ?> <?php foreach ($mainFields as $current): ?> <tr> + <?php /* finc: handel missing label #17434 */ ?> <?php if (!empty($current['label'])): ?> <th><?=$this->transEsc($current['label'])?>:</th> <td lang="" data-title="<?= $this->transEsc($current['label']) ?>:"> diff --git a/themes/finc/templates/RecordTab/descriptionlido.phtml b/themes/finc/templates/RecordTab/descriptionlido.phtml index 939b171f95eefcc3f9f00317a898dafd25cf85b9..cb5b32723e52ac9487fbef40537dca3dccdb31ea 100644 --- a/themes/finc/templates/RecordTab/descriptionlido.phtml +++ b/themes/finc/templates/RecordTab/descriptionlido.phtml @@ -8,7 +8,7 @@ $mainFields = $formatter->getData($driver, $formatter->getDefaults('description- ?> <?php /* This next table originally in table "Display Main Details" in DefaultRecord/core templates, compare with core and with description.phtml! - finc: remove schema tag here but keep in core, include responsive data table - CK */ + finc: remove schema tag here but keep in core, finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> <caption><span class="sr-only"><?= $this->transEsc('Description') ?></span></caption> diff --git a/themes/finc/templates/RecordTab/hierarchytree.phtml b/themes/finc/templates/RecordTab/hierarchytree.phtml index 0c5f7f1879a63203bbb26f691421c32928f8dad5..50ef83fc7f6f07f89bfbdbd7336b1bdfc81c9af4 100644 --- a/themes/finc/templates/RecordTab/hierarchytree.phtml +++ b/themes/finc/templates/RecordTab/hierarchytree.phtml @@ -23,13 +23,14 @@ ?> <?php if (count($hierarchyTreeList) > 1): ?> <div id="treeSelector"> + <?php /* finc adds hierarchyTreeSelect #14068 */ ?> <strong><?=$this->transEsc('hierarchyTreeSelect')?></strong><br> <?php foreach ($hierarchyTreeList as $hierarchy => $hierarchyTitle): ?> <?php if($activeTree == $hierarchy): ?> <i class="fa fa-sitemap" aria-hidden="true"></i> <?=$this->escapeHtml($hierarchyTitle)?> <?php else: ?> - <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree')?>?hierarchy=<?=urlencode($hierarchy)?>"><?=$this->escapeHtml($hierarchyTitle)?></a> + <i class="fa fa-sitemap text-muted" aria-hidden="true"></i> <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'HierarchyTree', ['hierarchy' => $hierarchy])?>"><?=$this->escapeHtml($hierarchyTitle)?></a> <?php endif; ?> <?php endforeach; ?> </div> @@ -43,23 +44,26 @@ <option value="AllFields"><?=$this->transEsc('All Fields')?></option> <option value="Title"><?=$this->transEsc('Title')?></option> </select> + <?php /* finc changes btn-default to btn-transparent */ ?> <input type="submit" class="btn btn-transparent" value="<?=$this->transEsc('Search') ?>"/> <i id="treeSearchLoadingImg" class="fa fa-spinner fa-spin hidden" aria-hidden="true"></i> </div> - <p id="treeSearchNoResults" class="alert alert-danger hidden"><?=$this->translate('nohit_heading')?></p> - <p id="treeSearchLimitReached" class="alert alert-danger hidden"><?=$this->translate('tree_search_limit_reached_html', ['%%url%%' => $this->url('search-results'), '%%limit%%' => $this->tab->getSearchLimit()])?></p> + <?php /* finc uses <p> and aria for alerts */ ?> + <p id="treeSearchNoResults" class="alert alert-danger hidden" aria-live="polite"><?=$this->translate('nohit_heading')?></p> + <p id="treeSearchLimitReached" class="alert alert-danger hidden" aria-live="polite"><?=$this->translate('tree_search_limit_reached_html', ['%%url%%' => $this->url('search-results'), '%%limit%%' => $this->tab->getSearchLimit()])?></p> <?php endif; ?> <div id="hierarchyLoading" class="hide"><i class="fa fa-spinner fa-spin" aria-hidden="true"></i> <?=$this->transEsc("Loading")?>...</div> <div id="hierarchyTree" class="hierarchy-tree"> <input type="hidden" value="<?=$this->escapeHtml($this->driver->getUniqueId())?>" class="hiddenRecordId" /> <input type="hidden" value="<?=$this->escapeHtml($activeTree)?>" class="hiddenHierarchyId" /> + <input type="hidden" value="<?=$this->escapeHtml($this->driver->getSourceIdentifier())?>" class="hiddenHierarchySource" /> <input type="hidden" value="<?=isset($this->treeContext) ? $this->treeContext : 'Record'?>" class="hiddenContext" /> <?php if ($this->layout()->getTemplate() != 'layout/lightbox'): ?> <noscript> <?php if ($this->config()->nonJavascriptSupportEnabled()): ?> - <ul class="fa-ul"> - <?=$this->tab->renderTree($this->url('home'))?> - </ul> + <ul class="fa-ul"> + <?=$this->tab->renderTree($this->url('home'))?> + </ul> <?php else: ?> <?=$this->transEsc('Please enable JavaScript.')?> <?php endif; ?> diff --git a/themes/finc/templates/RecordTab/holdingsils.phtml b/themes/finc/templates/RecordTab/holdingsils.phtml index 392dd55a860ef27148f0782b7d3ea375283f8c8c..df070aafc0ca0926f007439518b481c3b801c46b 100644 --- a/themes/finc/templates/RecordTab/holdingsils.phtml +++ b/themes/finc/templates/RecordTab/holdingsils.phtml @@ -21,7 +21,13 @@ try { $holdings = $this->driver->getRealTimeHoldings(); } catch (\VuFind\Exception\ILS $e) { - $holdings = ['holdings' => []]; + $holdings = [ + 'holdings' => [], + 'electronic_holdings' => [], + 'total' => 0, + 'page' => 0, + 'itemLimit' => 0 + ]; $offlineMode = 'ils-offline'; } } @@ -34,37 +40,40 @@ <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]);?> +<?php /* finc uses p for alerts + role */ ?> <?php if (!empty($holdings['blocks'])): ?> - <p id="account-block-msg" class="alert alert-danger"> + <p id="account-block-msg" class="alert alert-danger" role="alert"> <?=$this->transEsc('account_block_options_missing', ['%%details%%' => implode('; ', $holdings['blocks'])])?> </p> <?php endif; ?> <?=($offlineMode == "ils-offline") ? $this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message']) : ''?> + <?php if (($this->ils()->getHoldsMode() == 'driver' && !empty($holdings['holdings'])) || $this->ils()->getTitleHoldsMode() == 'driver'): ?> <?php if ($account->loginEnabled() && $offlineMode != 'ils-offline'): ?> <?php if (!$user): ?> - <p class="alert alert-info"> - <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'Holdings')?>?login=true&catalogLogin=true" data-lightbox><?=$this->transEsc("hold_login")?></a> + <p class="alert alert-info" aria-live="polite"> + <a href="<?=$this->recordLink()->getTabUrl($this->driver, 'Holdings', ['login' => 'true', 'catalogLogin' => 'true'])?>" data-lightbox><?=$this->transEsc("hold_login")?></a> </p> <?php elseif (!$user->cat_username): ?> - <p class="alert alert-info"> - <?=$this->translate("hold_profile_html", ['%%url%%' => $this->recordLink()->getTabUrl($this->driver, 'Holdings') . '?catalogLogin=true'])?> + <p class="alert alert-info" aria-live="polite"> + <?=$this->translate("hold_profile_html", ['%%url%%' => $this->recordLink()->getTabUrl($this->driver, 'Holdings', ['catalogLogin' => 'true'])])?> </p> <?php endif; ?> <?php endif; ?> <?php endif; ?> <?php $holdingTitleHold = $this->driver->tryMethod('getRealTimeTitleHold'); if (!empty($holdingTitleHold)): ?> - <a class="placehold" data-lightbox title="<?=$this->transEsc('request_place_text')?>" href="<?=$this->recordLink()->getRequestUrl($holdingTitleHold)?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc('title_hold_place')?> - </a> + <a class="placehold" data-lightbox title="<?=$this->transEsc('request_place_text')?>" href="<?=$this->recordLink()->getRequestUrl($holdingTitleHold)?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc('title_hold_place')?></a> <?php endif; ?> - -<?php /* finc: add 'Online Access' in holdings-tab #13770 - VE */ ?> +<?php /* finc adds 'Online Access' in holdings-tab #13770 - VE */ + /* finc turns h3 into h3 */ ?> <?php if (!empty($urls) || $openUrlActive || $doiActive): ?> + <?php /* finc uses h2 */ ?> <h2><?=$this->transEsc("Internet")?></h2> <?php if (!empty($urls)): ?> <?php foreach ($urls as $current): ?> + <?php /* finc: use external link view helper #19650 */ ?> <?= $this->externalLink($this->escapeHtmlAttr($this->proxyUrl($current['url'])), $current['desc']) ?><br/> <?php endforeach; ?> <?php endif; ?> @@ -75,6 +84,7 @@ if (!empty($holdingTitleHold)): ?> if (!empty($fallbackUrls)): ?> <span id="urlsHideable" style="display: none"> <?php foreach ($fallbackUrls as $current): ?> + <?php /* finc: use external link view helper #19650 */ ?> <?= $this->externalLink($this->escapeHtmlAttr($this->proxyUrl($current['url'])), $current['desc'] ?? $current['url']) ?><br/> <?php endforeach; ?> </span> @@ -86,13 +96,21 @@ if (!empty($holdingTitleHold)): ?> <?php /* finc-specific snippet - #9274 - END */ ?> <?php endif; ?> +<?php if (!empty($holdings['electronic_holdings'])): ?> + <?=$this->context($this)->renderInContext( + 'RecordTab/holdingsils/electronic.phtml', + ['holdings' => $holdings['electronic_holdings']] + );?> +<?php endif; ?> + <?php foreach ($holdings['holdings'] ?? [] as $holding): ?> <?php /* nxt line = finc-specific - #7841@56988450 - CK */ ?> <?php $holdingsEmpty = false; ?> <?php /* this next line produces an empty h2 heading in some cases - should be solved more elegantly - Fixme - CK */ ?> <h2> - <?php $locationText = $this->transEsc('location_' . $holding['location'], [], $holding['location']); ?> - <?php if (isset($holding['locationhref']) && $holding['locationhref']): ?> + <?php $locationText = $this->transEscWithPrefix('location_', $holding['location']); ?> + <?php if ($holding['locationhref'] ?? false): ?> + <?php /* finc: use external link view helper #19650 */ ?> <?= $this->externalLink($holding['locationhref'], $locationText) ?> <?php else: ?> <?=$locationText?> @@ -104,10 +122,10 @@ if (!empty($holdingTitleHold)): ?> <?=$this->branchInfo($this->driver)->getBranchInfo($holding['locationid']);?> <?php endif; ?> <?php /* finc: this next section produces an empty table in some cases - the table borders collapse, producing a thick line - should be solved more elegantly - Fixme. - include responsive data table - CK + finc adds code for responsive data table here - CK layout tables must not contain caption - CK */ ?> <table class="table table-striped table-resp-data" aria-label="<?=$this->transEsc('holdings_details_from', ['%%location%%' => $this->transEsc($holding['location'])])?>"> - <?php /* finc: change order and structure of table #13606 - VE */ ?> + <?php /* finc changes order and structure of table #13606 - VE */ ?> <?php foreach ($holding['items'] as $row): ?> <?php // finc: nxt line - emailholds #6096 - CK @@ -129,32 +147,30 @@ if (!empty($holdingTitleHold)): ?> We don't use the links to alphabrowse - CK */ ?> <?php $callNos = $this->tab->getUniqueCallNumbers($holding['items']); if (!empty($callNos)): ?> <tr> - <th> - <?=$this->transEsc("Call Number")?>: - </th> + <th><?=$this->transEsc("Call Number")?>: </th> <td data-title="<?= $this->transEsc("Call Number") ?>:"> <?php foreach ($callNos as $callNo): ?> - <?php if ($this->callnumberHandler): ?> - <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($callNo) ?>"><?=$this->escapeHtml($callNo)?></a> - <?php else: ?> - <?=$this->escapeHtml($callNo)?> - <?php endif; ?> + <?php if ($this->callnumberHandler): ?> + <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($callNo) ?>"><?=$this->escapeHtml($callNo)?></a> + <?php else: ?> + <?=$this->escapeHtml($callNo)?> + <?php endif; ?> <br /> <?php endforeach; ?> </td> </tr> <?php endif; ?> - <?php if (isset($holding['textfields'])): foreach ($holding['textfields'] as $textFieldName => $textFields): ?> + <?php foreach ($holding['textfields'] ?? [] as $textFieldName => $textFields): ?> <tr> <?php // Translation for summary is a special case for backwards-compatibility ?> <th><?=$textFieldName == 'summary' ? $this->transEsc("Volume Holdings") : $this->transEsc(ucfirst($textFieldName))?>:</th> <td data-title="<?= $this->transEsc("Volume Holdings") ?>:"> <?php foreach ($textFields as $current): ?> - <?=$this->escapeHtml($current)?><br/> + <?=$this->linkify($this->escapeHtml($current))?><br/> <?php endforeach; ?> </td> </tr> - <?php endforeach; endif; ?> + <?php endforeach; ?> <?php /* finc: we use call numbers after "Notes" - END */ ?> <?php if (!empty($holding['purchase_history'])): ?> <tr> @@ -186,15 +202,16 @@ if (!empty($holdingTitleHold)): ?> <?php endif; ?> <?php /* finc-specific - external access - END */ ?> +<?php if (!empty($holdings['total']) && $paginator = $this->tab->getPaginator($holdings['total'], $holdings['page'], $holdings['itemLimit'])): ?> + <?=$this->paginationControl($paginator, 'Sliding', 'Helpers/pagination.phtml', ['page' => $holdings['page']])?> +<?php endif; ?> <?php $history = $this->driver->getRealTimeHistory(); ?> <?php if (is_array($history) && !empty($history)): ?> <h2><?=$this->transEsc("Most Recent Received Issues")?></h2> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> <?php foreach ($history as $row): ?> - <tr> - <td><?=$this->escapeHtml($row['issue'])?></td> - </tr> + <tr><td><?=$this->escapeHtml($row['issue'])?></td></tr> <?php endforeach; ?> </table> <?php endif; ?> diff --git a/themes/finc/templates/RecordTab/holdingsils/standard.phtml b/themes/finc/templates/RecordTab/holdingsils/standard.phtml index edfcdba974eed2d95c2dd55a7b5729b135ca917a..87229c24aba014a539a494126619a2b2f183ada3 100644 --- a/themes/finc/templates/RecordTab/holdingsils/standard.phtml +++ b/themes/finc/templates/RecordTab/holdingsils/standard.phtml @@ -1,12 +1,12 @@ <!-- finc - recordtab - holdingsils - standard --> -<?php /* This is a new file introduced in VF5 -- it originated from the old holdingsils file, for history, check there, CK */ ?> +<?php /* This is a new file introduced in VF5 - it originated from the old holdingsils file, for history, check there, CK */ ?> <?php if (strlen($holding['barcode'] ?? '') > 0): ?> <?php - $check = $holding['check'] ?? false; - $checkStorageRetrievalRequest = $holding['checkStorageRetrievalRequest'] ?? false; - $checkILLRequest = $holding['checkILLRequest'] ?? false; - // finc: nxt line - emailholds #6096 - CK - $checkEmailHold = (isset($holding['checkEmailHold']) && $holding['checkEmailHold']); + $check = $holding['check'] ?? false; + $checkStorageRetrievalRequest = $holding['checkStorageRetrievalRequest'] ?? false; + $checkILLRequest = $holding['checkILLRequest'] ?? false; + // finc: nxt line - emailholds #6096 - CK + $checkEmailHold = (isset($holding['checkEmailHold']) && $holding['checkEmailHold']); ?> <tr vocab="http://schema.org/" typeof="Offer"> <?php /* finc: remove transEsc("Copy") and number from TH, #13606 - VE */ /* @@ -19,88 +19,83 @@ <td data-title="<?= $this->transEsc('Availability') ?>" class="availability-column"> <?php if ($holding['reserve'] == "Y"): ?> <link property="availability" href="http://schema.org/InStoreOnly"/> - <?= $this->transEsc("On Reserve - Ask at Circulation Desk") ?><br><br> + <?=$this->transEsc("On Reserve - Ask at Circulation Desk")?><br><br> <?php endif; ?> <?php if ($holding['use_unknown_message'] ?? false): ?> - <span class="text-muted"><?= $this->transEsc("status_unknown_message") ?></span> + <span class="text-muted"><?=$this->transEsc("status_unknown_message")?></span> <?php else: ?> - <?php if ($holding['availability'] ?? false): ?> - <?php /* Begin Available Items (Holds) */ ?> - <span class="text-success"><?= $this->transEsc("Available") ?> - <link property="availability" href="http://schema.org/InStock"/> - </span> - <?php if ($holding['link'] ?? false): ?> - <?php /* finc: add class .hidden-print CK */ ?> - <a class="<?= $check ? 'checkRequest ' : '' ?>placehold hidden-print" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?= $this->recordLink()->getRequestUrl($holding['link']) ?>"> - <i class="fa fa-flag" aria-hidden="true"></i> <?= $this->transEsc($check ? "Check Hold" : "Place a Hold") ?> - </a> - <?php endif; ?> - <?php if ($holding['storageRetrievalRequestLink'] ?? false): ?> - <a class="<?= $checkStorageRetrievalRequest ? 'checkStorageRetrievalRequest ' : '' ?> placeStorageRetrievalRequest" data-lightbox href="<?= $this->recordLink()->getRequestUrl($holding['storageRetrievalRequestLink']) ?>"> - <i class="fa fa-flag" aria-hidden="true"></i> <?= $this->transEsc($checkStorageRetrievalRequest ? "storage_retrieval_request_check_text" : "storage_retrieval_request_place_text") ?> - </a> - <?php endif; ?> - <?php if ($holding['ILLRequestLink'] ?? false): ?> - <a class="<?= $checkILLRequest ? 'checkILLRequest ' : '' ?>placeILLRequest" data-lightbox href="<?= $this->recordLink()->getRequestUrl($holding['ILLRequestLink']) ?>"> - <i class="fa fa-flag" aria-hidden="true"></i> <?= $this->transEsc($checkILLRequest ? "ill_request_check_text" : "ill_request_place_text") ?> - </a> - <?php endif; ?> - <?php /* finc-specific additional insert, newspaper orders via mail - #6096 - CK */ ?> - <?php if (isset($holding['emailHoldLink']) && $holding['emailHoldLink']): ?> - <a class="<?= $checkEmailHold ? 'checkEmailHold ' : '' ?>placeEmailHold " data-lightbox href="<?= $this->recordLink()->getRequestUrl($holding['emailHoldLink']) ?>"> - <i class="fa fa-flag"></i> <?= $this->transEsc($checkEmailHold ? "EmailHold::email_hold_check_text" : "EmailHold::email_hold_place_text") ?> - </a> - <?php endif; ?> - <?php /* finc-specific insert - #6096 - END */ ?> - <?php else: ?> - <?php /* Begin Unavailable Items (Recalls) */ ?> - <?php /* finc: use empty status and transEsc 'Unavailable', CK */ ?> - <span class="text-danger"><?= empty($holding['status']) ? $this->transEsc("Unavailable") : $this->transEsc($holding['status']) ?> <link property="availability" href="http://schema.org/OutOfStock"/></span> - <?php if ($holding['returnDate'] ?? false): ?>– <span><?= $this->escapeHtml($holding['returnDate']) ?></span><?php endif; ?> - <?php if ($holding['duedate'] ?? false): ?> - <?php /* finc: keep nbsp + ndash or due date text will bump into alert, CK */ ?> - – <span><?= $this->transEsc("Due") ?>: <?= $this->escapeHtml($holding['duedate']) ?></span> - <?php endif; ?> - <?php if (($holding['requests_placed'] ?? 0) > 0): ?> - <span><?= $this->transEsc("Requests") ?>: <?= $this->escapeHtml($holding['requests_placed']) ?></span> + <?php if ($holding['availability'] ?? false): ?> + <?php /* Begin Available Items (Holds) */ ?> + <span class="text-success"><?=$this->transEsc("Available")?><link property="availability" href="http://schema.org/InStock" /></span> + <?php if ($holding['link'] ?? false): ?> + <?php /* finc adds class '.hidden-print' CK */ ?> + <a class="<?= $check ? 'checkRequest ' : '' ?>placehold hidden-print" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?= $this->recordLink()->getRequestUrl($holding['link']) ?>"> + <i class="fa fa-flag" aria-hidden="true"></i> <?= $this->transEsc($check ? "Check Hold" : "Place a Hold") ?> + </a> + <?php /* finc: add class .hidden-print CK */ ?> + <a class="<?=$check ? 'checkRequest ' : ''?>placehold hidden-print" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Hold" : "Place a Hold")?></a> + <?php endif; ?> + <?php if ($holding['storageRetrievalRequestLink'] ?? false): ?> + <a class="<?=$checkStorageRetrievalRequest ? 'checkStorageRetrievalRequest ' : ''?> placeStorageRetrievalRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['storageRetrievalRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkStorageRetrievalRequest ? "storage_retrieval_request_check_text" : "storage_retrieval_request_place_text")?></a> + <?php endif; ?> + <?php if ($holding['ILLRequestLink'] ?? false): ?> + <a class="<?=$checkILLRequest ? 'checkILLRequest ' : ''?>placeILLRequest" data-lightbox href="<?=$this->recordLink()->getRequestUrl($holding['ILLRequestLink'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($checkILLRequest ? "ill_request_check_text" : "ill_request_place_text")?></a> + <?php endif; ?> + <?php /* finc-specific additional insert, newspaper orders via mail - #6096 - CK */ ?> + <?php if (isset($holding['emailHoldLink']) && $holding['emailHoldLink']): ?> + <a class="<?= $checkEmailHold ? 'checkEmailHold ' : '' ?>placeEmailHold " data-lightbox href="<?= $this->recordLink()->getRequestUrl($holding['emailHoldLink']) ?>"> + <i class="fa fa-flag"></i> <?= $this->transEsc($checkEmailHold ? "EmailHold::email_hold_check_text" : "EmailHold::email_hold_place_text") ?> + </a> + <?php endif; ?> + <?php /* finc-specific insert - #6096 - END */ ?> + <?php else: ?> + <?php /* Begin Unavailable Items (Recalls) */ ?> + <?php /* finc: use empty status and transEsc 'Unavailable', CK */ ?> + <span class="text-danger"><?= empty($holding['status']) ? $this->transEsc("Unavailable") : $this->transEsc($holding['status'])?><link property="availability" href="http://schema.org/OutOfStock" /></span> + <?php if ($holding['returnDate'] ?? false): ?>– <span><?=$this->escapeHtml($holding['returnDate'])?></span><?php endif; ?> + <?php if ($holding['duedate'] ?? false): ?> + <?php /* finc: keep nbsp + ndash or due date text will bump into alert, CK */ ?> + <?php /* finc: remove classname small #14450 */ ?> + – <span><?=$this->transEsc("Due")?>: <?=$this->escapeHtml($holding['duedate'])?></span> + <?php endif; ?> + <?php if (($holding['requests_placed'] ?? 0) > 0): ?> + <span><?=$this->transEsc("Requests")?>: <?=$this->escapeHtml($holding['requests_placed'])?></span> + <?php endif; ?> + <?php if ($holding['link'] ?? false): ?> + <a class="<?=$check ? 'checkRequest' : ''?> placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?=$this->recordLink()->getRequestUrl($holding['link'])?>"><i class="fa fa-flag" aria-hidden="true"></i> <?=$this->transEsc($check ? "Check Recall" : "Recall This")?></a> + <?php endif; ?> + <?=$this->relais()->renderButtonIfActive($this->driver ?? null)?> <?php endif; ?> - <?php if ($holding['link'] ?? false): ?> - <a class="<?= $check ? 'checkRequest' : '' ?> placehold" <?php if (!empty($holding['linkLightbox'])): ?>data-lightbox <?php endif; ?>href="<?= $this->recordLink()->getRequestUrl($holding['link']) ?>"> - <i class="fa fa-flag" aria-hidden="true"></i> <?= $this->transEsc($check ? "Check Recall" : "Recall This") ?> - </a> + </td> + <td data-title="<?= $this->transEsc('Notes') ?>" class="notes"> + <?php if (isset($holding['item_notes'])): ?> + <div class="item-notes"> + <?php /* finc: change b with strong #14450 */ ?> + <strong><?=$this->transEsc("Item Notes")?>:</strong> + <ul> + <?php foreach ($holding['item_notes'] as $item_note): ?> + <li><?=$this->escapeHtml($item_note) ?></li> + <?php endforeach; ?> + </ul> + </div> <?php endif; ?> - <?= $this->relais()->renderButtonIfActive($this->driver ?? null) ?> - <?php endif; ?> - </td> - <td data-title="<?= $this->transEsc('Notes') ?>" class="notes"> - <?php if (isset($holding['item_notes'])): ?> - <div class="item-notes"> - <strong><?= $this->transEsc("Item Notes") ?>:</strong> - <ul> - <?php foreach ($holding['item_notes'] as $item_note): ?> - <li><?= $this->escapeHtml($item_note) ?></li> - <?php endforeach; ?> - </ul> - </div> - <?php endif; ?> <?php endif; ?> <?php /* Embed item structured data: library, barcode, call number */ ?> <?php if ($holding['location'] ?? false): ?> - <meta property="seller" content="<?= $this->escapeHtmlAttr($holding['location']) ?>"/> + <meta property="seller" content="<?=$this->escapeHtmlAttr($holding['location'])?>" /> <?php endif; ?> <?php if ($holding['barcode'] ?? false): ?> - <meta property="serialNumber" content="<?= $this->escapeHtmlAttr($holding['barcode']) ?>"/> + <meta property="serialNumber" content="<?=$this->escapeHtmlAttr($holding['barcode'])?>" /> <?php endif; ?> <?php if ($holding['callnumber'] ?? false): ?> - <meta property="sku" content="<?= $this->escapeHtmlAttr($holding['callnumber']) ?>"/> + <meta property="sku" content="<?=$this->escapeHtmlAttr($holding['callnumber'])?>" /> <?php endif; ?> <?php /* Declare that the item is to be borrowed, not for sale */ ?> - <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut"/> - <link property="itemOffered" href="#record"/> + <link property="businessFunction" href="http://purl.org/goodrelations/v1#LeaseOut" /> + <link property="itemOffered" href="#record" /> <?php /* finc: price tags can be anywhere but seem to be required for product */ ?> <meta property="price" content="0"> <meta property="priceCurrency" content="€"> - <?php if (isset($holding['textfields'])): foreach ($holding['textfields'] as $textFieldName => $textFields): ?> <span> <?php foreach ($textFields as $current): ?> diff --git a/themes/finc/templates/RecordTab/holdingsworldcat.phtml b/themes/finc/templates/RecordTab/holdingsworldcat.phtml index b2feb10131638d3a1a70b9cde11fbe9352c70798..486f3a0c562894050fb4fa9b2171bfedecd66f8a 100644 --- a/themes/finc/templates/RecordTab/holdingsworldcat.phtml +++ b/themes/finc/templates/RecordTab/holdingsworldcat.phtml @@ -4,7 +4,7 @@ <p><?=$this->escapeHtml($holdings->diagnostic->message)?></p> <?php elseif ($holdings && count($holdings) > 0): ?> <h3><?=$this->transEsc('Holdings at Other Libraries')?></h3> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> <?php foreach ($holdings as $holding): ?> <tr> @@ -31,4 +31,4 @@ <?php endforeach; ?> </table> <?php endif; ?> -<!-- finc: recordTab - holdingswordlcat - END --> \ No newline at end of file +<!-- finc: recordTab - holdingswordlcat - END --> diff --git a/themes/finc/templates/RecordTab/staffviewai.phtml b/themes/finc/templates/RecordTab/staffviewai.phtml index d38b62fdc2e8e2c10139f41c3ef7feafe24b00ea..8f0300d1ab3b3386a7172c6e8b037eaf9a5c0b48 100644 --- a/themes/finc/templates/RecordTab/staffviewai.phtml +++ b/themes/finc/templates/RecordTab/staffviewai.phtml @@ -3,7 +3,7 @@ // Set page title. $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); ?> -<?php /* include responsive data table - CK */ ?> +<?php /* finc adds code for responsive data table here - CK */ ?> <table class="citation table table-striped table-resp-data"> <?php foreach ($this->driver->getAIRecord() as $field => $values): ?> <tr> diff --git a/themes/finc/templates/RecordTab/staffviewarray.phtml b/themes/finc/templates/RecordTab/staffviewarray.phtml index c046901cfae8ac587a7212b2c298778d27e6bf99..4fc6f4bd743ed0fbd48c119a523ccc2010203cf3 100644 --- a/themes/finc/templates/RecordTab/staffviewarray.phtml +++ b/themes/finc/templates/RecordTab/staffviewarray.phtml @@ -3,7 +3,7 @@ // Set page title. $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); ?> -<?php /* include responsive data table - CK */ ?> +<?php /* finc adds code for responsive data table here - CK */ ?> <table class="citation table table-striped table-resp-data"> <?php foreach ($this->driver->getRawData() as $field => $values): ?> <tr> diff --git a/themes/finc/templates/RecordTab/staffviewmarc.phtml b/themes/finc/templates/RecordTab/staffviewmarc.phtml index ebd800bbd7d5e6d2d6ed8fbd27770e893a99748a..672c14abe729e9af776e48e67d2686be7efacb90 100644 --- a/themes/finc/templates/RecordTab/staffviewmarc.phtml +++ b/themes/finc/templates/RecordTab/staffviewmarc.phtml @@ -1,7 +1,7 @@ <!-- finc: recordtab - staffviewmarc --> <?php -// Set page title. -$this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); + // Set page title. + $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBreadcrumb()); ?> <?=\VuFind\XSLT\Processor::process('record-marc.xsl', $this->driver->getXML('marc21'))?> <?php /* the following introduced in 9934*/ ?> @@ -17,7 +17,7 @@ $this->headTitle($this->translate('Staff View') . ': ' . $this->driver->getBread <?php endif; ?> <?php $fields = $this->driver->getRawData(); if (!empty($fields)): ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table here - CK */ ?> <table class="citation table table-striped table-resp-data"> <caption><?=$this->translate('SOLR')?></caption> <?php ksort($fields); diff --git a/themes/finc/templates/RecordTab/toc.phtml b/themes/finc/templates/RecordTab/toc.phtml index eb89dc704ee6e9e182599bac1de1076753b76bf9..8a3f6ae960f945323ba6aacfa619e122074d3b0f 100644 --- a/themes/finc/templates/RecordTab/toc.phtml +++ b/themes/finc/templates/RecordTab/toc.phtml @@ -2,6 +2,7 @@ <?php // Set page title. $this->headTitle($this->translate('Table of Contents') . ': ' . $this->driver->getBreadcrumb()); + // finc: use recordDataFormatter #19336; rest is mostly custom finc $formatter = $this->recordDataFormatter(); $mainFields = $formatter->getData($this->driver, $formatter->getDefaults('toc')); ?> diff --git a/themes/finc/templates/admin/home.phtml b/themes/finc/templates/admin/home.phtml deleted file mode 100644 index 5e8aa62a4df836ff1d9e1273b9cc352803b0394d..0000000000000000000000000000000000000000 --- a/themes/finc/templates/admin/home.phtml +++ /dev/null @@ -1,52 +0,0 @@ -<!-- finc: admin/home --> -<?php - /** @deprecated since v6.1 - * #18044 template needed to FIX bootstrap casting error in uptime display with '(int)' instead of '(string)' - */ - - // Set page title. - $this->headTitle($this->translate('VuFind Administration - Home')); - - // Set up map of core name => label - $coreLabels = [ - 'biblio' => $this->translate('Bibliographic Index'), - 'authority' => $this->translate('Authority Index'), - ]; -?> -<div class="<?=$this->layoutClass('mainbody')?>"> - <h2><?=$this->transEsc('VuFind Administration')?></h2> - <?php $cores = is_object($this->xml) ? $this->xml->xpath('/response/lst[@name="status"]/lst') : []; ?> - <?php foreach ($cores as $core): ?> - <?php $coreName = (string)$core['name']; ?> - <?php $coreLabel = $coreLabels[$coreName] ?? ucwords($coreName) . ' Index'; ?> - <h3><?=$this->transEsc($coreLabel)?></h3> - <table class="table table-striped"> - <tr> - <th><?=$this->transEsc('Record Count')?>: </th> - <?php $recordCount = $core->xpath('//lst[@name="' . $coreName . '"]/lst/int[@name="numDocs"]') ?> - <td><?=$this->escapeHtml((string)array_pop($recordCount))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Start Time')?>: </th> - <?php $startTime = $core->xpath('//lst[@name="' . $coreName . '"]/date[@name="startTime"]') ?> - <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($startTime))))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Last Modified')?>: </th> - <?php $lastModified = $core->xpath('//lst[@name="' . $coreName . '"]/lst/date[@name="lastModified"]') ?> - <td><?=$this->escapeHtml(strftime("%b %d, %Y %l:%M:%S%p", strtotime((string)array_pop($lastModified))))?></td> - </tr> - <tr> - <th><?=$this->transEsc('Uptime')?>: </th> - <?php $uptime = $core->xpath('//lst[@name="' . $coreName . '"]/long[@name="uptime"]') ?> - <?php /* #18044 fix casting error in uptime display - solved in Vufind 6 */ ?> - <td><?=$this->printms((int)array_pop($uptime))?></td> - </tr> - </table> - <?php endforeach; ?> -</div> - -<div class="<?=$this->layoutClass('sidebar')?>"> - <?=$this->render("admin/menu.phtml")?> -</div> -<!-- finc: admin/home - END --> diff --git a/themes/finc/templates/ajax/resolverLink.phtml b/themes/finc/templates/ajax/resolverLink.phtml new file mode 100644 index 0000000000000000000000000000000000000000..f84a2cd4eee2ffbabb071094472cec49f3b5072b --- /dev/null +++ b/themes/finc/templates/ajax/resolverLink.phtml @@ -0,0 +1,22 @@ +<?php if (!empty($link['href'])): ?> + <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=$this->transEsc($link['service_type'] ?? '')?>"<?=!empty($link['access']) ? ' class="access-' . $link['access'] . '"':''?>> + <?=$this->escapeHtml($link['title'] ?? '')?> + </a> +<?php else: ?> + <?=$this->escapeHtml($link['title'] ?? '')?> +<?php endif; ?> +<?php if (!empty($link['coverage'])): ?> + <span class="openurl-coverage"> + <?=$this->escapeHtml($link['coverage'])?> + </span> +<?php endif; ?> +<?php if (!empty($link['notes'])): ?> + <span class="openurl-notes"> + <?=$this->escapeHtml($link['notes'])?> + </span> +<?php endif; ?> +<?php if (!empty($link['authentication'])): ?> + <span class="openurl-authentication-notes"> + <?=$this->escapeHtml($link['authentication'])?> + </span> +<?php endif; ?> diff --git a/themes/finc/templates/ajax/resolverLinks.phtml b/themes/finc/templates/ajax/resolverLinks.phtml index a2235cd589385d9c86586f9116cd62d099777a38..b9723e784590669687c281081f31441795267756 100644 --- a/themes/finc/templates/ajax/resolverLinks.phtml +++ b/themes/finc/templates/ajax/resolverLinks.phtml @@ -6,44 +6,16 @@ <?php $noResolverContent = false; ?> <?php /* finc-specific change #8447 - END */ ?> <div class="openurls"> - <?php /* + <?php /* finc hide pseudo header */ + /* <strong><?=$this->transEsc('Electronic')?></strong> - */ ?> + */ + /* keep 'no-bullet' on ul below - CK */ + ?> <ul class="no-bullet"> <?php foreach ($this->electronic as $link): ?> <li> - <?php if (!empty($link['href'])): ?> - <?php /* finc-specific change #7986 - CK - traffic light */ ?> - <div class="show-availability"> - <span class="sr-only"> - <?=$this->translate('Availability')?>: <?=$this->transEsc('resolver_link_access_' . $link['access'])?> - </span> - <div aria-hidden="true"<?php if(!empty($link['access'])): ?> data-toggle="tooltip" title="<?=$this->transEsc('resolver_link_access_'.$link['access'])?>" class="traffic-light access-<?=$link['access']?>"<?php endif;?>> - <span class="first"></span> - <span class="second"></span> - <span class="last"></span> - </div> - </div> - <?php /* finc-specific change #7986 - END */ ?> - <?= $this->externalLink( - $this->escapeHtmlAttr($link['href']), - $link['title'] ?? '', - [ - 'title' => $link['service_type'] ?? '', - 'class' => !empty($link['access']) ? 'access-' . $link['access'] : '' - ] - ) ?> <br /> - <?php /* finc-specific change #5334 - CK */ ?> - <small> - <?= isset($link['coverage']) ? $this->escapeHtml($link['coverage']) : '' ?> - <?= isset($link['coverageHref']) - ? $this->externalLink($link['coverageHref'], $this->translate('Readme')) - : '' ?> - </small> - <?php /* finc-specific change #5334 - END */ ?> - <?php else: ?> - <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->transEsc($link['coverage']):''?> - <?php endif; ?> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'electronic']); ?> </li> <?php endforeach; ?> </ul> @@ -52,30 +24,27 @@ <?php /* finc-specific change - commented out in #7643 - hide info on print issues - CK */ ?> <?php /* <?php if (!empty($this->print)): ?> - <?php $noResolverContent = false; ?> <div class="openurls"> <strong><?=$this->transEsc('Holdings')?></strong> <ul> <?php foreach ($this->print as $link): ?> <li> - <?php if (!empty($link['href'])): ?> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php else: ?> - <?=isset($link['title'])?$this->escapeHtml($link['title']):''?> <?=isset($link['coverage'])?$this->escapeHtml($link['coverage']):''?> - <?php endif; ?> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'print']); ?> </li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <div class="openurls"> - <?php if (!empty($this->moreOptionsLink)): ?><strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong><?php endif; ?> + <?php if (!empty($this->moreOptionsLink)): ?> + <strong><a href="<?=$this->escapeHtmlAttr($this->moreOptionsLink)?>"><?=$this->transEsc('More options')?></a></strong> + <?php endif; ?> <?php if (!empty($this->services)): ?> <ul> <?php foreach ($this->services as $link): ?> <?php if (!empty($link['href'])): ?> <li> - <a href="<?=$this->escapeHtmlAttr($link['href'])?>" title="<?=isset($link['service_type'])?$this->escapeHtmlAttr($link['service_type']):''?>"<?=!empty($link['access'])?' class="access-'.$link['access'].'"':''?>><?=isset($link['title'])?$this->escapeHtml($link['title']):''?></a> + <?=$this->context($this)->renderInContext('ajax/resolverLink.phtml', ['link' => $link, 'type' => 'service']); ?> </li> <?php endif; ?> <?php endforeach; ?> @@ -86,7 +55,7 @@ <?php /* finc-specific change - commented out in #7643 - END */ ?> <?php /* finc-specific change #9274 - CK */ ?> - <?php if ($noResolverContent): // no content to show, so tell the user ?> + <?php if ($noResolverContent): // no content to show, so tell the user?> <span id="noResolverContentMessage" class="hidden"><?=$this->resolver . ': ' . $this->translate('no_resolver_links')?></span> <script>if ($('#urlsHideable').length) { $('#urlsHideable').show(); } else { $('#noResolverContentMessage').show(); }</script> <?php endif; ?> diff --git a/themes/finc/templates/ajax/status-full.phtml b/themes/finc/templates/ajax/status-full.phtml index f48411b6a7613839ee6b963ee2510646bc2053be..3aadb05d168ec9499c8ec14f4b25ae7933d84707 100644 --- a/themes/finc/templates/ajax/status-full.phtml +++ b/themes/finc/templates/ajax/status-full.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - ajax - status-full --> -<?php /* include responsive data table - CK */ ?> +<?php /* finc has specific code for responsive data tables - CK */ ?> <table class="table table-condensed table-resp-data"> <tr> <th><?=$this->transEsc('Location')?></th> @@ -10,9 +10,9 @@ <?php if (++$i == 5) break; // Show no more than 5 items ?> <tr> <td data-title="<?= $this->transEsc('Location') ?>:" class="fullLocation"> - <?php $locationText = $this->transEsc('location_' . $item['location'], [], $item['location']); ?> - <?php if (isset($item['locationhref']) && $item['locationhref']): ?> - <?= $this->externalLink($item['locationhref'], $locationText) ?> + <?php $locationText = $this->transEscWithPrefix('location_', $item['location']); ?> + <?php if ($item['locationhref'] ?? false): ?> + <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a> <?php else: ?> <?=$locationText?> <?php endif; ?> @@ -25,7 +25,7 @@ <?php endif; ?> </td> <td data-title="<?= $this->transEsc('Status') ?>:" class="fullAvailability"> - <?php if (isset($item['use_unknown_message']) && $item['use_unknown_message']): ?> + <?php if ($item['use_unknown_message'] ?? false): ?> <span><?=$this->transEsc("status_unknown_message")?></span> <?php elseif ($item['availability']): ?> <span class="text-success"><?=($item['reserve'] == 'Y') ? $this->transEsc("On Reserve") : $this->transEsc("Available")?></span> diff --git a/themes/finc/templates/ajax/status-unavailable-services.phtml b/themes/finc/templates/ajax/status-unavailable-services.phtml index cf6c13247f4a15f5cb6213d061368f94f1dc4c02..4b16538bd3ee72ec838222acf8479f50d0488636 100644 --- a/themes/finc/templates/ajax/status-unavailable-services.phtml +++ b/themes/finc/templates/ajax/status-unavailable-services.phtml @@ -1,25 +1,27 @@ <!-- finc: ajax - status-unavailable-services --> +<?php /* finc: compare with BS-file status-available-services */ ?> <?php // defaultServiceStatusMessage for multiple status and as fallback for missing translations $defaultServiceStatusMessage = 'HoldingStatus::services_available_html'; // generate serviceStatusMessage $serviceStatusMessage = - count($services) == 1 - ? $this->transEsc('HoldingStatus::service_available_' . $services[0], [], $defaultServiceStatusMessage) - : $defaultServiceStatusMessage; + count($services) == 1 + ? $this->transEsc('HoldingStatus::service_available_' . $services[0], [], $defaultServiceStatusMessage) + : $defaultServiceStatusMessage; // build the translated serviceList for usage in defaultServiceStatusMessage $that = $this; $translator = function ($in) use ($that) { - return '<span class="service_' . $in . '">' . $that->transEsc('HoldingStatus::service_' . $in, [], $in) . '</span>'; + return '<span class="service_' . $in . '">' . $that->transEsc('HoldingStatus::service_' . $in, [], $in) . '</span>'; }; $serviceList = implode('', array_map($translator, $services)); ?> +<?php /* finc: change classname 'success' to 'danger' */ ?> <span class="label label-danger services-<?=implode('-', $services)?>"> <?=$serviceStatusMessage === $defaultServiceStatusMessage - ? $this->translate($serviceStatusMessage, ['%%list%%' => $serviceList]) - : '<span class="service_' . $services[0] . '">' . $serviceStatusMessage . '</span>'?> + ? $this->translate($serviceStatusMessage, ['%%list%%' => $serviceList]) + : '<span class="service_' . $services[0] . '">' . $serviceStatusMessage . '</span>' ?> </span> <!-- finc: ajax - status-unavailable-services - END --> diff --git a/themes/finc/templates/ajax/status-unavailable.phtml b/themes/finc/templates/ajax/status-unavailable.phtml index d0a9131facc3b11e1b74352ade5f7edd9065502b..47e6894894f8de4672874fb3941010fba0d33a6a 100644 --- a/themes/finc/templates/ajax/status-unavailable.phtml +++ b/themes/finc/templates/ajax/status-unavailable.phtml @@ -1 +1,2 @@ +<?php /* finc: change token in status-unavailable template #19580 */ ?> <span class="label label-danger"><?=$this->transEsc("Unavailable")?></span> diff --git a/themes/finc/templates/alphabrowse/home.phtml b/themes/finc/templates/alphabrowse/home.phtml index 0e024ed7736b62ce650b2ecb94301eaa95c34525..b4e7ca1ae1c1426609b0aa0c6b2d3433c2424fb6 100644 --- a/themes/finc/templates/alphabrowse/home.phtml +++ b/themes/finc/templates/alphabrowse/home.phtml @@ -27,6 +27,7 @@ </a> </li> <?php else: ?> + <?php /* finc: keep 'aria-hidden' - CK */ ?> <li class="disabled" aria-hidden="true"><i class="fa fa-angle-left" aria-hidden="true"></i> <?=$this->transEsc('Prev')?></li> <?php endif; ?> @@ -38,6 +39,7 @@ </a> </li> <?php else: ?> + <?php /* finc: keep 'aria-hidden' - CK */ ?> <li class="disabled" aria-hidden="true"><?=$this->transEsc('Next')?> <i class="fa fa-angle-right" aria-hidden="true"></i></li> <?php endif; ?> </ul> @@ -62,7 +64,7 @@ <?php if ($this->result): ?> <?=$pageLinks ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc has specific code for responsive data tables - CK */ ?> <table class="alphabrowse table table-striped table-resp-data"> <thead> <tr> diff --git a/themes/finc/templates/browse/home.phtml b/themes/finc/templates/browse/home.phtml index d20402286c31d1e1cb1a62bc133202e76d32be3a..3ae7857da7febac0ca782603dedfac30ef6af14f 100644 --- a/themes/finc/templates/browse/home.phtml +++ b/themes/finc/templates/browse/home.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - browse - home --> -<?php /* copied from bootstrap3 - added h1 for sr-only - #17596 - HR */?> +<?php /* finc has h1 as sr-only - #17596 */?> <?php $this->headTitle($this->translate('Browse the Catalog')); @@ -84,6 +84,7 @@ </div> <?php elseif (isset($this->query)): ?> <div class="browse-list" id="list4"> + <?php /* finc: keep 'lang' - CK */ ?> <span class="browse-item" lang=""><?=$this->transEsc('nohit_heading') ?></span> </div> <?php endif; ?> diff --git a/themes/finc/templates/cart/cart.phtml b/themes/finc/templates/cart/cart.phtml index 7ac208d9e771e3d811ae9d85a734363d7bf9ab7f..9eb9112c5ee1c87f1f9c64ab9515b6e693076ca7 100644 --- a/themes/finc/templates/cart/cart.phtml +++ b/themes/finc/templates/cart/cart.phtml @@ -6,6 +6,7 @@ // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li>' . $this->searchMemory()->getLastSearchLink($this->transEsc('Search'), '', '</li> ') ?> +<?php /* finc: use h1 for correct header structure */ ?> <h1><?=$this->transEsc('Book Bag') ?></h1> <?=$this->flashmessages()?> <?php /* finc: use .cart class to style content below */ ?> @@ -13,6 +14,7 @@ <input type="hidden" id="dropdown_value"/> <?php if (!$this->cart()->isEmpty()): ?> <div class="cart-controls clearfix"> + <?php /* finc: add classname w-100 for linebreak after 'Select Page' #11813 */ ?> <div class="checkbox pull-left flip w-100"> <label> <input type="checkbox" name="selectAll" class="checkbox-select-all"/> @@ -20,43 +22,37 @@ </label> </div> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> - <button type="submit" class="btn btn-transparent" name="saveCart" value="1"> - <i class="fa fa-save" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-save" name="saveCart" value="1" title="<?=$this->transEsc('bookbag_save')?>" value="1"> <?=$this->transEsc('Save')?> </button> <?php endif; ?> - <button type="submit" class="btn btn-transparent" name="email" value="1"> - <i class="fa fa-envelope-o" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-email" name="email" value="1" title="<?=$this->transEsc('bookbag_email')?>" value="1"> <?=$this->transEsc('Email')?> </button> <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> - <button type="submit" class="btn btn-transparent" name="export" value="1"> - <i class="fa fa-list-alt" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-export" name="export" value="1" title="<?=$this->transEsc('bookbag_export')?>" value="1"> <?=$this->transEsc('Export')?> </button> <?php endif; ?> - <button type="submit" class="btn btn-transparent dropdown-toggle" name="print" value="1"> - <i class="fa fa-printer" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-print dropdown-toggle" name="print" value="1" title="<?=$this->transEsc('print_selected')?>" value="1"> <?=$this->transEsc('Print')?> </button> <div class="btn-group" id="cartDelete"> - <button type="submit" name="delete" class="btn btn-transparent dropdown-toggle" data-toggle="dropdown" id="cart-delete-label" value="1"> - <i class="fa fa-trash" aria-hidden="true"></i> + <button type="submit" name="delete" value="1" class="toolbar-btn btn-type-delete dropdown-toggle" data-toggle="dropdown" id="cart-delete-label" value="1"> <?=$this->transEsc('Delete')?> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="cart-delete-label"> - <li><a href="javascript:;" id="cart-confirm-delete" onClick="submitFormWithButton(this, 'delete')" title="<?=$this->transEsc('confirm_delete')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> - <li><a href="javascript:;"><?=$this->transEsc('confirm_dialog_no')?></a></li> + <li><a id="cart-confirm-delete" onClick="submitFormWithButton(this, 'delete')" title="<?=$this->transEsc('confirm_delete')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> + <li><a><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> <div class="btn-group"> - <button type="submit" class="btn btn-transparent dropdown-toggle" name="empty" data-toggle="dropdown" id="cart-empty-label" value="1"> - <i class="fa fa-close" aria-hidden="true"></i> + <button type="submit" class="toolbar-btn btn-type-empty dropdown-toggle" name="empty" value="1" data-toggle="dropdown" id="cart-empty-label" value="1"> <?=$this->transEsc('Empty Book Bag')?> </button> <ul class="dropdown-menu" role="menu" aria-labelledby="cart-empty-label"> - <li><a href="javascript:;" id="cart-confirm-empty" onClick="submitFormWithButton(this, 'empty')" title="<?=$this->transEsc('bookbag_confirm_empty')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> - <li><a href="javascript:;" onClick="$('.fa.fa-spinner').remove()"><?=$this->transEsc('confirm_dialog_no')?></a></li> + <li><a id="cart-confirm-empty" onClick="submitFormWithButton(this, 'empty')" title="<?=$this->transEsc('bookbag_confirm_empty')?>"><?=$this->transEsc('confirm_dialog_yes')?></a></li> + <li><a onClick="$('.fa.fa-spinner').remove()"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> </div> diff --git a/themes/finc/templates/cart/contents.phtml b/themes/finc/templates/cart/contents.phtml index fc4400bdcec92dc28b6d07b0a50a168f266f702c..3d9b570f75fee09bcefc9cd09fd0239319dbb4ba 100644 --- a/themes/finc/templates/cart/contents.phtml +++ b/themes/finc/templates/cart/contents.phtml @@ -3,11 +3,12 @@ if (!empty($records)): ?> <hr/> <ul class="list-unstyled"> - <?php foreach ($records as $i => $record): ?> - <li> - <?php /* remove label here -- we use it in record/checkbox already - CK */ ?> + <?php foreach ($records as $i => $record): ?> + <li> + <?php /* remove label here - we use it in record/checkbox already - CK */ ?> <div class="checkbox"> <?=$this->record($record)->getCheckbox('cart')?> + <?php /* finc adds aria-label and -describedby + 'lang' - CK */ ?> <?php $describedById = $record->getSourceIdentifier() . '|' . $record->getUniqueId(); ?> <a id="<?=$describedById?>" title="<?=$this->transEsc('View Record')?>" href="<?=$this->recordLink()->getUrl($record)?>" data-lightbox-ignore lang=""><?=$this->escapeHtml($record->getBreadcrumb())?> <?php @@ -26,6 +27,7 @@ if (!empty($records)): ?> <?php endforeach; ?> </ul> <?php else: ?> - <p class="alert alert-info"><?=$this->transEsc('bookbag_is_empty')?>.</p> +<?php /* finc adds aria-live and uses p for alerts */ ?> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc('bookbag_is_empty')?>.</p> <?php endif; ?> <!-- finc: cart - contents - END --> diff --git a/themes/finc/templates/cart/email.phtml b/themes/finc/templates/cart/email.phtml index 54d82cec86f881f049b263075dbd504ce3b1bec2..1327e5d40338402c17580afe25c59ff7df41acd5 100644 --- a/themes/finc/templates/cart/email.phtml +++ b/themes/finc/templates/cart/email.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - cart - email --> -<?php /* copied from bootstrap3 - added h1 for sr-only - #17596 - HR */?> +<?php /* styles h1 as sr-only - #17596 */?> <?php // Set page title. diff --git a/themes/finc/templates/cart/save.phtml b/themes/finc/templates/cart/save.phtml index 92183fe7ae68d337ba0517f85ad01e5049ff4638..e4350b364b20e223602154ee18b2f537f9a99459 100644 --- a/themes/finc/templates/cart/save.phtml +++ b/themes/finc/templates/cart/save.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - cart - save --> -<?php /* copied from bootstrap3 - h2 becomes h1 - #17596 - HR */?> +<?php /* finc turns h2 into h1 - #17596 */?> <?php // Set page title. diff --git a/themes/finc/templates/channels/home.phtml b/themes/finc/templates/channels/home.phtml index 5bf072cc53b5b52ba409d30d005d485f67a0ac57..4293482acc6df8f668bd3ea6eb06a224a1caf9c9 100644 --- a/themes/finc/templates/channels/home.phtml +++ b/themes/finc/templates/channels/home.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - channels - home --> -<?php /* copied from bootstrap3 - added h1 for sr-only - #17596 - HR */?> +<?php /* finc styles h1 as sr-only - #17596 */?> <?php // Set up page title: diff --git a/themes/finc/templates/collection/view.phtml b/themes/finc/templates/collection/view.phtml index 2179d5cb00cf3319fad98a4ece3235c51298e4d1..176b98227bbdcf1fe711c5a941c6afcf552f0613 100644 --- a/themes/finc/templates/collection/view.phtml +++ b/themes/finc/templates/collection/view.phtml @@ -1,37 +1,42 @@ <!-- finc: collection - view --> <?php - // Set up standard record scripts: - $this->headScript()->appendFile("record.js"); - $this->headScript()->appendFile("check_save_statuses.js"); - // Activate Syndetics Plus if necessary: - if ($this->syndeticsPlus()->isActive()) { - $this->headScript()->appendFile($this->syndeticsPlus()->getScript()); - } + // Set up standard record scripts: + $this->headScript()->appendFile("record.js"); + $this->headScript()->appendFile("check_save_statuses.js"); + // Activate Syndetics Plus if necessary: + if ($this->syndeticsPlus()->isActive()) { + $this->headScript()->appendFile($this->syndeticsPlus()->getScript()); + } - // Add RDF header link if applicable: - if ($this->export()->recordSupportsFormat($this->driver, 'RDF')) { - $this->headLink()->appendAlternate($this->recordLink()->getActionUrl($this->driver, 'RDF'), 'application/rdf+xml', 'RDF Representation'); - } + // Add RDF header link if applicable: + if ($this->export()->recordSupportsFormat($this->driver, 'RDF')) { + $this->headLink()->appendAlternate($this->recordLink()->getActionUrl($this->driver, 'RDF'), 'application/rdf+xml', 'RDF Representation'); + } - // Set flag for special cases relating to full-width hierarchy tree tab: - $tree = (strtolower($this->activeTab) == 'hierarchytree'); + // Set flag for special cases relating to full-width hierarchy tree tab: + $tree = (strtolower($this->activeTab) == 'hierarchytree'); - // Set up breadcrumbs: - $lastSearch = $this->searchMemory()->getLastSearchLink($this->transEsc('Search')); - if (!empty($lastSearch)) { - $this->layout()->breadcrumbs = '<li>' . $lastSearch . '</li> '; - } - $this->layout()->breadcrumbs .= '<li><a href="' . $this->url('collections-home') . '">' . $this->transEsc('Collections') . '</a></li> ' - . '<li class="active">' . $this->recordLink()->getBreadcrumb($this->driver) . '</li>'; + // Set up breadcrumbs: + $lastSearch = $this->searchMemory()->getLastSearchLink($this->transEsc('Search')); + if (!empty($lastSearch)) { + $this->layout()->breadcrumbs = '<li>' . $lastSearch . '</li> '; + } + $this->layout()->breadcrumbs .= '<li><a href="' . $this->url('collections-home') . '">' . $this->transEsc('Collections') . '</a></li> ' + . '<li class="active">' . $this->recordLink()->getBreadcrumb($this->driver) . '</li>'; ?> <?php if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> <?=$this->render('record/prev-next.phtml'); ?> <?php endif; ?> -<?php /* DON'T pull the toolbar in here but below, finc-specific, CK */ ?> +<?php /* DON'T pull the toolbar in here but below, finc-specific, CK */ +/* + <?=$this->record($this->driver)->getToolbar()?> +*/ +?> <div class="record"> + <?php /* finc - don't use '<?=count($sidebarList) < 1 ? ' solo' : '' ?>' or toolbar won't fit; BS count sidebars but our toolbar isn't counted */ ?> <div<?php if (!$tree): /* in tree mode, do not constrain width with a class */ ?> class="<?= $this->layoutClass('mainbody') ?>"<?php endif; ?>> <input type="hidden" value="<?= $this->escapeHtmlAttr($this->driver->getUniqueId()) ?>" class="hiddenId" id="record_id"/> <input type="hidden" value="<?= $this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource"/> @@ -63,44 +68,38 @@ $activeTabObj = $obj; } if (!$obj->isVisible()) { - $tab_classes[] = 'hidden'; - } - if (!$obj->supportsAjax()) { - $tab_classes[] = 'noajax'; - } - ?> - <li <?= count($tab_classes) > 0 ? ' class="' . implode(' ', $tab_classes) . '"' : '' ?>> - <a class="<?= strtolower($tab) ?>" - href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)): ?> data-background<?php endif ?>><?= $this->transEsc($desc) ?></a> - </li> - <?php endforeach; ?> - </ul> + $tab_classes[] = 'hidden';} + if (!$obj->supportsAjax()) { $tab_classes[] = 'noajax'; } + ?> + <li<?=count($tab_classes) > 0 ? ' class="' . implode(' ', $tab_classes) . '"' : ''?>> + <a class="<?=strtolower($tab) ?>" href="<?=$this->recordLink()->getTabUrl($this->driver, $tab)?>#tabnav"<?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?>><?=$this->transEsc($desc)?></a> + </li> + <?php endforeach; ?> + </ul> - <div class="tab-content collectionDetails<?= $tree ? 'Tree' : '' ?>"> - <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> - <div class="tab-pane active <?= $this->activeTab ?>-tab"> - <?= isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> - </div> - <?php endif; ?> - </div> + <div class="tab-content collectionDetails<?=$tree ? 'Tree' : ''?>"> + <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> + <div class="tab-pane active <?=$this->activeTab ?>-tab"> + <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> + </div> + <?php endif; ?> </div> - <?php endif; ?> - - <?= $this->driver->supportsCoinsOpenURL() ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>' : '' ?> + </div> + <?php endif; ?> + + <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>':''?> </div> - + + <?php /* pull the toolbar here, finc-specific, CK */ ?> <?php if (isset($activeTabObj) && is_callable([$activeTabObj, 'getSideRecommendations'])): ?> <div class="<?= $this->layoutClass('sidebar') ?>" id="myresearch-sidebar"> - <?php /* pull the toolbar here, finc-specific, CK */ ?> <?= $this->record($this->driver)->getToolbar() ?> - <?php foreach ($activeTabObj->getSideRecommendations() as $current): ?> <?= $this->recommend($current) ?> <?php endforeach; ?> </div> <?php endif; ?> -</div> - -<?= $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$(document).ready(recordDocReady);', 'SET'); ?> + </div> +<?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$(document).ready(recordDocReady);', 'SET'); ?> <!-- finc: collection - view - END --> diff --git a/themes/finc/templates/footer.phtml b/themes/finc/templates/footer.phtml index 719d70099b343d088d6db736236e067b6fa92110..1d10374d3bdb894f38d4ad241eeb6eae145e5fee 100644 --- a/themes/finc/templates/footer.phtml +++ b/themes/finc/templates/footer.phtml @@ -8,11 +8,13 @@ <li><a href="<?=$this->url('search-advanced')?>"><?=$this->transEsc('Advanced Search')?></a></li> </ul> </div> - <div class="footer-column"> <h2><?=$this->transEsc('Find More')?></h2> <ul> <li><a href="<?=$this->url('browse-home')?>"><?=$this->transEsc('Browse the Catalog')?></a></li> + <?php /* finc: activate, if necessary */ /* + <li><a href="<?=$this->url('alphabrowse-home')?>"><?=$this->transEsc('Browse Alphabetically')?></a></li> + */ ?> <li><a href="<?=$this->url('channels-home')?>"><?=$this->transEsc('channel_explore')?></a></li> <li><a href="<?=$this->url('search-reserves')?>"><?=$this->transEsc('Course Reserves')?></a></li> <li><a href="<?=$this->url('search-newitem')?>"><?=$this->transEsc('New Items')?></a></li> @@ -23,16 +25,15 @@ <div class="footer-column"> <h2><?=$this->transEsc('Need Help?')?></h2> <ul> - <li><a href="<?=$this->url('help-home')?>?topic=search&_=<?=time()?>" data-lightbox class="help-link"><?=$this->transEsc('Search Tips')?></a></li> - <li><a href="<?=$this->url('content-page', ['page' => 'asklibrary'])?>"><?=$this->transEsc('Ask a Librarian')?></a></li> - <li><a href="<?=$this->url('content-page', ['page' => 'faq'])?>"><?=$this->transEsc('FAQs')?></a></li> + <li><a href="<?=$this->url('help-home')?>?topic=search&_=<?=time() ?>" data-lightbox class="help-link"><?=$this->transEsc('Search Tips')?></a></li> + <li><a href="<?=$this->url('content-page', ['page' => 'asklibrary']) ?>"><?=$this->transEsc('Ask a Librarian')?></a></li> + <li><a href="<?=$this->url('content-page', ['page' => 'faq']) ?>"><?=$this->transEsc('FAQs')?></a></li> </ul> </div> </div> - <?php /* finc branding footer */ ?> <hr> - <div class="footer-container powered-by"> + <div class="footer-container poweredBy"> <div class="footer-column"> <span> <?= $this->transEsc("Footer-Powered-By-Text") ?> diff --git a/themes/finc/templates/header.phtml b/themes/finc/templates/header.phtml index d16a618bd9507ed978083ff3ca19d0c487c961b7..bd33cef76b89153af2c422eddb5453aee23295bb 100644 --- a/themes/finc/templates/header.phtml +++ b/themes/finc/templates/header.phtml @@ -21,7 +21,6 @@ <?php /* Custom finc header - END */ ?> </a> </div> - <?php if (!isset($this->layout()->renderingError)): ?> <div class="collapse navbar-collapse" id="header-collapse"> <?php /* finc: right-hand navbar section - see flex-container in SCSS: @@ -29,74 +28,65 @@ but reverse it on anything but mobile */ /* <nav> element implies role="navigation" - we use aria-label to clarify - CK */ ?> + <?php /* finc: class 'btn' never showed up in BS, so it probably was added for a reason to the following a-elements in #13547 */ ?> + <?php /* finc: element 'span' never showed up in BS, so it probably was added for a reason to the following elements in #9844 */ ?> <nav aria-label="<?=$this->transEsc('main_navigation')?>"> <ul class="nav navbar-nav navbar-right flip"> <?php if ($this->feedback()->tabEnabled()): ?> <li> - <a id="feedbackLink" data-lightbox class="btn" href="<?=$this->url('feedback-home')?>" aria-label="<?=$this->transEsc("Feedback")?>"> <i class="fa fa-envelope" aria-hidden="true"></i></a> + <?php /* finc: BARF bookbag, #1999 */?> + <a id="feedbackLink" data-lightbox href="<?=$this->url('feedback-home')?>" class="btn" aria-label="<?=$this->transEsc("Feedback")?>"> <i class="fa fa-envelope" aria-hidden="true"></i></a> </li> <?php endif; ?> - <?php $cart = $this->cart(); - if ($cart->isActive()): ?> + <?php $cart = $this->cart(); if ($cart->isActive()): ?> <li id="cartSummary"> <a id="cartItems" class="btn" data-lightbox title="<?=$this->transEsc('View Book Bag')?>" href="<?=$this->url('cart-home')?>"> <i class="fa fa-clipboard" aria-hidden="true"></i> <span role="status"> <span class="sr-only"><?=$this->transEsc('Book Bag')?>:</span> <strong><?=count($cart->getItems())?></strong> <span class="cart-label"><?=$this->transEsc('items')?></span> - <span class="sr-only full<?=!$cart->isFull() ? ' hidden' : ''?>">(<?=$this->transEsc('bookbag_full_msg')?>)</span> + <span class="sr-only full<?=!$cart->isFull() ? ' hidden' : '' ?>">(<?=$this->transEsc('bookbag_full_msg')?>)</span> </span> </a> </li> <?php endif; ?> <?php if (is_object($account) && $account->loginEnabled()): // hide login/logout if unavailable ?> - <li class="logoutOptions<?php if ($account->dropdownEnabled()): ?> with-dropdown<?php endif ?><?php if (!$account->isLoggedIn()): ?> hidden<?php endif ?>"> + <li class="logoutOptions<?php if($account->dropdownEnabled()): ?> with-dropdown<?php endif ?><?php if(!$account->isLoggedIn()): ?> hidden<?php endif ?>"> <a class="btn" href="<?=$this->url('myresearch-home', [], ['query' => ['redirect' => 0]])?>"> <i id="account-icon" class="fa fa-home" aria-hidden="true"></i> + <?php /* finc: accessibility fixes in site header #17720 */ ?> <span class="hidden-sm-md"><?=$this->transEsc("Your Account")?></span> <span class="visible-sm-md-only"><?=$this->transEsc("Account")?></span> </a> </li> - <?php if ($account->dropdownEnabled()): ?> - <li id="login-dropdown" class="dropdown<?php if (!$account->isLoggedIn()): ?> hidden<?php endif ?>"> + <?php if($account->dropdownEnabled()): ?> + <li id="login-dropdown" class="dropdown<?php if(!$account->isLoggedIn()): ?> hidden<?php endif ?>"> <a href="#" data-toggle="dropdown"><i class="fa fa-caret-down"></i></a> <div class="dropdown-menu"> - <?=$this->render('myresearch/menu');?> + <?=$this->render('myresearch/menu'); ?> </div> </li> <?php endif; ?> - <li class="logoutOptions<?php if (!$account->isLoggedIn()): ?> hidden<?php endif ?>"> - <a href="<?=$this->url('myresearch-logout')?>" class="logout btn"> - <i class="fa fa-sign-out" aria-hidden="true"></i> - <span><?=$this->transEsc("Log Out")?></span> - </a> + <li class="logoutOptions<?php if(!$account->isLoggedIn()): ?> hidden<?php endif ?>"> + <a href="<?=$this->url('myresearch-logout')?>" class="logout btn"><i class="fa fa-sign-out" aria-hidden="true"></i> <span><?=$this->transEsc("Log Out")?></span></a> </li> - - <li id="loginOptions"<?php if ($account->isLoggedIn()): ?> class="hidden"<?php endif ?>> + <li id="loginOptions"<?php if($account->isLoggedIn()): ?> class="hidden"<?php endif ?>> <?php if ($account->getSessionInitiator($this->serverUrl($this->url('myresearch-home')))): ?> - <a class="btn" href="<?=$this->url('myresearch-userlogin')?>"> - <i class="fa fa-sign-in" aria-hidden="true"></i> - <span><?=$this->transEsc("Institutional Login")?></span> - </a> + <a class="btn" href="<?=$this->url('myresearch-userlogin')?>"><i class="fa fa-sign-in" aria-hidden="true"></i> <span><?=$this->transEsc("Institutional Login")?></span></a> <?php else: ?> - <a class="btn" href="<?=$this->url('myresearch-userlogin')?>" data-lightbox> - <i class="fa fa-sign-in" aria-hidden="true"></i> - <span><?=$this->transEsc("Login")?></span> - </a> + <a class="btn" href="<?=$this->url('myresearch-userlogin')?>" data-lightbox><i class="fa fa-sign-in" aria-hidden="true"></i> <span><?=$this->transEsc("Login")?></span></a> <?php endif; ?> </li> <?php endif; ?> <?php if (isset($this->layout()->themeOptions) && count($this->layout()->themeOptions) > 1): ?> <li class="theme dropdown"> - <form method="post" name="themeForm" id="themeForm"> - <input type="hidden" name="ui"/> - </form> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Theme")?> <strong class="caret"></strong></a> - + <a href="#" class="dropdown-toggle" data-toggle="dropdown"><?=$this->transEsc("Theme")?> <b class="caret"></b></a> <ul class="dropdown-menu"> <?php foreach ($this->layout()->themeOptions as $current): ?> - <li <?=$current['selected'] ? ' class="active"' : ''?>> - <a href="#" onClick="document.themeForm.ui.value='<?=$this->escapeHtmlAttr($current['name'])?>';document.themeForm.submit()"><?=$this->transEsc($current['desc'])?></a> + <li<?=$current['selected'] ? ' class="active"' : ''?>> + <a href="<?=$this->escapeHtmlAttr($this->url()->addQueryParameters(['ui' => $current['name']])) ?>" rel="nofollow"> + <?=$this->transEsc($current['desc']) ?> + </a> </li> <?php endforeach; ?> </ul> @@ -110,11 +100,14 @@ <a href="#" class="btn dropdown-toggle <?=(count($this->layout()->allLangs) == 2) ? ' hidden' : ''?>" data-toggle="dropdown" aria-controls="langmenu" aria-expanded="false"> <?=$this->transEsc("Language")?> <strong class="caret"></strong> </a> + <?php /* finc: need id="langmenu"; adapt langmenu #17599 */ ?> <ul id="langmenu" class="dropdown-menu <?=(count($this->layout()->allLangs) == 2) ? ' oneLanguage' : ''?>"> <?php foreach ($this->layout()->allLangs as $langCode => $langName): ?> - <?php if ($langCode !== $this->layout()->userLang) : ?> + <?php /* finc: more accessibility rework #17908 */ ?> + <?php if ($langCode !==$this->layout()->userLang) : ?> <li> <button type="submit" class="btn <?=(count($this->layout()->allLangs) == 2) ? ' btn-secondary' : ''?>" data-href="#" onClick="document.langForm.mylang.value='<?=$langCode?>';document.langForm.submit()"> + <?php /* finc: accessibility fixes in site header #17720 */ ?> <span class="visible-sm-md-only"><?=$langCode?></span> <span class="hidden-sm-md"><?=$this->displayLanguageOption($langName)?></span> </button> @@ -135,7 +128,7 @@ <?php /* finc: remove role="search" here - must be place on form element surrounding the search box - CK */ ?> <div class="search container"> <div class="nav searchbox hidden-print"> - <?=$this->layout()->searchbox?> + <?=$this->layout()->searchbox ?> </div> </div> <?php endif; ?> diff --git a/themes/finc/templates/layout/layout.phtml b/themes/finc/templates/layout/layout.phtml index 379454d45f7091db530667ddc0e1fc449400ac54..c5f67388fc17a69591a7d0fc7d5f6e0939cb3757 100644 --- a/themes/finc/templates/layout/layout.phtml +++ b/themes/finc/templates/layout/layout.phtml @@ -2,12 +2,16 @@ <html lang="<?=$this->layout()->userLang?>"<?php if ($this->layout()->rtl): ?> dir="rtl"<?php endif; ?>> <head> <?php $this->headThemeResources(); ?> - <?php /* remove meta because of W3C Validator error - CK */ + <?php /* finc: remove meta because of W3C Validator error - CK */ /* <meta charset="utf-8"> */ ?> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width,initial-scale=1.0"/> <?=$this->headMeta()?> + <?php /* finc: translate header title - CK */ + /* + <?=$this->headTitle()?> + */ ?> <?php // Pullrequest 2157 Ticket #20826 // Format the page title using the translation system: @@ -36,6 +40,7 @@ <?php if ($this->layout()->rtl) { $this->headLink()->appendStylesheet('vendor/bootstrap-rtl.min.css'); } ?> + <?php /* finc adds plugin localization #17850 */ ?> <?php if (strcmp($this->layout()->userLang, 'de') == 0) { $this->headScript()->appendFile('vendor/bootstrap-accessibility-de.min.js'); @@ -48,7 +53,7 @@ <?php if (!isset($this->renderingError)) { // Add translation strings - // PDA (BELOW) is a custom finc string - CK + // finc: PDA... (BELOW) is a custom finc string - CK $this->jsTranslations()->addStrings( [ 'pda_send_success' => 'PDA::pda_send_success', @@ -161,6 +166,7 @@ JS; ?> <?=$this->headScript()?> </head> +<?php /* finc: accessibility fixes #17720 */ ?> <?php $searchType = ''; // to determine the searchtype it is necessary to ask the child model for its searchType @@ -258,33 +264,28 @@ if (!isset($this->layout()->searchbox)) { <!-- MODAL IN CASE WE NEED ONE --> -<?php /* move X button to logical pos. in structure + make accessible via tab - CK */ ?> -<div id="modal" class="modal fade hidden-print" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true" aria-describedby="modal-description"> - <div class="modal-dialog"> - <div class="modal-content"> - <button type="button" class="close" data-dismiss="modal" tabindex="0" aria-label="<?= $this->transEsc('CloseModal') ?>"> - <i class="fa fa-times" aria-hidden="true"></i> - </button> - <div class="sr-only" id="modal-description"> - <?=$this->transEsc('Modal_description')?> - </div> - <div class="modal-body"> - <?=$this->transEsc('Loading')?> ... +<?php /* finc: move X button to logical pos. in structure + use 'aria-describedby' instead of 'aria-labelledby - CK */ ?> + <div id="modal" class="modal fade hidden-print" tabindex="-1" role="dialog" aria-modal="true" aria-hidden="true" aria-describedby="modal-description"> + <div class="modal-dialog"> + <div class="modal-content"> + <button type="button" class="close" data-dismiss="modal" tabindex="0" aria-label="<?= $this->transEsc('CloseModal') ?>"><i class="fa fa-times" aria-hidden="true"></i></button> + <?php /* finc: add div for screenreader #13547 */ ?> + <div class="sr-only" id="modal-description"><?=$this->transEsc('Modal_description')?></div> + <div class="modal-body"><?=$this->transEsc('Loading') ?> ...</div> + </div> </div> </div> - </div> -</div> -<div class="offcanvas-overlay" data-toggle="offcanvas"></div> -<?=$this->googleanalytics()?> -<?=$this->piwik()?> -<?php if ($this->recaptcha()->active()): ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::FILE, "https://www.google.com/recaptcha/api.js?onload=recaptchaOnLoad&render=explicit&hl=" . $this->layout()->userLang, 'SET')?> -<?php endif; ?> -<?php /* Enable Tooltips, finc-specific, CK */ ?> -<script type="text/javascript"> - $(document).ready(function () { - $("body").tooltip({selector: '[data-toggle=tooltip]'}); - }); -</script> -</body> + <div class="offcanvas-overlay" data-toggle="offcanvas"></div> + <?=$this->googleanalytics()?> + <?=$this->piwik()?> + <?php if ($this->recaptcha()->active()): ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::FILE, "https://www.google.com/recaptcha/api.js?onload=recaptchaOnLoad&render=explicit&hl=" . $this->layout()->userLang, 'SET')?> + <?php endif; ?> + <?php /* Enable Tooltips, finc-specific, CK */ ?> + <script> + $(document).ready(function () { + $("body").tooltip({selector: '[data-toggle=tooltip]'}); + }); + </script> + </body> </html> diff --git a/themes/finc/templates/librarycards/editcard.phtml b/themes/finc/templates/librarycards/editcard.phtml index 9a7926db76b7240eaabdc86e678bccbaf0ab8cb3..09e3f5b61fe4b679705d6609629bac07aca012ab 100644 --- a/themes/finc/templates/librarycards/editcard.phtml +++ b/themes/finc/templates/librarycards/editcard.phtml @@ -1,7 +1,7 @@ <!-- finc - templates - librarycards - editcard --> <?php // Set up page title: - $pageTitle = empty($this->card->id) ? 'Add a Library Card' : "Edit Library Card"; + $pageTitle = empty($this->card->id) ? 'Add a Library Card' : 'Edit Library Card'; $this->headTitle($this->translate($pageTitle)); // Set up breadcrumbs: @@ -11,7 +11,7 @@ ?> <?=$this->flashmessages()?> - +<?php /* finc uses h1 instead of h2 - #17596 */ ?> <h1><?=$this->transEsc($pageTitle); ?></h1> <form class="form-edit-card" method="post" name="<?=empty($this->card->id) ? 'newCardForm' : 'editCardForm'?>" autocomplete="off"> @@ -31,15 +31,33 @@ </div> <?php endif; ?> <div class="form-group"> - <label class="control-label" for="login_username"><?=$this->transEsc('Username')?>:</label> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <label class="control-label password-login" for="login_username"><?=$this->transEsc('Username')?>:</label> + <?php endif; ?> + <?php if (null === $this->loginMethod || 'email' === $this->loginMethod): ?> + <label class="control-label email-login<?php if (null === $this->loginMethod): ?> hidden<?php endif; ?>" for="login_username"><?=$this->transEsc('Email')?>:</label> + <?php endif; ?> <input id="login_username" type="text" name="username" value="<?=$this->escapeHtmlAttr($this->username)?>" class="form-control"/> </div> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <div class="form-group"> + <label class="control-label" for="login_password"><?=$this->transEsc('Password')?>:</label> + <input id="login_password" type="password" name="password" value="" placeholder="<?=!empty($this->card->id) ? $this->escapeHtmlAttr($this->translate('library_card_edit_password_placeholder')) : ''?>" class="form-control"/> + </div> + <?php endif; ?> <div class="form-group"> - <label class="control-label" for="login_password"><?=$this->transEsc('Password')?>:</label> - <input id="login_password" type="password" name="password" value="" placeholder="<?=!empty($this->card->id) ? $this->escapeHtmlAttr($this->translate('library_card_edit_password_placeholder')) : ''?>" class="form-control"/> - </div> - <div class="form-group"> + <?php /* finc adds aria-label - #17915 */?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>" aria-label="<?=$this->transEsc('Save') ?>"/> </div> </form> + +<?php + if (null !== $targets) { + $methods = json_encode($this->loginMethods); + $script = "setupMultiILSLoginFields($methods, 'login_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + } +?> <!-- finc - templates - librarycards - editcard - END --> diff --git a/themes/finc/templates/librarycards/home.phtml b/themes/finc/templates/librarycards/home.phtml index 25338aa28e5edea51bc2a71d4f43f48a6918bd21..70dca788c13e715e737eb1f0f09eb1f4e3c77a22 100644 --- a/themes/finc/templates/librarycards/home.phtml +++ b/themes/finc/templates/librarycards/home.phtml @@ -7,6 +7,7 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Library Cards') . '</li>'; ?> + <?php /* finc: review offcanvas behaviour - #16701 */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> @@ -15,17 +16,14 @@ <?=$this->flashmessages()?> + <?php /* finc uses h1 instead of h2 - #13547 */ ?> <h1><?=$this->transEsc('Library Cards')?></h1> <?php if ($this->libraryCards->count() == 0): ?> <div><?=$this->transEsc('You do not have any library cards')?></div> <?php else: ?> - <?php /* Table works without further responsiveness code inserted - CK */ ?> + <?php /* finc: this table works without further responsiveness code inserted - CK */ ?> <table class="table table-striped"> - <caption> - <span class="sr-only"> - <?=$this->transEsc('Library Cards')?> - </span> - </caption> + <caption class="sr-only"><?=$this->transEsc('Library Cards')?></caption> <tr> <th><?=$this->transEsc('Library Card Name')?></th> <?php if ($this->multipleTargets): ?> @@ -67,7 +65,8 @@ </div> </div> - <div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> + <?php /* finc adds id 'myresearch-sidebar' */ ?> +<div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'librarycards'])?> - </div> +</div> <!-- finc: librarycards - home - END --> diff --git a/themes/finc/templates/myresearch/account.phtml b/themes/finc/templates/myresearch/account.phtml index f4e40c2f045ce098ba788dd2cc29355671bfa88a..e79e7f8c2e3334ca63ee4cf23dc1d3736028ddbd 100644 --- a/themes/finc/templates/myresearch/account.phtml +++ b/themes/finc/templates/myresearch/account.phtml @@ -7,6 +7,7 @@ // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Account') . '</li>'; ?> +<?php /* finc uses 'User Account' here */ ?> <h2><?=$this->transEsc('Create New Account')?></h2> <?=$this->flashmessages()?> @@ -15,6 +16,7 @@ <?=$this->recaptcha()->html($this->useRecaptcha) ?> <div class="form-group"> <a class="back-to-login btn btn-link" href="<?=$this->url('myresearch-userlogin') ?>"><i class="fa fa-chevron-left" aria-hidden="true"></i> <?=$this->transEsc('Back')?></a> + <?php /* finc uses 'form-button-submit' here */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('form-button-submit')?>" /> </div> </form> diff --git a/themes/finc/templates/myresearch/bulk-action-buttons.phtml b/themes/finc/templates/myresearch/bulk-action-buttons.phtml deleted file mode 100644 index 0f70b338fb60343c7e44a4ec998bba4c2c7538f0..0000000000000000000000000000000000000000 --- a/themes/finc/templates/myresearch/bulk-action-buttons.phtml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- finc: myresearch - bulk-action-buttons --> -<?php if (isset($list)): ?> - <input type="hidden" name="listID" value="<?=$this->escapeHtmlAttr($list->id)?>" /> - <input type="hidden" name="listName" value="<?=$this->escapeHtmlAttr($list->title)?>" /> -<?php endif; ?> -<?php $user = $this->auth()->isLoggedIn(); ?> -<div class="bulkActionButtons"> - <div class="bulk-checkbox"> - <input type="checkbox" name="selectAll" class="checkbox-select-all" id="myresearchCheckAll"/> - <label for="myresearchCheckAll"><?=$this->transEsc('select_page')?> | <?=$this->transEsc('with_selected')?>:</label> - </div> - <div class="btn-group"> - <input id="<?=$this->idPrefix?>ribbon-email" class="btn btn-default" type="submit" name="email" value="<?=$this->transEsc('Email')?>" title="<?=$this->transEsc('email_selected')?>"/> - <?php if ((null !== $this->list && $this->list->editAllowed($user)) || null === $this->list && $user): ?> - <input class="btn btn-default" id="<?=$this->idPrefix?>delete_list_items_<?=null !== $this->list ? $this->escapeHtmlAttr($this->list->id) : ''?>" type="submit" name="delete" value="<?=$this->transEsc('Delete')?>" title="<?=$this->transEsc('delete_selected')?>"/> - <?php endif; ?> - <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> - <input id="<?=$this->idPrefix?>ribbon-export" class="btn btn-default" type="submit" name="export" value="<?=$this->transEsc('Export')?>" title="<?=$this->transEsc('export_selected')?>"/> - <?php endif; ?> - <input id="<?=$this->idPrefix?>ribbon-print" class="btn btn-default" type="submit" name="print" value="<?=$this->transEsc('Print')?>" title="<?=$this->transEsc('print_selected')?>" data-lightbox-ignore/> - <?php if ($this->cart()->isActive()): ?> - <input class="btn btn-default" id="<?=$this->idPrefix?>updateCart" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/> - <?php endif; ?> - </div> -</div> -<!-- finc: myresearch - bulk-action-buttons - END --> \ No newline at end of file diff --git a/themes/finc/templates/myresearch/cataloglogin.phtml b/themes/finc/templates/myresearch/cataloglogin.phtml index 0ae0ff0c85d5f04b38dacde8f2ea2eb076b066f0..78d6e0a35fe11047502724abaf6d0ecdb01671da 100644 --- a/themes/finc/templates/myresearch/cataloglogin.phtml +++ b/themes/finc/templates/myresearch/cataloglogin.phtml @@ -11,6 +11,7 @@ <?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_login_message'])?> <?php else: ?> + <?php /* finc uses h2 here */ ?> <h2><?=$this->transEsc('Library Catalog Profile')?></h2> <?=$this->flashmessages()?> <p> @@ -20,24 +21,44 @@ <form method="post" action="<?=$this->serverUrl(true)?>" class="form-catalog-login"> <?php if ($this->targets !== null): ?> <div class="form-group"> - <label class="control-label" for="login_target"><?=$this->transEsc('login_target')?>:</label> - <select id="login_target" name="target" class="form-control"> + <label class="control-label" for="profile_cat_target"><?=$this->transEsc('login_target')?>:</label> + <select id="profile_cat_target" name="target" class="form-control"> <?php foreach ($this->targets as $target): ?> - <option value="<?=$this->escapeHtmlAttr($target)?>"><?=$this->transEsc("source_$target", null, $target)?></option> + <option value="<?=$this->escapeHtmlAttr($target)?>"<?=($target === $this->defaultTarget ? ' selected="selected"' : '')?>><?=$this->transEsc("source_$target", null, $target)?></option> <?php endforeach; ?> </select> </div> <?php endif; ?> <div class="form-group"> - <label class="control-label" for="profile_cat_username"><?=$this->transEsc('Library Catalog Username')?>:</label> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <label class="control-label password-login" for="profile_cat_username"><?=$this->transEsc('Library Catalog Username')?>:</label> + <?php endif; ?> + <?php if (null === $this->loginMethod || 'email' === $this->loginMethod): ?> + <label class="control-label email-login<?php if (null === $this->loginMethod): ?> hidden<?php endif; ?>" for="profile_cat_username"><?=$this->transEsc('Email')?>:</label> + <?php endif; ?> <input id="profile_cat_username" type="text" name="cat_username" value="" class="form-control"/> </div> + <?php if (null === $this->loginMethod || 'password' === $this->loginMethod): ?> + <div class="form-group"> + <label class="control-label" for="profile_cat_password"><?=$this->transEsc('Library Catalog Password')?>:</label> + <input id="profile_cat_password" type="password" name="cat_password" value="" class="form-control"/> + </div> + <?php else: ?> + <input type="hidden" name="cat_password" value="****"> + <?php endif; ?> <div class="form-group"> - <label class="control-label" for="profile_cat_password"><?=$this->transEsc('Library Catalog Password')?>:</label> - <input id="profile_cat_password" type="password" name="cat_password" value="" class="form-control" autocomplete="current-password"/> - </div> - <div class="form-group"> + <?php /* finc adds aria-label to input */ ?> <input class="btn btn-primary" type="submit" name="processLogin" aria-label="<?= $this->transEsc("Login-to-account") ?>" value="<?=$this->transEsc('Login')?>"> </div> </form> + + <?php + if (null !== $this->targets) { + $methods = json_encode($this->loginMethods); + $script = "setupMultiILSLoginFields($methods, 'profile_cat_');"; + + // Inline the script for lightbox compatibility + echo $this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); + } + ?> <?php endif; ?> diff --git a/themes/finc/templates/myresearch/checkedout.phtml b/themes/finc/templates/myresearch/checkedout.phtml index ed51a1cb7ae77b15378a8722bb69eff6e17a5db6..c311bed115d3ccac7edc74fb18d26cbe8c0ace1d 100644 --- a/themes/finc/templates/myresearch/checkedout.phtml +++ b/themes/finc/templates/myresearch/checkedout.phtml @@ -10,17 +10,20 @@ $renewAll = !$this->ilsPaging || !$paginator; ?> +<?php /* finc removes title and adds aria-label instead */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here, #13547 */ ?> <h1><?=$this->transEsc('Your Checked Out Items')?></h1> <?=$this->flashmessages()?> <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> <?php if (!empty($this->transactions)): ?> + <?php /* finc adds aria-label to nav */ ?> <nav class="search-header hidden-print" aria-label="<?=$this->transEsc('aria_search_header')?>"> <?php if ($paginator): ?> <div class="search-stats"> @@ -43,7 +46,7 @@ <?php endif; ?> </nav> <?php if ($this->renewForm): ?> - <form name="renewals" method="post" id="renewals"> + <form name="renewals" method="post" id="renewals" data-clear-account-cache="checkedOut"> <div class="toolbar"> <div class="checkbox"> <label> @@ -64,9 +67,10 @@ <?php $renewDetails = $this->renewResult[$ilsDetails['item_id']]; ?> <?php $prefix = $ilsDetails['title'] ?? $ilsDetails['item_id']; ?> <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?> - <p class="alert alert-success"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_success')?></p> + <?php /* finc uses <p> and aria for alerts */ ?> + <p class="alert alert-success" aria-live="polite"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_success')?></p> <?php else: ?> - <p class="alert alert-danger"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->escapeHtml($prefix . ': ') . $this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></p> <?php endif; ?> <?php endif; ?> <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_details'])): ?> @@ -75,9 +79,11 @@ <?php endif; ?> <?php endforeach; ?> + <?php /* finc uses ul to present list structure correctly #18535 */ ?> <ul class="record-list"> <?php $i = 0; foreach ($this->transactions as $resource): ?> <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php /* finc adds aria-label and -describedby #18019 */ ?> <?php $describedById = $resource->getSourceIdentifier() . '|' . $resource->getUniqueId(); ?> <li id="record<?=$this->escapeHtmlAttr($resource->getUniqueId())?>" class="result"> <?php if ($this->renewForm): ?> @@ -102,6 +108,7 @@ $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account'); if ($cover): ob_start(); ?> + <?php /* finc adds aria-hidden */ ?> <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>" aria-hidden="true"> <?=$cover ?> </div> @@ -118,11 +125,12 @@ if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); - echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) . + // finc adds empty lang (='undefined') tag + echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) . '" class="title" lang="">' . $title . '</a>'; - } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ - // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... - echo $this->escapeHtml($ilsDetails['title']); + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])) { + // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... + echo $this->escapeHtml($ilsDetails['title']); } else { // Last resort -- indicate that no title could be found. echo $this->transEsc('Title not available'); @@ -151,12 +159,12 @@ <?php endif; ?> <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> + <strong><?=$this->transEscWithPrefix('location_', $ilsDetails['institution_name'])?></strong> <br /> <?php endif; ?> <?php if (!empty($ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> + <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEscWithPrefix('location_', $ilsDetails['borrowingLocation'])?> <br /> <?php endif; ?> @@ -175,24 +183,24 @@ <?php if (isset($renewDetails['success']) && $renewDetails['success']): ?> <?php $showStatus = false; ?> <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($renewDetails['new_date'])?> <?php if (isset($renewDetails['new_time'])): ?><?=$this->escapeHtml($renewDetails['new_time'])?><?php endif; ?></strong> - <p class="alert alert-success"><?=$this->transEsc('renew_success')?></p> + <p class="alert alert-success" aria-live="polite"><?=$this->transEsc('renew_success')?></p> <?php else: ?> <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong> - <p class="alert alert-danger"><?=$this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc('renew_fail')?><?php if (isset($renewDetails['sysMessage'])): ?>: <?=$this->escapeHtml($renewDetails['sysMessage'])?><?php endif; ?></p> <?php endif; ?> <?php else: ?> <strong><?=$this->transEsc('Due Date')?>: <?=$this->escapeHtml($ilsDetails['duedate'])?><?php if (isset($ilsDetails['dueTime'])): ?> <?=$this->escapeHtml($ilsDetails['dueTime'])?><?php endif; ?></strong> <?php if ($showStatus): ?> <?php if (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "overdue"): ?> - <p class="alert alert-danger"><?=$this->transEsc("renew_item_overdue")?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc("renew_item_overdue")?></p> <?php elseif (isset($ilsDetails['dueStatus']) && $ilsDetails['dueStatus'] == "due"): ?> - <p class="alert alert-info"><?=$this->transEsc("renew_item_due")?></p> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc("renew_item_due")?></p> <?php endif; ?> <?php endif; ?> <?php endif; ?> <?php if ($showStatus && isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?> - <p class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></p> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc($ilsDetails['message'])?></p> <?php endif; ?> <?php if (isset($ilsDetails['renewable']) && $ilsDetails['renewable'] && isset($ilsDetails['renew_link'])): ?> <a href="<?=$this->escapeHtmlAttr($ilsDetails['renew_link'])?>"><?=$this->transEsc('renew_item')?></a> @@ -202,7 +210,7 @@ <?=$thumbnail ?> <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl') ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>' : ''?> </li> <?php endforeach; ?> </ul> diff --git a/themes/finc/templates/myresearch/edit.phtml b/themes/finc/templates/myresearch/edit.phtml index 0484ff434fbfb08d8b1806970c946cb7d0545bd3..caee1b907dd5a8ea9f1b543025569126f0311909 100644 --- a/themes/finc/templates/myresearch/edit.phtml +++ b/themes/finc/templates/myresearch/edit.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - myresearch - edit --> -<?php /* copied from bootstrap3 - added h1 for sr-only - #17596 - HR */?> +<?php /* finc adds h1 sr-only - #17596 */?> <?php // Set up page title: @@ -8,14 +8,16 @@ // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Edit') . '</li>'; ?> -<h1 class="sr-only"><?=$this->translate('Edit') . ' : ' . $this->driver->getBreadcrumb()?></h1> + +<?php /* Finc: remove space before color for correct spacing 'Bearbeiten: ....' instead of 'Bearbeiten : ....' */ ?> +<h1 class="sr-only"><?=$this->translate('Edit') . ': ' . $this->driver->getBreadcrumb()?></h1> <div class="record"> <h2><?=$this->escapeHtml($this->driver->getBreadcrumb())?></h2> <form class="form-list-edit" method="post" name="editForm"> <?php if (empty($this->savedData)): ?> - <p class="alert alert-info"> + <p class="alert alert-info" aria-live="polite"> <?php if (isset($listFilter)): ?> <?=$this->transEsc('The record you selected is not part of the selected list.') ?> <?php else: ?> @@ -59,6 +61,7 @@ <?php endif; ?> <?php if (!empty($this->savedData) || count($this->lists) > 0): ?> <div class="form-group"> + <?php /* finc adds aria-label #17915 */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>" aria-label="<?=$this->transEsc('Save') ?>"/> </div> <?php endif; ?> diff --git a/themes/finc/templates/myresearch/editlist.phtml b/themes/finc/templates/myresearch/editlist.phtml index affb476f7e3d4dd00f80ec3a0ba5e51cb2470210..82667d9156e1d031ec105d501361641f0443cab9 100644 --- a/themes/finc/templates/myresearch/editlist.phtml +++ b/themes/finc/templates/myresearch/editlist.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - myresearch - editlist --> -<?php /* copied from bootstrap3 - h2 becomes h1 - #17596 - HR */?> +<?php /* finc uses h1 instead of h2 - #17596 */?> <?php // Set up page title: @@ -13,12 +13,15 @@ <?=$this->flashmessages()?> +<?php /* finc: show my account menu #19722 */ ?> <?php if ($this->auth()->isLoggedIn() && $this->layout()->getTemplate() !== 'layout/lightbox') :?> <div class="<?=$this->layoutClass('mainbody')?>"> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <?php endif; ?> + +<?php /* finc uses h1 here, #17596 */ ?> <h1><?=$this->transEsc($pageTitle); ?></h1> <form class="form-edit-list" method="post" name="<?=empty($this->list->id) ? 'newList' : 'editListForm'?>"> @@ -37,15 +40,16 @@ <input type="hidden" name="public" value="0" /> <?php else: ?> <div class="form-group"> + <?php /* finc: improve form fields search, adds fieldset, uses legend, #19423 */ ?> <fieldset> <legend><?=$this->transEsc('Access') ?></legend> <div class="radio inline"> - <?php /* finc: add for attribute to label; take out input element and put it before label element, #21664 - VE */ ?> + <?php /* finc adds 'for' attribute to label; places input element before label element, #21664 - VE */ ?> <input id="list_public_1" type="radio" name="public" value="1"<?php if ($this->list->isPublic()): ?> checked="checked"<?php endif; ?>/> <label for="list_public_1"><?=$this->transEsc('Public') ?></label> </div> <div class="radio inline"> - <?php /* finc: add for attribute to label; take out input element and put it before label element, #21664 - VE */ ?> + <?php /* finc adds 'for' attribute to label; places input element before label element, #21664 - VE */ ?> <input id="list_public_0" type="radio" name="public" value="0"<?php if (!$this->list->isPublic()): ?> checked="checked"<?php endif; ?>/> <label for="list_public_0"><?=$this->transEsc('Private') ?></label> </div> @@ -53,9 +57,12 @@ </div> <?php endif; ?> <div class="form-group"> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Save') ?>" aria-label="<?=$this->transEsc('Save') ?>"/> </div> </form> + +<?php /* finc: show menu for new edit list in account, #19722 */ ?> <?php if ($this->auth()->isLoggedIn() && $this->layout()->getTemplate() !== 'layout/lightbox') :?> </div> <div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> diff --git a/themes/finc/templates/myresearch/fines.phtml b/themes/finc/templates/myresearch/fines.phtml index a4054ab251ff02135bbac77bc1fddac309500dbb..792d1dedfbed008d0a9313fdabfb701765bc6104 100644 --- a/themes/finc/templates/myresearch/fines.phtml +++ b/themes/finc/templates/myresearch/fines.phtml @@ -7,11 +7,13 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Fines') . '</li>'; ?> +<?php /* finc removes title, adds aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Your Fines')?></h1> <?=$this->flashmessages()?> @@ -39,7 +41,8 @@ $tableData['Title'][] = $title; $tableData['Checked Out'][] = $this->escapeHtml($record['checkout'] ?? ''); $tableData['Due Date'][] = $this->escapeHtml($record['duedate'] ?? ''); - $tableData['Fine'][] = $this->escapeHtml($record['fine'] ?? ''); + $tableData['Fine'][] = $this->transEsc($record['fine'] ?? ''); + /* finc: change 'Fine Date' to 'fine_date_short' for responsive table #17621 */ $tableData['fine_date_short'][] = $this->escapeHtml($record['createdate'] ?? ''); $tableData['Fee'][] = isset($record['amount']) ? $this->safeMoneyFormat($record['amount'] / 100.00) : ''; @@ -66,9 +69,9 @@ $columns = array_keys($tableData); $rowCount = count($this->fines); ?> - <?php /* include responsive data table - CK */ ?> + <?php /* finc adds code for responsive data table - CK */ ?> <table class="table table-striped table-resp-data-md"> - <caption><span class="sr-only"><?=$this->transEsc('Your Fines')?></span></caption> + <caption class="sr-only"><?=$this->transEsc('Your Fines')?></caption> <tr> <?php foreach ($columns as $header): ?> <th><?=$this->transEsc($header)?></th> diff --git a/themes/finc/templates/myresearch/historicloans.phtml b/themes/finc/templates/myresearch/historicloans.phtml index af62dd87d98bfaa44937c69b4ec3f13a6667ce46..4ffe7957fc5ba832cb48f6d2981cb646324d061a 100644 --- a/themes/finc/templates/myresearch/historicloans.phtml +++ b/themes/finc/templates/myresearch/historicloans.phtml @@ -7,17 +7,20 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Loan History') . '</li>'; ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Loan History')?></h1> <?=$this->flashmessages()?> <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->auth()->isLoggedIn()]); ?> <?php if (!empty($this->transactions)): ?> + <?php /* finc adds aria-label */ ?> <nav class="search-header hidden-print" aria-label="<?=$this->transEsc('aria_search_header')?>"> <?php if ($this->paginator): ?> <div class="search-stats"> @@ -40,6 +43,7 @@ <?php endif; ?> </nav> + <?php /* finc uses ul to present list structure correctly, adds empty 'lang' attribute #18535 */ ?> <ul class="record-list"> <?php $i = 0; foreach ($this->transactions as $resource): ?> <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> @@ -96,12 +100,12 @@ <?php endif; ?> <?php if (!empty($ilsDetails['institution_name']) && (empty($ilsDetails['borrowingLocation']) || $ilsDetails['institution_name'] != $ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('location_' . $ilsDetails['institution_name'], [], $ilsDetails['institution_name'])?></strong> + <strong><?=$this->transEscWithPrefix('location_', $ilsDetails['institution_name'])?></strong> <br /> <?php endif; ?> <?php if (!empty($ilsDetails['borrowingLocation'])): ?> - <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEsc('location_' . $ilsDetails['borrowingLocation'], [], $ilsDetails['borrowingLocation'])?> + <strong><?=$this->transEsc('Borrowing Location')?>:</strong> <?=$this->transEscWithPrefix('location_', $ilsDetails['borrowingLocation'])?> <br /> <?php endif; ?> @@ -116,7 +120,8 @@ <?php endif; ?> <?php if (isset($ilsDetails['message']) && !empty($ilsDetails['message'])): ?> - <p class="alert alert-info"><?=$this->transEsc($ilsDetails['message'])?></p> + <?php /* finc uses <p> and aria for alerts */ ?> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc($ilsDetails['message'])?></p> <?php endif; ?> </div> <?php if ($thumbnail && $thumbnailAlignment == 'right'): ?> diff --git a/themes/finc/templates/myresearch/holds.phtml b/themes/finc/templates/myresearch/holds.phtml index 6b584c7355cd127ed8e15dc3c689e737e098636d..f35c5a4999d500b2faaf7239547edafb173e6fff 100644 --- a/themes/finc/templates/myresearch/holds.phtml +++ b/themes/finc/templates/myresearch/holds.phtml @@ -7,11 +7,13 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('My Holds') . '</li>'; ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Your Holds and Recalls') ?></h1> <?=$this->flashmessages()?> @@ -20,10 +22,11 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelHold"> + <form name="cancelForm" class="inline" method="post" id="cancelHold" data-clear-account-cache="holds"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> + <?php /* finc adds aria-label */ ?> <input id="cancelSelected" name="cancelSelected" type="submit" value="<?=$this->transEsc("hold_cancel_selected") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-label="<?=$this->transEsc("hold_cancel_selected") ?>"/> <ul class="dropdown-menu"> <li class="disabled"><a><?=$this->transEsc("confirm_hold_cancel_selected_text") ?></a></li> @@ -32,6 +35,7 @@ </ul> </div> <div class="btn-group"> + <?php /* finc adds aria-label */ ?> <input id="cancelAll" name="cancelAll" type="submit" value="<?=$this->transEsc("hold_cancel_all") ?>" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-label="<?=$this->transEsc("hold_cancel_all") ?>"/> <ul class="dropdown-menu"> <li class="disabled"><a><?=$this->transEsc("confirm_hold_cancel_all_text") ?></a></li> @@ -42,10 +46,12 @@ <?php endif; ?> <?php $iteration = 0; ?> + <?php /* finc uses ul to present list structure correctly #18535 */ ?> <ul class="record-list"> <?php foreach ($this->recordList as $resource): ?> <?php $iteration++; ?> <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php /* finc adds aria-label and -describedby #18019 */ ?> <?php $describedById = $resource->getSourceIdentifier() . '|' . $resource->getUniqueId(); ?> <li id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> @@ -65,6 +71,7 @@ $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account'); if ($cover): ob_start(); ?> + <?php /* finc adds aria-hidden */ ?> <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>" aria-hidden="true"> <?=$cover ?> </div> @@ -79,16 +86,17 @@ <?php // If this is a non-missing Solr record, we should display a link: if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { - $title = $resource->getTitle(); - $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); - echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) + $title = $resource->getTitle(); + $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); + // finc adds empty lang (='undefined') tag + echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) . '" class="title" lang="">' . $title . '</a>'; - } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ - // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... - echo '<span class="title" id="' . $describedById . '" lang="">' . $this->escapeHtml($ilsDetails['title']) . '</span>'; + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])) { + // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... + echo '<span class="title" id="' . $describedById . '" lang="">' . $this->escapeHtml($ilsDetails['title']) . '</span>'; } else { - // Last resort -- indicate that no title could be found. - echo $this->transEsc('Title not available'); + // Last resort -- indicate that no title could be found. + echo $this->transEsc('Title not available'); } ?><br/> <?php $listAuthors = $resource->getPrimaryAuthors(); if (!empty($listAuthors)): ?> @@ -134,7 +142,7 @@ <?php endif; ?> <?php if (!empty($pickupDisplay)): ?> <strong><?=$this->transEsc('pick_up_location') ?>:</strong> - <?=$pickupTranslate ? $this->transEsc('location_' . $pickupDisplay, null, $pickupDisplay) : $this->escapeHtml($pickupDisplay)?> + <?=$pickupTranslate ? $this->transEscWithPrefix('location_', $pickupDisplay) : $this->escapeHtml($pickupDisplay)?> <br /> <?php endif; ?> @@ -147,16 +155,17 @@ <?php endif; ?> <br /> + <?php /* finc uses <p> and aria for alerts */ ?> <?php if (isset($this->cancelResults['items'])): ?> <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <p class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> <?php if (isset($ilsDetails['available']) && $ilsDetails['available'] == true): ?> - <p class="text-success"> + <p class="text-success" aria-live="polite"> <?php if (!empty($ilsDetails['last_pickup_date'])): ?> <?=$this->transEsc('hold_available_until', ['%%date%%' => $ilsDetails['last_pickup_date']]) ?> <?php else: ?> @@ -164,7 +173,7 @@ <?php endif; ?> </p> <?php elseif (isset($ilsDetails['in_transit']) && $ilsDetails['in_transit']): ?> - <p class="text-success"><?=$this->transEsc('request_in_transit') . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></p> + <p class="text-success" aria-live="polite"><?=$this->transEsc('request_in_transit') . (is_string($ilsDetails['in_transit']) ? ': ' . $this->transEsc('institution_' . $ilsDetails['in_transit'], [], $ilsDetails['in_transit']) : '') ?></p> <?php elseif (isset($ilsDetails['position'])): ?> <p><strong><?=$this->transEsc("hold_queue_position") ?>:</strong> <?=$this->escapeHtml($ilsDetails['position']) ?></p> <?php endif; ?> diff --git a/themes/finc/templates/myresearch/illrequests.phtml b/themes/finc/templates/myresearch/illrequests.phtml index 6f87f860b07bdb742a7a115a18a82eabeb1a8a79..1886475a0ba0c1e000c57e8b1ffe4e271aa7df48 100644 --- a/themes/finc/templates/myresearch/illrequests.phtml +++ b/themes/finc/templates/myresearch/illrequests.phtml @@ -8,11 +8,13 @@ . '<li class="active">' . $this->transEsc('Interlibrary Loan Requests') . '</li>'; ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Interlibrary Loan Requests') ?></h1> <?=$this->flashmessages()?> @@ -21,7 +23,7 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelILLRequest"> + <form name="cancelForm" class="inline" method="post" id="cancelILLRequest" data-clear-account-cache="illRequests"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> @@ -43,10 +45,12 @@ <?php endif; ?> <?php $iteration = 0; ?> + <?php /* finc uses ul to present list structure correctly #18535 */ ?> <ul class="record-list"> <?php foreach ($this->recordList as $resource): ?> <?php $iteration++; ?> <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php /* finc adds aria-label and -describedby #18019 */ ?> <?php $describedById = $resource->getSourceIdentifier() . '|' . $resource->getUniqueId(); ?> <li id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> @@ -82,6 +86,7 @@ if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); + // finc adds empty lang (='undefined') tag echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) . '" class="title" lang="">' . $title . '</a>'; } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ @@ -143,10 +148,11 @@ <?php endif; ?> <br /> + <?php /* finc uses <p> and role for alerts */ ?> <?php if (isset($this->cancelResults['items'])): ?> <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <p class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> + <p class="alert alert-danger" role="alert"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> diff --git a/themes/finc/templates/myresearch/menu.phtml b/themes/finc/templates/myresearch/menu.phtml index f639faf74d577057e7b24a0d6a34b8cdd74aa6d4..e98fc9528ea9185bc197a1982ae46cd2e9c943f5 100644 --- a/themes/finc/templates/myresearch/menu.phtml +++ b/themes/finc/templates/myresearch/menu.phtml @@ -1,48 +1,49 @@ <!-- finc: myresearch - menu --> <?php -$user = $this->auth()->isLoggedIn(); -$patron = $user ? $this->auth()->getILSPatron() : false; -$capabilityParams = $patron ? ['patron' => $patron] : []; + $user = $this->auth()->isLoggedIn(); + $patron = $user ? $this->auth()->getILSPatron() : false; + $capabilityParams = $patron ? ['patron' => $patron] : []; + $ilsOnline = ('ils-none' !== $this->ils()->getOfflineMode()); ?> +<?php /* finc change btn-link to btn-default */ ?> <button class="close-offcanvas btn btn-default" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button> - +<?php /* finc: change h3 to h2 */ ?> <h2><?=$this->transEsc('Your Account')?></h2> +<?php /* finc needs to add .facet-group class and classes on sub items for borders - CK */ + /* also adds aria-current for correct menu action */ ?> <?php /* finc needs to add .facet-group class and classes on sub items for borders - CK */ ?> +<?php /* finc: myreasearch menu as list #19734 */ ?> +<?php /* finc: specify current page menu entry in following elements #19941 */ ?> <ul class="myresearch-menu account-menu facet-group"> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> - <li class="facet"> <a href="<?=$this->url('myresearch-favorites')?>"<?=$this->active == 'favorites' ? ' class="active" aria-current="page"' : ''?>> - <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Favorites')?> - </a> + <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Favorites')?> + </a> </li> - <?php endif; ?> - <?php if ('ils-none' !== $this->ils()->getOfflineMode()): ?> - <?php if ($this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?> - - <li class="facet"> - <a href="<?=$this->url('myresearch-checkedout')?>" class="flex checkedout<?=$this->active == 'checkedout' ? ' active' : ''?>" - <?=$this->active == 'checkedout' ? ' aria-current="page"' : ''?> - > - <span class="flex-col"><i class="fa fa-fw fa-book" aria-hidden="true"></i> <?=$this->transEsc('Checked Out Items')?></span> - <span class="checkedout-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> - <?php /* nxt line finc specific - CK */ ?> - <span id="getMyTransactions" class="itemCount pull-right no-padding"></span> - </a> - </li> - <?php endif; ?> - <?php if ($this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?> - <li class="facet"> - <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active" aria-current="page"' : ''?>> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyTransactions', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-checkedout')?>" class="flex checkedout<?=$this->active == 'checkedout' ? ' active' : ''?>" + <?=$this->active == 'checkedout' ? ' aria-current="page"' : ''?> + > + <span class="flex-col"><i class="fa fa-fw fa-book" aria-hidden="true"></i> <?=$this->transEsc('Checked Out Items')?></span> + <span class="checkedout-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> + <?php /* nxt line finc specific - CK */ ?> + <span id="getMyTransactions" class="itemCount pull-right no-padding"></span> + </a> + </li> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('getMyTransactionHistory', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-historicloans')?>"<?=$this->active == 'historicloans' ? ' class="active" aria-current="page"' : ''?>> <i class="fa fa-fw fa-history" aria-hidden="true"></i> <?=$this->transEsc('Loan History')?> </a> - </li> - <?php endif; ?> - <?php if ($this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?> - - <li class="facet"> - <a href="<?=$this->url('myresearch-holds')?>" class="flex<?=$this->active == 'holds' ? ' active' : ''?>" + </li> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyHolds', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-holds')?>" class="flex<?=$this->active == 'holds' ? ' active' : ''?>" <?=$this->active == 'holds' ? ' aria-current="page"' : ''?> > <span class="flex-col"><i class="fa fa-fw fa-flag" aria-hidden="true"></i> <?=$this->transEsc('Holds and Recalls')?></span> @@ -50,12 +51,11 @@ $capabilityParams = $patron ? ['patron' => $patron] : []; <?php /* nxt line finc specific - CK */ ?> <span id="getMyHolds" class="itemCount pull-right no-padding"></span> </a> - </li> - <?php endif; ?> - - <?php if ($this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?> - <li class="facet"> - <a href="<?=$this->url('myresearch-storageretrievalrequests')?>" class="flex<?=$this->active == 'storageRetrievalRequests' ? ' active' : ''?>" + </li> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('StorageRetrievalRequests', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-storageretrievalrequests')?>" class="flex<?=$this->active == 'storageRetrievalRequests' ? ' active' : ''?>" <?=$this->active == 'storageRetrievalRequests' ? ' aria-current="page"' : ''?> > <span class="flex-col"><i class="fa fa-fw fa-archive" aria-hidden="true"></i> <?=$this->transEsc('Storage Retrieval Requests')?></span> @@ -63,12 +63,11 @@ $capabilityParams = $patron ? ['patron' => $patron] : []; <?php /* nxt line finc specific - CK */ ?> <span id="getMyStorageRetrievalRequests" class="itemCount pull-right no-padding"></span> </a> - </li> - <?php endif; ?> - - <?php if ($this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?> - <li class="facet"> - <a href="<?=$this->url('myresearch-illrequests')?>" class="flex<?=$this->active == 'ILLRequests' ? ' active' : ''?>" + </li> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkFunction('ILLRequests', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-illrequests')?>" class="flex<?=$this->active == 'ILLRequests' ? ' active' : ''?>" <?=$this->active == 'ILLRequests' ? ' aria-current="page"' : ''?> > <span class="flex-col"><i class="fa fa-fw fa-exchange" aria-hidden="true"></i> <?=$this->transEsc('Interlibrary Loan Requests')?></span> @@ -76,72 +75,66 @@ $capabilityParams = $patron ? ['patron' => $patron] : []; <?php /* nxt line finc specific - CK */ ?> <span id="getMyILLRequests" class="itemCount pull-right no-padding"></span> </a> - </li> - <?php endif; ?> - - <?php if ($this->ils()->checkCapability('getMyFines', $capabilityParams)): ?> - <li class="facet"> - <a href="<?=$this->url('myresearch-fines')?>" class="flex<?=$this->active == 'fines' ? ' active' : ''?>" + </li> + <?php endif; ?> + <?php if ($ilsOnline && $this->ils()->checkCapability('getMyFines', $capabilityParams)): ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-fines')?>" class="flex<?=$this->active == 'fines' ? ' active' : ''?>" <?=$this->active == 'fines' ? ' aria-current="page"' : ''?> > <span class="flex-col"><i class="fa fa-fw fa-usd" aria-hidden="true"></i> <?=$this->transEsc('Fines')?></span> <span class="fines-status status hidden"><i class="fa fa-spin fa-spinner" aria-hidden="true"></i></span> </a> - </li> - <?php endif; ?> - <li class="facet"> - <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active" aria-current="page"' : ''?>> + </li> + <?php endif; ?> + <li class="facet"> + <a href="<?=$this->url('myresearch-profile')?>"<?=$this->active == 'profile' ? ' class="active" aria-current="page"' : ''?>> <i class="fa fa-fw fa-user" aria-hidden="true"></i> <?=$this->transEsc('Profile')?> </a> - </li> - <?php if ($user && $user->libraryCardsEnabled()): ?> - <li class="facet"> - <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active" aria-current="page"' : ''?>> + </li> + <?php if ($ilsOnline && $user && $user->libraryCardsEnabled()): ?> + <li class="facet"> + <a href="<?=$this->url('librarycards-home')?>"<?=$this->active == 'librarycards' ? ' class="active" aria-current="page"' : ''?>> <i class="fa fa-fw fa-barcode" aria-hidden="true"></i> <?=$this->transEsc('Library Cards')?> </a> - </li> - <?php endif; ?> + </li> + <?php endif; ?> + <?php if ($this->overdrive()->showMyContentLink()):?> + <li class="facet"> + <a href="<?=$this->url('overdrive-mycontent')?>"<?=$this->active == 'dgcontent' ? ' class="active"' : ''?>> + <i class="fa fa-fw fa-download" aria-hidden="true"></i> <?=$this->transEsc('Overdrive Content')?> + </a> + </li> <?php endif; ?> <?php if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> <li class="facet"> <a href="<?=$this->url('search-history')?>?require_login"<?=$this->active == 'history' ? ' class="active" aria-current="page"' : ''?>> - <i class="fa fa-fw fa-search" aria-hidden="true"></i> <?=$this->transEsc('history_saved_searches')?> - </a> + <i class="fa fa-fw fa-search" aria-hidden="true"></i> <?=$this->transEsc('history_saved_searches')?> + </a> </li> <?php endif; ?> <?php if ($user): ?> <li class="facet"> <a href="<?=$this->url('myresearch-logout')?>"> - <i class="fa fa-fw fa-sign-out" aria-hidden="true"></i> <?=$this->transEsc("Log Out")?> - </a> + <i class="fa fa-fw fa-sign-out" aria-hidden="true"></i> <?=$this->transEsc("Log Out")?> + </a> </li> <?php endif; ?> </ul> -<?php if ($this->auth()->isLoggedIn() && $this->auth()->getManager()->supportsPasswordChange()): ?> - <h3><?=$this->transEsc('Preferences')?></h3> - <ul class="myresearch-menu facet-group"> - <li class="facet"> - <a href="<?=$this->url('myresearch-changepassword')?>"<?=$this->active == 'newpassword' ? ' class="active"' : ''?> - <?=$this->active == 'newpassword' ? ' aria-current="page"' : ''?> - > - <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?=$this->transEsc('Change Password')?> - </a> - </li> - </ul> -<?php endif; ?> - <?php if ($user && $this->userlist()->getMode() !== 'disabled'): ?> - <h3><?=$this->transEsc('Your Lists')?></h3> + <?php /* finc adds '.lists-heading' for styling purposes */ ?> + <h3 class="lists-heading"><?=$this->transEsc('Your Lists')?></h3> + <?php /* finc: change menu into list */ ?> <ul class="myresearch-menu facet-group"> <li class="facet"> + <?php /* finc adds aria-current */ ?> <a href="<?=$this->url('myresearch-favorites')?>"<?=$this->active == 'favorites' ? ' class="active"' : ''?> <?=$this->active == 'favorites' ? ' aria-current="page"' : ''?> > - <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Your Favorites')?> - </a> + <i class="fa fa-fw fa-star" aria-hidden="true"></i> <?=$this->transEsc('Your Favorites')?> + </a> </li> - <?php $lists = $user->getLists() ?> <?php foreach ($lists as $list): ?> <?php /* finc: keep icon inside + keep braces in badge!; CK*/ ?> @@ -149,20 +142,18 @@ $capabilityParams = $patron ? ['patron' => $patron] : []; <a href="<?=$this->url('userList', ['id' => $list['id']])?>"<?=$this->active == 'list' . $list['id'] ? ' class="active"' : ''?> <?=$this->active == 'list' . $list['id'] ? ' aria-current="page"' : ''?> > - <i class="fa fa-fw fa-star-o" aria-hidden="true"></i> <?=$this->escapeHtml($list['title'])?> - <span class="badge">(<?=$list->cnt?>)</span> - </a> + <i class="fa fa-fw fa-star-o" aria-hidden="true"></i> <?=$this->escapeHtml($list['title'])?> + <span class="badge">(<?=$list->cnt?>)</span> + </a> </li> - <?php endforeach; ?> <li class="facet"> <a href="<?=$this->url('editList', ['id' => 'NEW'])?>"<?=$this->active == 'editlist/NEW' ? ' class="active"' : ''?> <?=$this->active == 'editlist/NEW' ? ' aria-current="page"' : ''?> > - <i class="fa fa-fw fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Create a List')?> - </a> + <i class="fa fa-fw fa-plus" aria-hidden="true"></i> <?=$this->transEsc('Create a List')?> + </a> </li> - </ul> <?php endif ?> <!-- finc: myresearch - menu - END --> diff --git a/themes/finc/templates/myresearch/mylist.phtml b/themes/finc/templates/myresearch/mylist.phtml index 848b938300395b3cab7575802dd89767101e0f68..a38f0584fce0e1105408508d14099b8111ceeab5 100644 --- a/themes/finc/templates/myresearch/mylist.phtml +++ b/themes/finc/templates/myresearch/mylist.phtml @@ -1,62 +1,67 @@ <!-- finc: myresearch - mylist --> <?php -// Grab list object from search results (if applicable): -$list = $this->results->getListObject(); + // Grab list object from search results (if applicable): + $list = $this->results->getListObject(); -// Set up page title: -$this->headTitle(isset($list) ? $list->title : $this->translate('Favorites')); + // Set up page title: + $this->headTitle(isset($list) ? $list->title : $this->translate('Favorites')); -// Set up breadcrumbs: -$currPage = isset($list) ? 'List' : 'Favorites'; -$this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc($currPage) . '</li>'; + // Set up breadcrumbs: + $currPage = isset($list) ? 'List' : 'Favorites'; + $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc($currPage) . '</li>'; -// Load Javascript dependencies into header: -$this->headScript()->appendFile("check_item_statuses.js"); + // Load Javascript dependencies into header: + $this->headScript()->appendFile("check_item_statuses.js"); -// Load Javascript only if list view parameter is NOT full: -if ($this->params->getOptions()->getListViewOption() != "full") { - $this->headScript()->appendFile("record.js"); - $this->headScript()->appendFile("embedded_record.js"); -} + // Load Javascript only if list view parameter is NOT full: + if ($this->params->getOptions()->getListViewOption() != "full") { + $this->headScript()->appendFile("record.js"); + $this->headScript()->appendFile("embedded_record.js"); + } -$recordTotal = $this->results->getResultTotal(); + $recordTotal = $this->results->getResultTotal(); -// Convenience variable: -$account = $this->auth()->getManager(); -$user = $this->auth()->isLoggedIn(); + // Convenience variable: + $account = $this->auth()->getManager(); + $user = $this->auth()->isLoggedIn(); ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> - <?=$this->transEsc('Your Account') ?> + <?=$this->transEsc('Your Account') ?> </a> + <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$list ? $this->escapeHtml($list->title) : $this->transEsc("Your Favorites")?></h1> <?=$this->flashmessages()?> + <?php /* finc adds aria-label */ ?> <nav class="search-header hidden-print" aria-label="<?=$this->transEsc('aria_search_header')?>"> <div class="search-stats"> <?php if ($recordTotal > 0): ?> <?php - $transParams = [ - '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), - '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), - '%%total%%' => $this->localizedNumber($recordTotal) - ]; + $transParams = [ + '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), + '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), + '%%total%%' => $this->localizedNumber($recordTotal) + ]; ?> - <?=$this->translate('showing_items_of_html', $transParams);?> + <?=$this->translate('showing_items_of_html', $transParams); ?> <?php endif; ?> </div> <div class="search-controls"> <?php if (isset($list)): ?> <?php if ($list->editAllowed($account->isLoggedIn())): ?> - <a href="<?=$this->url('editList', ['id' => $list->id])?>" class="btn btn-link"><i class="fa fa-edit" aria-hidden="true"></i> <?=$this->transEsc("edit_list")?></a> + <a href="<?=$this->url('editList', ['id' => $list->id]) ?>" class="btn btn-link"><i class="fa fa-edit" aria-hidden="true"></i> <?=$this->transEsc("edit_list")?></a> <div class="btn-group"> - <a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="<?=$this->url('myresearch-deletelist')?>?listID=<?=urlencode($list->id)?>"> + <a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="<?=$this->url('myresearch-deletelist') ?>?listID=<?=urlencode($list->id)?>"> <i class="fa fa-trash-o" aria-hidden="true"></i> <?=$this->transEsc("delete_list")?> </a> <ul class="dropdown-menu"> - <li><a href="<?=$this->url('myresearch-deletelist')?>?listID=<?=urlencode($list->id)?>&confirm=1"><?=$this->transEsc('confirm_dialog_yes')?></a></li> + <li><a href="<?=$this->url('myresearch-deletelist') ?>?listID=<?=urlencode($list->id)?>&confirm=1"><?=$this->transEsc('confirm_dialog_yes') ?></a></li> + <?php /* finc adds dropdown-abort to restore focus on dd-toggle after abort in mylist # 20377 */ ?> <li><a href="#" class="dropdown-abort"><?=$this->transEsc('confirm_dialog_no')?></a></li> </ul> </div> @@ -69,11 +74,13 @@ $user = $this->auth()->isLoggedIn(); </div> </nav> <?php if ($list && !empty($list->description)): ?> + <?php /* finc adds class-name */ ?> <p class="list-desc"><?=$this->escapeHtml($list->description)?></p> <?php endif; ?> <?php if ($recordTotal > 0): ?> <form class="form-inline" method="post" name="bulkActionForm" action="<?=$this->url('cart-myresearchbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler"> <?=$this->context($this)->renderInContext('myresearch/bulk-action-buttons.phtml', ['idPrefix' => '', 'list' => $list ?? null, 'account' => $this->account])?> + <?php /* finc uses ul to present list structure correctly #18535 */ ?> <ul class="record-list"> <?php foreach ($this->results->getResults() as $i => $current): ?> <?=$this->record($current)->getListEntry($list, $user)?> @@ -92,6 +99,7 @@ $user = $this->auth()->isLoggedIn(); <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => isset($list) ? 'list' . $list['id'] : 'favorites'])?> <?php endif; ?> <?php foreach ($this->results->getRecommendations('side') as $current): ?> + <?php /* finc adds br-element #16050 */ ?> <br/><?=$this->recommend($current)?> <?php endforeach; ?> </div> diff --git a/themes/finc/templates/myresearch/newpassword.phtml b/themes/finc/templates/myresearch/newpassword.phtml index e493278a4bcba753d7aed4bbb79edfc44bffe16a..1acd090f9232defd227c83a058cdebbb6f7a7323 100644 --- a/themes/finc/templates/myresearch/newpassword.phtml +++ b/themes/finc/templates/myresearch/newpassword.phtml @@ -8,12 +8,14 @@ . '<li class="active">' . $this->transEsc('Create New Password') . '</li>'; ?> <?php if ($this->auth()->isLoggedIn()): ?> + <?php /* finc adds 'a' element for offcanvas behaviour on xs, #17601 */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> <?php endif; ?> +<?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Create New Password') ?></h1> <?=$this->flashmessages() ?> @@ -30,6 +32,7 @@ <?=$this->auth()->getNewPasswordForm() ?> <?=$this->recaptcha()->html($this->useRecaptcha) ?> <div class="form-group"> + <?php /* finc uses 'form-button-submit' */ ?> <input class="btn btn-primary" name="submit" type="submit" value="<?=$this->transEsc('form-button-submit')?>" /> </div> </form> @@ -37,6 +40,7 @@ <?php if ($this->auth()->isLoggedIn()): ?> </div> + <?php /* finc adds id to sidebar */ ?> <div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'newpassword'])?> </div> diff --git a/themes/finc/templates/myresearch/profile.phtml b/themes/finc/templates/myresearch/profile.phtml index 8360cd61d7bb948c9826c2a4ef1c035eaa4718fe..e7814c188ae85f99fcccc4728c5291b48aa833b9 100644 --- a/themes/finc/templates/myresearch/profile.phtml +++ b/themes/finc/templates/myresearch/profile.phtml @@ -2,100 +2,114 @@ <?php // Set up page title: $this->headTitle($this->translate('My Profile')); - + // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Profile') . '</li>'; - + // Template for use by the renderArray helper: $arrTemplate = '<tr><th>%%LABEL%%:</th><td> %%VALUE%%</td></tr>'; ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Your Profile')?></h1> <?=$this->flashmessages();?> - <?php /* works well without inserting resp data titles - CK */ ?> + <?php /* This table works well without inserting responsive data titles - CK */ ?> <table class="table table-striped"> - <?= $this->renderArray( - $arrTemplate, $this->user, - [ - $this->transEsc('First Name') => 'firstname', - $this->transEsc('Last Name') => 'lastname', - // finc: show e-mail in table below - // $this->transEsc('Email') => 'email', - ] - ) ?> - <?php if (count($this->pickup ?? []) > 1): // Skip form if only one location: ?> - <tr> - <th><?= $this->transEsc('Preferred Library') ?>:</th> - <?php - $selected = (strlen($this->profile['home_library'] ?? '') > 0) + <?=$this->renderArray( + $arrTemplate, $this->user, + [ + $this->transEsc('First Name') => 'firstname', + $this->transEsc('Last Name') => 'lastname', + // finc: uses show e-mail in table below + // $this->transEsc('Email') => 'email', + ] + )?> + <?php /* this section renders under two distinct circumstances; see if/else below: */ ?> + <?php if (count($this->pickup ?? []) > 1 || !empty($this->preferredLibraryDisplay)): ?> + <tr> + <th><?=$this->transEsc('Preferred Library')?>:</th> + <td> + <?php if (count($this->pickup ?? []) > 1): // case 1: set home library allowed ?> + <?php + $selected = (strlen($this->profile['home_library'] ?? '') > 0) ? $this->profile['home_library'] : $this->defaultPickupLocation - ?> - <td> - <form id="profile_form" class="form-inline" method="post"> - <select id="home_library" name="home_library" class="form-control"> - <?php foreach ($this->pickup as $lib): ?> - <option - value="<?= $this->escapeHtmlAttr($lib['locationID']) ?>"<?= ($selected == $lib['locationID']) ? ' selected="selected"' : '' ?>><?= $this->transEsc('location_' . $lib['locationDisplay'], null, $lib['locationDisplay']) ?></option> - <?php endforeach; ?> - </select> - <input class="btn btn-default" type="submit" value="<?= $this->transEsc('Save') ?>" aria-label="<?= $this->transEsc('Save') ?>"/> - </form> - </td> + ?> + <form id="profile_form" class="form-inline" method="post"> + <select id="home_library" name="home_library" class="form-control"> + <?php foreach ($this->pickup as $lib): ?> + <option value="<?=$this->escapeHtmlAttr($lib['locationID'])?>"<?=($selected == $lib['locationID'])?' selected="selected"':''?>><?=$this->transEscWithPrefix('location_', $lib['locationDisplay'])?></option> + <?php endforeach; ?> + </select> + <input class="btn btn-default" type="submit" value="<?=$this->transEsc('Save')?>" /> + </form> + <?php else: // case 2: set home library disallowed, but default provided by ILS ?> + <?=$this->transEscWithPrefix('location_', $this->preferredLibraryDisplay)?> + <?php endif; ?> + </td> + </tr> <?php endif; ?> </table> <div id="account-actions"> - <?php if ($this->auth()->getManager()->supportsPasswordChange()): ?> - <a class="btn btn-default" href="<?= $this->url('myresearch-changepassword') ?>"> - <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?= $this->transEsc('Change Password') ?> - </a> - <?php endif; ?> - - <?php if ($this->accountDeletion): ?> - <a class="btn btn-default" href="<?= $this->url('myresearch-deleteaccount') ?>" data-lightbox> - <i class="fa fa-times"></i> <?= $this->transEsc('delete_account_title') ?> - </a> - <?php endif; ?> - </div> - - <?php if (is_array($this->profile)): ?> - <h2><?= $this->transEsc('Library Catalog Profile') ?></h2> - <p> - <?= $this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->user]); ?> - </p> - <?php /* Table works without further responsiveness code inserted - CK */ ?> - <table class="table table-striped"> - <?= $this->renderArray( - $arrTemplate, $this->profile, - [ - $this->transEsc('First Name') => 'firstname', - $this->transEsc('Last Name') => 'lastname', - $this->transEsc('Address') . ' 1' => 'address1', - $this->transEsc('Address') . ' 2' => 'address2', - $this->transEsc('Zip') => 'zip', - $this->transEsc('City') => 'city', - $this->transEsc('Country') => 'country', - // finc: show e-mail here - $this->transEsc('Email') => 'email', - $this->transEsc('Phone Number') => 'phone', - $this->transEsc('Mobile Number') => 'mobile_phone', - $this->transEsc('Group') => 'group', - $this->transEsc('Expires') => 'expiration_date' - ] - ) ?> - </table> - <?php elseif ('ils-none' !== $this->ils()->getOfflineMode() && $this->patronLoginView && !empty($this->patronLoginView->getTemplate())): ?> - <?= $this->partial($this->patronLoginView); ?> + <?php if ($this->auth()->getManager()->supportsEmailChange()): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-changeemail') ?>"> + <i class="fa fa-fw fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Change Email Address') ?> + </a> + <?php endif; ?> + + <?php if ($this->auth()->getManager()->supportsPasswordChange()): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-changepassword') ?>"> + <i class="fa fa-fw fa-lock" aria-hidden="true"></i> <?=$this->transEsc('Change Password') ?> + </a> <?php endif; ?> + + <?php if ($this->accountDeletion): ?> + <a class="btn btn-default" href="<?=$this->url('myresearch-deleteaccount') ?>" data-lightbox> + <i class="fa fa-times"></i> <?=$this->transEsc('delete_account_title') ?> + </a> + <?php endif; ?> + </div> + + <?php if (is_array($this->profile)): ?> + <?php /* finc: change h3 to h2 */ ?> + <h2><?=$this->transEsc('Library Catalog Profile')?></h2> + <p> + <?=$this->context($this)->renderInContext('librarycards/selectcard.phtml', ['user' => $this->user]); ?> + </p> + <?php /* Table works without further responsiveness code inserted - CK */ ?> + <table class="table table-striped"> + <?=$this->renderArray( + $arrTemplate, $this->profile, + [ + $this->transEsc('First Name') => 'firstname', + $this->transEsc('Last Name') => 'lastname', + $this->transEsc('Address') . ' 1' => 'address1', + $this->transEsc('Address') . ' 2' => 'address2', + $this->transEsc('Zip') => 'zip', + $this->transEsc('City') => 'city', + $this->transEsc('Country') => 'country', + // finc: show e-mail here + $this->transEsc('Email') => 'email', + $this->transEsc('Phone Number') => 'phone', + $this->transEsc('Mobile Number') => 'mobile_phone', + $this->transEsc('Group') => 'group', + $this->transEsc('Expires') => 'expiration_date' + ] + )?> + </table> + <?php elseif ('ils-none' !== $this->ils()->getOfflineMode() && $this->patronLoginView && !empty($this->patronLoginView->getTemplate())): ?> + <?=$this->partial($this->patronLoginView);?> + <?php endif; ?> </div> <div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> - <?= $this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'profile']) ?> + <?=$this->context($this)->renderInContext("myresearch/menu.phtml", ['active' => 'profile'])?> </div> <!-- finc: myresearch - profile - END --> diff --git a/themes/finc/templates/myresearch/setpin.phtml b/themes/finc/templates/myresearch/setpin.phtml index 12ecc98e1b0b0501abe6ee5f9da94f1970a433b2..0bfb500aba6aa2beb2978ac1b363c44f31d9b794 100644 --- a/themes/finc/templates/myresearch/setpin.phtml +++ b/themes/finc/templates/myresearch/setpin.phtml @@ -15,7 +15,7 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . <?php endif; ?> <h1><?=$this->transEsc('LiberoAccount::change_user_pin')?></h1> - <p class="alert alert-info"><?=$this->transEsc('LiberoAccount::set_pin_note')?></p> + <p class="alert alert-info" aria-live="polite"><?=$this->transEsc('LiberoAccount::set_pin_note')?></p> <?=$this->flashmessages()?> <?php if (!$this->auth()->getManager()->supportsPasswordChange($this->auth_method)): ?> diff --git a/themes/finc/templates/myresearch/storageretrievalrequests.phtml b/themes/finc/templates/myresearch/storageretrievalrequests.phtml index a7ec9c253467e5a0395fceac4257cbfb79c20a62..602b9bbd61d413036f33a819f7c75443b5240289 100644 --- a/themes/finc/templates/myresearch/storageretrievalrequests.phtml +++ b/themes/finc/templates/myresearch/storageretrievalrequests.phtml @@ -7,11 +7,13 @@ $this->layout()->breadcrumbs = '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li> <li class="active">' . $this->transEsc('Storage Retrieval Requests') . '</li>'; ?> +<?php /* finc: remove title, add aria-label */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <div class="<?=$this->layoutClass('mainbody')?>"> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Storage Retrieval Requests') ?></h1> <?=$this->flashmessages()?> @@ -20,7 +22,7 @@ <?php if (!empty($this->recordList)): ?> <?php if ($this->cancelForm): ?> - <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest"> + <form name="cancelForm" class="inline" method="post" id="cancelStorageRetrievalRequest" data-clear-account-cache="storageRetrievalRequests"> <input type="hidden" id="submitType" name="cancelSelected" value="1"/> <input type="hidden" id="cancelConfirm" name="confirm" value="0"/> <div class="btn-group"> @@ -42,10 +44,12 @@ <?php endif; ?> <?php $iteration = 0; ?> + <?php /* finc uses ul to present list structure correctly #18535 */ ?> <ul class="record-list"> <?php foreach ($this->recordList as $resource): ?> <?php $iteration++; ?> <?php $ilsDetails = $resource->getExtraDetail('ils_details'); ?> + <?php /* finc adds aria-label and -describedby #18019 */ ?> <?php $describedById = $resource->getSourceIdentifier() . '|' . $resource->getUniqueId(); ?> <li id="record<?=$this->escapeHtmlAttr($resource->getUniqueId()) ?>" class="result"> <?php if ($this->cancelForm && isset($ilsDetails['cancel_details'])): ?> @@ -65,6 +69,7 @@ $thumbnailAlignment = $this->record($resource)->getThumbnailAlignment('account'); if ($cover): ob_start(); ?> + <?php /* finc adds aria-hidden */ ?> <div class="media-<?=$thumbnailAlignment ?> <?=$this->escapeHtmlAttr($coverDetails['size'])?>" aria-hidden="true"> <?=$cover ?> </div> @@ -81,9 +86,10 @@ if (is_a($resource, 'VuFind\\RecordDriver\\SolrDefault') && !is_a($resource, 'VuFind\\RecordDriver\\Missing')) { $title = $resource->getTitle(); $title = empty($title) ? $this->transEsc('Title not available') : $this->escapeHtml($title); + // finc adds empty lang (='undefined') tag echo '<a id="' . $describedById . '" href="' . $this->recordLink()->getUrl($resource) - . '" class="title" lang="">' . $title . '</a>'; - } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])){ + . '" class="title" lang="">' . $title . '</a>'; + } elseif (isset($ilsDetails['title']) && !empty($ilsDetails['title'])) { // If the record is not available in Solr, perhaps the ILS driver sent us a title we can show... echo $this->escapeHtml($ilsDetails['title']); } else { @@ -142,10 +148,11 @@ <?php endif; ?> <br /> + <?php /* finc changes div to p for alerts, plus aria */ ?> <?php if (isset($this->cancelResults['items'])): ?> <?php foreach ($this->cancelResults['items'] as $itemId => $cancelResult): ?> <?php if ($itemId == $ilsDetails['item_id'] && $cancelResult['success'] == false): ?> - <p class="alert alert-danger"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc($cancelResult['status']) ?><?php if ($cancelResult['sysMessage']) echo ' : ' . $this->transEsc($cancelResult['sysMessage']); ?></p> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> @@ -167,7 +174,7 @@ <?=$thumbnail ?> <?php endif ?> </div> - <?=$resource->tryMethod('supportsCoinsOpenUrl')?'<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>':''?> + <?=$resource->tryMethod('supportsCoinsOpenUrl') ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($resource->getCoinsOpenUrl()) . '"></span>' : ''?> </li> <?php endforeach; ?> </ul> diff --git a/themes/finc/templates/record/addtag.phtml b/themes/finc/templates/record/addtag.phtml index 6d1a3cc3bc5c93e78e7c3bacaf26858c7d11ef25..fa0090264800e380a9bd36a44c4bfe43e5b8a6ff 100644 --- a/themes/finc/templates/record/addtag.phtml +++ b/themes/finc/templates/record/addtag.phtml @@ -8,7 +8,7 @@ . '<li>' . $this->recordLink()->getBreadcrumb($this->driver) . '</li> ' . '<li class="active">' . $this->transEsc('Add Tag') . '</li>'; ?> - +<?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Add Tags') ?></h1> <form method="post" name="tagRecord" class="form-add-tag" data-lightbox-onclose="refreshTagListCallback"> <input type="hidden" name="submit" value="1" /> diff --git a/themes/finc/templates/record/cart-buttons.phtml b/themes/finc/templates/record/cart-buttons.phtml index beaa7cbe11028a7347ce015293c8cd923e53f0b9..51e5abe25d2bb7bf1b13f159165acee67a62db76 100644 --- a/themes/finc/templates/record/cart-buttons.phtml +++ b/themes/finc/templates/record/cart-buttons.phtml @@ -1,27 +1,29 @@ <!-- finc: record - cart-buttons --> <?php $cart = $this->cart(); ?> <?php if ($cart->isActive()): ?> - <?php $cartId = $this->source . '|' . $this->id; ?> + <?php /* finc changes span to div */ ?> <div class="btn-bookbag-toggle" data-cart-id="<?=$this->escapeHtmlAttr($this->id)?>" data-cart-source="<?=$this->escapeHtmlAttr($this->source)?>"> - <?php /* Make add-to/remove-from bookbag accessible for keyboard navigation - CK */ ?> - <a class="cart-add hidden<?php if (!$cart->contains($cartId)): ?> correct<?php endif ?>" href="javascript:" tabindex="0"> - <i class="cart-link-icon fa fa-plus" aria-hidden="true"></i><span class="cart-link-label"><?=$this->transEsc('Add to Book Bag')?></span> + <?php /* finc makes add-to/remove-from bookbag accessible for keyboard navigation - CK */ ?> + <a href="javascript:" class="cart-add hidden<?php if (!$cart->contains($cartId)): ?> correct<?php endif ?>"> + <span class="cart-link-label btn-type-add"><?=$this->transEsc('Add to Book Bag')?></span> </a> - <a class="cart-remove hidden<?php if ($cart->contains($cartId)): ?> correct<?php endif ?>" href="javascript:" tabindex="0"> - <i class="cart-link-icon fa fa-minus-circle" aria-hidden="true"></i> <span class="cart-link-label"><?=$this->transEsc('Remove from Book Bag')?></span> + <a href="javascript:" class="cart-remove hidden<?php if ($cart->contains($cartId)): ?> correct<?php endif ?>"> + <span class="cart-link-label btn-type-minus"><?=$this->transEsc('Remove from Book Bag')?></span> </a> - <form class="cartProcessorNoJs" method="post" name="addForm" action="<?=$this->url('cart-processor')?>"> - <noscript> - <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($cartId)?>"/> + + <?php /* finc adds form and JS function */ + /* removes noscript braces for better W3C Validation */ ?> + <form method="post" name="addForm" action="<?=$this->url('cart-processor')?>" class="cartProcessorNoJs"> + <input type="hidden" name="ids[]" value="<?=$this->escapeHtmlAttr($cartId)?>" /> <?php if ($cart->contains($cartId)): ?> <input class="btn btn-default" type="submit" name="delete" value="<?=$this->transEsc('Remove from Book Bag')?>"/> <?php else: ?> <input class="btn btn-default" type="submit" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"/> <?php endif; ?> - </noscript> - </form> - <script>$(document).ready(function() { $(".cartProcessorNoJs").css('display', 'none'); });</script> + </form> + <?php /* finc adds JS function for better W3C Validation */ ?> + <script>$(document).ready(function() { $(".cartProcessorNoJs").css('display', 'none'); });</script> </div> <?php endif; ?> <!-- finc: record - cart-buttons END --> diff --git a/themes/finc/templates/record/checkbox.phtml b/themes/finc/templates/record/checkbox.phtml index 408f48b1a76de9533c3568044d94c2582ead29b9..0c76b545487d9e07bd111885f96629df72ba20e6 100644 --- a/themes/finc/templates/record/checkbox.phtml +++ b/themes/finc/templates/record/checkbox.phtml @@ -1,6 +1,8 @@ <!-- finc: record - checkbox --> +<?php /* finc adds php for more specific labels, #18754 */ ?> <?php $label = isset($this->context) ? 'select_item_' . $this->context : 'select_item'; ?> <label class="record-checkbox hidden-print"> + <?php /* finc adds aria-label and -describedby */ ?> <input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->id?>"<?php if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<?php endif; ?> aria-describedby="<?=$this->id?>" aria-label="<?=$this->transEsc($label)?>"/> <span class="checkbox-icon"></span> <?php if (strlen($this->number ?? '') > 0): ?><span class="sr-only"><?=$this->transEsc('result_checkbox_label', ['%%number%%' => $this->number]) ?></span><?php endif; ?> diff --git a/themes/finc/templates/record/cover.phtml b/themes/finc/templates/record/cover.phtml index c52afc99b43bb49d501289e6ce5c8a2b4ff6983c..63b6241951f925b51482ac0eee112e7bade88bd4 100644 --- a/themes/finc/templates/record/cover.phtml +++ b/themes/finc/templates/record/cover.phtml @@ -1,7 +1,8 @@ <!-- finc: record - cover --> <?php /* Display thumbnail if appropriate: */ ?> -<?php /* If you want to load covers in lightbox use .recordcover; - the class .nocover prevents nocover images from loading in lightbox or can be used to hide the images */ ?> +<?php /* finc: this one is very special, see revisions for context */ + /*If you want to load covers in lightbox use '.recordcover'; + the class '.nocover' prevents nocover images from loading in lightbox or can be used to hide the images */ ?> <?php $alt = $alt ?? $this->transEsc('Cover Image')?> <?php $id = $driver->getUniqueID() ?> <?php if ($id) { diff --git a/themes/finc/templates/record/prev-next.phtml b/themes/finc/templates/record/prev-next.phtml index 229736c361b9671b1c63cfd4ee8605a57957592e..97214d0beab2a9f1943ccd8e6633a89f3093fa8a 100644 --- a/themes/finc/templates/record/prev-next.phtml +++ b/themes/finc/templates/record/prev-next.phtml @@ -1,8 +1,11 @@ <!-- finc: record - prev-next --> +<?php /* finc uses 'pagination_label' here */ ?> <nav aria-label="<?=$this->transEsc('pagination_label')?>"> <ul class="pager hidden-print"> <?php if ($this->scrollData['previousRecord']): ?> <?php if ($this->scrollData['firstRecord']): ?> + <?php /* finc removes role from li-elements */ ?> + <?php /* finc sets classname left or right for li-elements */ ?> <li class="left"> <a href="<?=$this->recordLink()->getUrl($this->scrollData['firstRecord'])?>" aria-label="<?=$this->transEsc('First Search Result')?>" rel="nofollow"> <i class="fa fa-angle-double-left" aria-hidden="true"></i> diff --git a/themes/finc/templates/record/save.phtml b/themes/finc/templates/record/save.phtml index 54b754eecc8b47310ae66ca0ae27c39253cddbbb..cab84abbf1a963fa8a680e6a1f3f0c600d167f53 100644 --- a/themes/finc/templates/record/save.phtml +++ b/themes/finc/templates/record/save.phtml @@ -8,7 +8,7 @@ . '<li>' . $this->recordLink()->getBreadcrumb($this->driver) . '</li> ' . '<li class="active">' . $this->transEsc('Save') . '</li>'; ?> - +<?php /* finc uses h1 here */ ?> <h1><?=$this->translate("add_to_favorites_html", ['%%title%%' => $this->escapeHtml($this->driver->getBreadcrumb())]) ?></h1> <form class="form-record-save" method="post" action="<?=$this->recordLink()->getActionUrl($this->driver, 'Save')?>" name="saveRecord" data-lightbox-onclose="checkSaveStatusesCallback"> <input type="hidden" name="submit" value="1" /> diff --git a/themes/finc/templates/record/view.phtml b/themes/finc/templates/record/view.phtml index eb4c40ee8c3a146db8fa915a01d5b1de2db4d17a..7758fa75dae164aa17c0c4014fcbf75b60095f82 100644 --- a/themes/finc/templates/record/view.phtml +++ b/themes/finc/templates/record/view.phtml @@ -1,22 +1,22 @@ <!-- finc: record - view --> <?php - // Set up standard record scripts: - $this->headScript()->appendFile("record.js"); - $this->headScript()->appendFile("check_save_statuses.js"); - // Activate Syndetics Plus if necessary: - if ($this->syndeticsPlus()->isActive()) { - $this->headScript()->appendFile($this->syndeticsPlus()->getScript()); - } + // Set up standard record scripts: + $this->headScript()->appendFile("record.js"); + $this->headScript()->appendFile("check_save_statuses.js"); + // Activate Syndetics Plus if necessary: + if ($this->syndeticsPlus()->isActive()) { + $this->headScript()->appendFile($this->syndeticsPlus()->getScript()); + } - // Add RDF header link if applicable: - if ($this->export()->recordSupportsFormat($this->driver, 'RDF')) { - $this->headLink()->appendAlternate($this->recordLink()->getActionUrl($this->driver, 'RDF'), 'application/rdf+xml', 'RDF Representation'); - } + // Add RDF header link if applicable: + if ($this->export()->recordSupportsFormat($this->driver, 'RDF')) { + $this->headLink()->appendAlternate($this->recordLink()->getActionUrl($this->driver, 'RDF'), 'application/rdf+xml', 'RDF Representation'); + } - // Set up breadcrumbs: - $this->layout()->breadcrumbs = '<li>' . $this->searchMemory()->getLastSearchLink($this->transEsc('Search'), '', '</li> ') . + // Set up breadcrumbs: + $this->layout()->breadcrumbs = '<li>' . $this->searchMemory()->getLastSearchLink($this->transEsc('Search'), '', '</li> ') . '<li class="active" aria-current="page">' . $this->recordLink()->getBreadcrumb($this->driver) . '</li> '; - $this->layout()->title = $this->driver->getShortTitle(); + $this->layout()->title = $this->driver->getShortTitle(); ?> <?php if (isset($this->scrollData) && ($this->scrollData['previousRecord'] || $this->scrollData['nextRecord'])): ?> @@ -24,26 +24,25 @@ <?php endif; ?> <?php - /* finc-specific: Remove getToolbar from here and add it to sidebar below - CK - <?=$this->record($this->driver)->getToolbar()?> */ + /* finc removes 'getToolbar' from here and adds it to sidebar below - CK + <?=$this->record($this->driver)->getToolbar()?> + */ ?> <div class="record source<?= $this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>"> - <?php /* finc: remove related-sidebar count and "solo" class since we use a custom sidebar, - keep print classes, CK */ ?> - <?php $sidebarList = $this->related()->getList($this->driver); ?> - <?php /* finc: remove the count of related-items-sidebars since we have a custom sidebar ! CK */ ?> + <?php $sidebarList = $this->related()->getList($this->driver); ?> + <?php /* finc - don't use '<?=count($sidebarList) < 1 ? ' solo' : '' ?>' or toolbar won't fit; BS count sidebars but our toolbar isn't counted */ ?> <div class="<?=$this->layoutClass('mainbody')?>"> - <input type="hidden" value="<?= $this->escapeHtmlAttr($this->driver->getUniqueId()) ?>" class="hiddenId"/> - <input type="hidden" value="<?= $this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource"/> - <?= $this->flashmessages() ?> - <?= $this->record($this->driver)->getCoreMetadata() ?> - - <?php if (count($this->tabs) > 0): ?> - <?php /* swap deprecated 'name' for 'ID' - CK */ ?> - <a id="tabnav"></a> - <div class="record-tabs"> - <?php /* DO NOT add 'role=tablist' for accessibility, see #19938 - CK */ ?> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getUniqueId())?>" class="hiddenId" /> + <input type="hidden" value="<?=$this->escapeHtmlAttr($this->driver->getSourceIdentifier()) ?>" class="hiddenSource" /> + <?=$this->flashmessages()?> + <?=$this->record($this->driver)->getCoreMetadata()?> + + <?php if (count($this->tabs) > 0): ?> + <?php /* finc swaps deprecated 'name' for 'ID' - CK */ ?> + <a id="tabnav"></a> + <div class="record-tabs"> + <?php /* finc:DO NOT add 'role=tablist' for accessibility, see #19938 - CK */ ?> <ul class="nav nav-tabs"> <?php foreach ($this->tabs as $tab => $obj): ?> <?php // add current tab to breadcrumbs if applicable: @@ -56,56 +55,52 @@ } $desc = $obj->getDescription(); $tabName = preg_replace("/\W/", "-", strtolower($tab)); - $tabClasses = ['record-tab', $tabName]; - if (($isActiveTab = 0 === strcasecmp($this->activeTab, $tab))) { - if (!$this->loadInitialTabWithAjax || !$obj->supportsAjax()) { - $tabClasses[] = 'active'; - } - $tabClasses[] = 'initiallyActive'; - $this->layout()->breadcrumbs .= '<li class="active">' . $this->transEsc($desc) . '</li>'; - $activeTabObj = $obj; - } - if (!$obj->isVisible()) { - $tabClasses[] = 'hidden'; - } - if (!$obj->supportsAjax()) { - $tabClasses[] = 'noajax'; - } - ?> - <?php /* DO NOT add role="tab" BUT DO ADD aria-controls and ID for accessibility -- - 'aria-selected' (true/false) needs to be set via record.js - CK */ ?> - <li class="<?= implode(' ', $tabClasses) ?>" data-tab="<?= $tabName ?>"> - <a href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav" - <?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)): ?> data-background<?php endif ?> - aria-selected="<?= $isActiveTab ? "true" : "false" ?>" - <?php if($isActiveTab): ?>aria-controls="<?= $tabName ?>"<?php endif; ?> - id="<?= $tabName ?>-tabselector"> - <?= $this->transEsc($desc) ?> - <span class="sr-only load-tab-content"><?= $this->transEsc('load_tab_content_hint') ?></span></a> - </li> - <?php endforeach; ?> - </ul> - - <div class="tab-content" aria-live="polite" tabindex="-1"> - <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> - <?php /* Add ID, role and aria-labelledby for accessibility - CK */ ?> - <div class="tab-pane active <?= $this->escapeHtmlAttr($this->activeTab) ?>-tab" - role="tabpanel" - id="<?= $this->escapeHtmlAttr($this->activeTab) ?>" - aria-labelledby="<?= $this->activeTab ?>-tabselector"> - <?= isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> - </div> - <?php endif; ?> - </div> + $tabClasses = ['record-tab', $tabName];// finc: add $isActiveTab = 0 to if; #19938 + if (($isActiveTab = 0 === strcasecmp($this->activeTab, $tab))) { + if (!$this->loadInitialTabWithAjax || !$obj->supportsAjax()) { + $tabClasses[] = 'active'; + } + $tabClasses[] = 'initiallyActive'; + $this->layout()->breadcrumbs .= '<li class="active">' . $this->transEsc($desc) . '</li>'; + $activeTabObj = $obj; + } + if (!$obj->isVisible()) { $tabClasses[] = 'hidden'; } + if (!$obj->supportsAjax()) { $tabClasses[] = 'noajax'; } + ?> + <?php /* finc: DO NOT add role='tab' BUT DO ADD aria-controls and ID for accessibility -- + 'aria-selected' (true/false) needs to be set via record.js - CK */ ?> + <li class="<?=implode(' ', $tabClasses) ?>" data-tab="<?=$tabName ?>"> + <a href="<?= $this->recordLink()->getTabUrl($this->driver, $tab) ?>#tabnav" + <?php if ($obj->supportsAjax() && in_array($tab, $this->backgroundTabs)):?> data-background<?php endif ?> + aria-selected="<?= $isActiveTab ? "true" : "false" ?>" + <?php if ($isActiveTab): ?>aria-controls="<?= $tabName ?>"<?php endif; ?> + id="<?= $tabName ?>-tabselector"> + <?=$this->transEsc($desc) ?> + <?php /* finc: add span #19938 */ ?> + <span class="sr-only load-tab-content"><?=$this->transEsc('load_tab_content_hint') ?></span></a> + </li> + <?php endforeach; ?> + </ul> + <?php /* finc adds aria and accessibility code */ ?> + <div class="tab-content" aria-live="polite" tabindex="-1"> + <?php if (!$this->loadInitialTabWithAjax || !isset($activeTabObj) || !$activeTabObj->supportsAjax()): ?> + <?php /* finc adds ID, role and aria-labelledby for accessibility - CK */ ?> + <div class="tab-pane active <?= $this->escapeHtmlAttr($this->activeTab) ?>-tab" + role="tabpanel" + id="<?= $this->escapeHtmlAttr($this->activeTab) ?>" + aria-labelledby="<?= $this->activeTab ?>-tabselector"> + <?=isset($activeTabObj) ? $this->record($this->driver)->getTab($activeTabObj) : '' ?> + </div> + <?php endif; ?> </div> - <?php endif; ?> - - <?= $this->driver->supportsCoinsOpenURL() ? '<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>' : '' ?> - </div> + </div> + <?php endif; ?> + <?=$this->driver->supportsCoinsOpenURL()?'<span class="Z3988" title="' . $this->escapeHtmlAttr($this->driver->getCoinsOpenURL()) . '"></span>':''?> + </div> <div class="<?= $this->layoutClass('sidebar') ?>" id="myresearch-sidebar"> - <?php /* finc-specific: add toolbar to sidebar - CK */ ?> + <?php /* finc adds toolbar to sidebar - CK */ ?> <?= $this->record($this->driver)->getToolbar() ?> <?php foreach ($sidebarList as $current): ?> diff --git a/themes/finc/templates/search/advanced/layout.phtml b/themes/finc/templates/search/advanced/layout.phtml index 350eabbcb32299defd5719084c250809c70a3ffc..ee3e898c0a92c4189bbf767bf1832c833b927288 100644 --- a/themes/finc/templates/search/advanced/layout.phtml +++ b/themes/finc/templates/search/advanced/layout.phtml @@ -1,65 +1,65 @@ <!-- finc: search - advanced - layout --> <?php -// Set page title. -$this->headTitle($this->translate('Advanced Search')); + // Set page title. + $this->headTitle($this->translate('Advanced Search')); -// Disable top search box -- this page has a special layout. -$this->layout()->searchbox = false; + // Disable top search box -- this page has a special layout. + $this->layout()->searchbox = false; -// Set up breadcrumbs: -$this->layout()->breadcrumbs = '<li>'; -$lastSearchLink = $this->searchMemory()->getLastSearchLink($this->transEsc('Search')); -$this->layout()->breadcrumbs .= !empty($lastSearchLink) - ? $lastSearchLink : $this->transEsc('Search'); -$this->layout()->breadcrumbs .= '</li> <li class="active">' . $this->transEsc('Advanced') . '</li>'; + // Set up breadcrumbs: + $this->layout()->breadcrumbs = '<li>'; + $lastSearchLink = $this->searchMemory()->getLastSearchLink($this->transEsc('Search')); + $this->layout()->breadcrumbs .= !empty($lastSearchLink) + ? $lastSearchLink : $this->transEsc('Search'); + $this->layout()->breadcrumbs .= '</li> <li class="active">' . $this->transEsc('Advanced') . '</li>'; -// Set up saved search details: -if (isset($this->saved) && is_object($this->saved)) { - $searchDetails = $this->saved->getParams()->getQuery(); - if ($searchDetails instanceof \VuFindSearch\Query\Query) { - // Not an advanced query -- ignore it. - $searchDetails = $groups = false; + // Set up saved search details: + if (isset($this->saved) && is_object($this->saved)) { + $searchDetails = $this->saved->getParams()->getQuery(); + if ($searchDetails instanceof \VuFindSearch\Query\Query) { + // Not an advanced query -- ignore it. + $searchDetails = $groups = false; + } else { + $groups = $searchDetails->getQueries(); + } + $hasDefaultsApplied = $this->saved->getParams()->hasDefaultsApplied(); + $searchFilters = $this->saved->getParams()->getFilterList(); + $hiddenFilters = $this->saved->getParams()->getHiddenFilters(); } else { - $groups = $searchDetails->getQueries(); + $hasDefaultsApplied = $searchDetails = $searchFilters = $groups = false; + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, true); } - $hasDefaultsApplied = $this->saved->getParams()->hasDefaultsApplied(); - $searchFilters = $this->saved->getParams()->getFilterList(); - $hiddenFilters = $this->saved->getParams()->getHiddenFilters(); -} else { - $hasDefaultsApplied = $searchDetails = $searchFilters = $groups = false; - $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, true); -} -// Step 1: Load the javascript -$this->headScript()->appendFile( - isset($this->advancedSearchJsOverride) ? $this->advancedSearchJsOverride : 'advanced_search.js' -); -// Step 2: Build the page -$this->headScript()->appendScript( - $this->partial( - isset($this->buildPageOverride) ? $this->buildPageOverride : 'search/advanced/build_page.phtml', - ['options' => $this->options, 'searchDetails' => $searchDetails] - ) -); + // Step 1: Load the javascript + $this->headScript()->appendFile( + isset($this->advancedSearchJsOverride) ? $this->advancedSearchJsOverride : 'advanced_search.js' + ); + // Step 2: Build the page + $this->headScript()->appendScript( + $this->partial( + isset($this->buildPageOverride) ? $this->buildPageOverride : 'search/advanced/build_page.phtml', + ['options' => $this->options, 'searchDetails' => $searchDetails] + ) + ); -// Collect previous search queries -$setSearchGroups = []; -$setGroupCount = 0; -$setQueries = []; -if (isset($searchDetails) && is_object($searchDetails)) { - foreach ($searchDetails->getQueries() as $group=>$searchGroup) { - $setSearchGroups[$group] = $searchGroup->isNegated() ? 'NOT' : $searchGroup->getOperator(); - if ($setGroupCount < $group) { - $setGroupCount = $group; - } - if (!isset($setQueries[$group])) { - $setQueries[$group] = []; - } - foreach ($searchGroup->getQueries() as $search) { - $setQueries[$group][] = $search; + // Collect previous search queries + $setSearchGroups = []; + $setGroupCount = 0; + $setQueries = []; + if (isset($searchDetails) && is_object($searchDetails)) { + foreach ($searchDetails->getQueries() as $group => $searchGroup) { + $setSearchGroups[$group] = $searchGroup->isNegated() ? 'NOT' : $searchGroup->getOperator(); + if ($setGroupCount < $group) { + $setGroupCount = $group; + } + if (!isset($setQueries[$group])) { + $setQueries[$group] = []; + } + foreach ($searchGroup->getQueries() as $search) { + $setQueries[$group][] = $search; + } } } -} ?> <?=$this->flashmessages()?> @@ -76,71 +76,73 @@ if (isset($searchDetails) && is_object($searchDetails)) { <input type="hidden" name="sort" value="<?=$this->escapeHtmlAttr($lastSort)?>" /> <?php endif; ?> <div class="clearfix"> - <?php ?> + <?php /* finc: offcanvas behaviour for xs #17601 */ ?> <span class="offcanvas-toggler"> <a class="search-filter-toggle btn btn-primary visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('offcanvas-toggler-search-tips')?> </a> </span> + <?php /* finc uses h1 */ ?> <h1 class="pull-left flip"><?=$this->transEsc('Advanced Search')?></h1> <div id="groupJoin" class="form-inline pull-right flip"> <label for="groupJoinOptions"><?=$this->transEsc("search_match")?>:</label> <select id="groupJoinOptions" name="join" class="form-control"> - <option value="AND"<?php if($searchDetails && $searchDetails->getOperator()=='ALL'):?> selected<?php endif;?>><?= $this->transEsc('group_AND') ?></option> - <option value="OR"<?php if($searchDetails && $searchDetails->getOperator()=='OR'):?> selected<?php endif;?>><?= $this->transEsc('group_OR') ?></option> + <option value="AND"<?php if($searchDetails && $searchDetails->getOperator() == 'ALL'):?> selected<?php endif; ?>><?= $this->transEsc('group_AND') ?></option> + <option value="OR"<?php if($searchDetails && $searchDetails->getOperator() == 'OR'):?> selected<?php endif; ?>><?= $this->transEsc('group_OR') ?></option> </select> </div> </div> <?php /* finc: keep icon inside link for consistent functionality */ ?> <span id="groupPlaceHolder" class="hidden"> - <a href="javascript:void(0);" onClick="addGroup();return false" class="btn btn-default" role="button"> - <i class="fa fa-plus" aria-hidden="true"> </i> <?=$this->transEsc('add_search_group')?> - </a> + <a href="javascript:void(0);" onClick="addGroup();return false" class="btn btn-default" role="button"> + <i class="fa fa-plus" aria-hidden="true"></i><?= $this->transEsc('add_search_group') ?> + </a> </span> <?php /* fallback to a fixed set of search groups/fields if JavaScript is turned off */ ?> <div class="no-js"> <?php if(!empty($this->formOverride)): ?> <?=$this->formOverride ?> <?php else: ?> - <?php for($group=0 ; $group<3 || $group<=$setGroupCount ; $group++): ?> + <?php for($group = 0; $group < 3 || $group <= $setGroupCount; $group++): ?> <?php if($group == 0): ?> <div id="new_group_template"> <?php endif; ?> - <div id="group<?=$group?>" class="adv-group" role="group" aria-label="<?=$this->transEsc('adv_search_searchgroup')?> <?=$group?>"> + <?php /* finc add roles, aria-label #19418 */ ?> + <div id="group<?=$group ?>" class="adv-group" role="group" aria-label="<?=$this->transEsc('adv_search_searchgroup')?> <?=$group?>"> <div class="adv-group-terms"> <?php /* remove label in favor of more detailed labelling */ /* <label class="adv-group-label" for="search_lookfor<?=$group ?>_0"><?=$this->transEsc("adv_search_label")?>:</label> */ ?> - <?php for($search=0 ; $search<3 || (isset($setQueries[$group]) && $search<count($setQueries[$group])) ; $search++): ?> + <?php for($search = 0; $search < 3 || (isset($setQueries[$group]) && $search < count($setQueries[$group])); $search++): ?> <?php if($group == 0 && $search == 0): ?> <div id="new_search_template"> - <?php else: ?> + <?php else: ?> <?php endif; ?> - <div id="search<?=$group.'_'.$search ?>" class="adv-search"> - <fieldset> - <legend class="sr-only"> - <?=$this->transEsc("adv_search_searchfield")?> <?=$search + 1?> - </legend> - <div class="adv-input"> - <label for="search_lookfor<?=$group . '_' . $search?>"> - <?=$this->transEsc("search_terms")?>: - </label> - <input name="lookfor<?=$group ?>[]" id="search_lookfor<?=$group . '_' . $search ?>" class="adv-term-input form-control" type="text"<?php if (isset($setQueries[$group][$search])): ?> value="<?=$this->escapeHtml($setQueries[$group][$search]->getString())?>"<?php endif; ?>> - </div> - - <div class="adv-select"> - <label for="type<?=$group . '_' . $search?>"><?=$this->transEsc("Search type")?>:</label> - <select id="type<?=$group . '_' . $search?>" class="adv-term-type form-control" name="type<?=$group?>[]"> - <?php foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> - <option value="<?=$this->escapeHtml($searchVal)?>"<?php if(isset($setQueries[$group][$search]) && $searchVal == $setQueries[$group][$search]->getHandler()):?> selected<?php endif;?>><?=$this->transEsc($searchDesc)?></option> - <?php endforeach; ?> - </select> - </div> - + <div id="search<?=$group . '_' . $search ?>" class="adv-search"> + <?php /* finc uses fieldset and label for more consistent formatting - check again in VF 7 or 8.x */ ?> + <fieldset> + <legend class="sr-only"> + <?=$this->transEsc("adv_search_searchfield")?> <?=$search + 1?> + </legend> + <div class="adv-input"> + <label for="search_lookfor<?=$group . '_' . $search?>"> + <?=$this->transEsc("search_terms")?>: + </label> + <input name="lookfor<?=$group ?>[]" id="search_lookfor<?=$group . '_' . $search ?>" class="adv-term-input form-control" type="text"<?php if (isset($setQueries[$group][$search])): ?> value="<?=$this->escapeHtml($setQueries[$group][$search]->getString())?>"<?php endif; ?>> + </div> + <div class="adv-select"> + <label for="type<?=$group . '_' . $search?>"><?=$this->transEsc("Search type")?>:</label> + <?php /* finc: add id #19418 */ ?> + <select id="type<?=$group . '_' . $search?>" class="adv-term-type form-control" name="type<?=$group ?>[]"> + <?php foreach ($this->options->getAdvancedHandlers() as $searchVal => $searchDesc): ?> + <option value="<?=$this->escapeHtml($searchVal)?>"<?php if(isset($setQueries[$group][$search]) && $searchVal == $setQueries[$group][$search]->getHandler()):?> selected<?php endif;?>><?=$this->transEsc($searchDesc)?></option> + <?php endforeach; ?> + </select> + </div> <a href="javascript:void(0);" class="adv-term-remove hidden"> + <?php /* finc: add span for screenreader */ ?> <span class="sr-only"><?=$this->transEsc("remove")?></span> <i class="fa fa-times" aria-hidden="true"></i> </a> - </fieldset> - + </fieldset> </div> <?php if($group == 0 && $search == 0): ?> </div> @@ -148,23 +150,23 @@ if (isset($searchDetails) && is_object($searchDetails)) { (As this will also 'shift search_place_holder') Therefore Keep 'search_place_holder' separate and move icon only ! */ ?> <span class="float-left"> + <?php /* finc: remove classname fa fa-plus-circle #11813 */ ?> <span class="search_place_holder hidden" aria-hidden="true"></span> - <a href="javascript:void(0);" class="btn btn-default add_search_link hidden" role="button"> - <i class="fa fa-plus" aria-hidden="true"></i> <?=$this->transEsc("add_search")?></a> + <a href="javascript:void(0);" class="btn btn-default add_search_link hidden" role="button"><i class="fa fa-plus" aria-hidden="true"></i> <?=$this->transEsc("add_search")?></a> </span> <?php endif; ?> <?php endfor; ?> </div> <div class="adv-group-match"> + <?php /* finc: add missing labels #18209 */ ?> <label class="search_bool" for="search_bool<?=$group ?>"><?=$this->transEsc("search_match")?>: </label> - <select id="search_bool<?=$group?>" name="bool<?=$group?>[]" class="form-control"> - <option value="AND"<?php if(isset($setSearchGroups[$group]) && 'AND' == $setSearchGroups[$group]):?> selected<?php endif;?>><?=$this->transEsc("search_AND")?></option> - <option value="OR"<?php if(isset($setSearchGroups[$group]) && 'OR' == $setSearchGroups[$group]):?> selected<?php endif;?>><?=$this->transEsc("search_OR")?></option> - <option value="NOT"<?php if(isset($setSearchGroups[$group]) && 'NOT' == $setSearchGroups[$group]):?> selected<?php endif;?>><?=$this->transEsc("search_NOT")?></option> + <select name="bool<?=$group ?>[]" id="search_bool<?=$group ?>" class="form-control"> + <option value="AND"<?php if(isset($setSearchGroups[$group]) && 'AND' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_AND")?></option> + <option value="OR"<?php if(isset($setSearchGroups[$group]) && 'OR' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_OR")?></option> + <option value="NOT"<?php if(isset($setSearchGroups[$group]) && 'NOT' == $setSearchGroups[$group]):?> selected<?php endif; ?>><?=$this->transEsc("search_NOT")?></option> </select> </div> - - <a href="javascript:void(0);" class="btn btn-default adv-group-close hidden" role="button"><i class="fa fa-close"></i> <?=$this->transEsc("del_search")?></a> + <a href="javascript:void(0);" class="btn btn-default adv-group-close hidden" role="button"><i class="fa fa-close"></i> <?=$this->transEsc("del_search")?></a> </div> <?php if($group == 0): ?> </div> @@ -188,7 +190,9 @@ if (isset($searchDetails) && is_object($searchDetails)) { <?php endif; ?> </div> + <?php /* finc: add id myresearch-sidebar #17908 */ ?> <div class="<?=$this->layoutClass('sidebar')?>" id="myresearch-sidebar"> + <?php /* finc: add button #17601 */ ?> <button class="close-offcanvas btn btn-primary" data-toggle="offcanvas"><?=$this->transEsc('navigate_back') ?></button> <?php if ($hasDefaultsApplied): ?> <input type="hidden" name="dfApplied" value="1" /> diff --git a/themes/finc/templates/search/advanced/limit.phtml b/themes/finc/templates/search/advanced/limit.phtml index c8f5ad0eaeb8ae41e3a70277c204546bbe579747..9c0b0ed0bed5285cd8569bfd241777d81679c0a5 100644 --- a/themes/finc/templates/search/advanced/limit.phtml +++ b/themes/finc/templates/search/advanced/limit.phtml @@ -6,11 +6,13 @@ // If a previous limit was used, make that the default; otherwise, use the "default default" $lastLimit = $this->searchMemory()->getLastLimit($this->options->getSearchClassId()); $defaultLimit = empty($lastLimit) ? $this->options->getDefaultLimit() : $lastLimit; + // finc specific: $this->headScript()->appendScript("var defaultLimit = $defaultLimit"); ?> <?php if (count($limitList) > 1): ?> <fieldset class="limits"> <legend><?=$this->transEsc('Results per page')?></legend> + <?php /* finc adds label */ ?> <label for="limit"><?=$this->transEsc('Limit To')?></label> <select id="limit" name="limit" class="form-control"> <?php foreach ($limitList as $limitVal): ?> diff --git a/themes/finc/templates/search/advanced/ranges.phtml b/themes/finc/templates/search/advanced/ranges.phtml index aa7cfbac64068a75b1d7a5d474fd452f4456d3a4..d6ec0dbd519608ed33e171a4e22e9caf61f8cbc2 100644 --- a/themes/finc/templates/search/advanced/ranges.phtml +++ b/themes/finc/templates/search/advanced/ranges.phtml @@ -1,15 +1,15 @@ <!-- finc: search - advanced - ranges --> <?php if (isset($this->ranges) && !empty($this->ranges)): ?> - <?php $params = $this->searchParams($this->searchClassId); $params->activateAllFacets(); ?> + <?php $params = $this->searchParams($this->searchClassId); ?> <?php foreach ($this->ranges as $current): $escField = $this->escapeHtmlAttr($current['field']); ?> <?php /* finc adds 'max="'.(date('Y')+1).' to prevent dates beyond the year + 1 to be entered; maxlength was causing w3c issues */ ?> <?php $extraInputAttribs = ($current['type'] == 'date') ? 'max="'.(date('Y')+1).'" ' : ''; ?> - <?php /* #18306: alignment left to result - GG */ ?> - <fieldset class="range left"> + <fieldset class="range"> <legend><?=$this->transEsc($params->getFacetLabel($current['field']))?></legend> <input type="hidden" name="<?=$this->escapeHtmlAttr($current['type'])?>range[]" value="<?=$escField?>"/> <div class="date-fields"> <div class="date-from"> + <?php /* finc adds id for labels */ ?> <label id="from-label" for="<?=$escField?>from"><?=$this->transEsc('date_from')?>:</label> <input type="number" name="<?=$escField?>from" id="<?=$escField?>from" value="<?=isset($current['values'][0])?$this->escapeHtmlAttr($current['values'][0]):''?>" class="form-control" <?=$extraInputAttribs?>/> </div> @@ -20,6 +20,7 @@ </div> <?php if ($current['type'] == 'date'): ?> <div class="slider-container"> + <?php /* finc adds label */ ?> <label for="<?=$escField?><?=$this->escapeHtmlAttr($current['type'])?>Slider"><?=$this->transEsc('Range-from-to')?>:</label> <input type="text" id="<?=$escField?><?=$this->escapeHtmlAttr($current['type'])?>Slider"> </div> @@ -68,6 +69,7 @@ $('#{$escField}from, #{$escField}to').change(function () { true ); }); +// finc adds label reference $(document).ready(function() { $(".slider-handle.min-slider-handle").attr("aria-labelledby", "from-label"); $(".slider-handle.max-slider-handle").attr("aria-labelledby", "to-label"); diff --git a/themes/finc/templates/search/advanced/solr.phtml b/themes/finc/templates/search/advanced/solr.phtml index a25a6a5a85a218f86d16aa5f2a06ae3451d48b41..46b72069fd4105dfcb2e9b7b2169c7077ae51ecf 100644 --- a/themes/finc/templates/search/advanced/solr.phtml +++ b/themes/finc/templates/search/advanced/solr.phtml @@ -16,8 +16,7 @@ <?php if (is_array($this->hierarchicalFacets) && in_array($field, $this->hierarchicalFacets)): ?> <?php foreach ($list['list'] as $value): ?> <?php $display = str_pad('', 4 * $value['level'] * 6, ' ', STR_PAD_LEFT) . $this->escapeHtml($value['displayText']); ?> - <option - value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected']) ? ' selected="selected"' : ''?>><?=$display?></option> + <option value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected'])?' selected="selected"':''?>><?=$display?></option> <?php endforeach; ?> <?php else: ?> <?php @@ -34,8 +33,7 @@ ?> <?php foreach ($sorted as $i => $display): ?> <?php $value = $list['list'][$i]; ?> - <option - value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected']) ? ' selected="selected"' : ''?>><?=$this->escapeHtml($display)?></option> + <option value="<?=$this->escapeHtmlAttr(($value['operator'] == 'OR' ? '~' : '') . $field . ':"' . $value['value'] . '"')?>"<?=(isset($value['selected']) && $value['selected'])?' selected="selected"':''?>><?=$this->escapeHtml($display)?></option> <?php endforeach; ?> <?php endif; ?> </select> @@ -43,15 +41,12 @@ <?php endforeach; ?> </div> </fieldset> - <?php endif; ?> - <?php if (isset($this->illustratedLimit)): ?> <fieldset class="solr"> <legend><?=$this->transEsc("Illustrated")?>:</legend> <?php foreach ($this->illustratedLimit as $current): ?> - <input id="illustrated_<?=$this->escapeHtmlAttr($current['value'])?>" type="radio" name="illustration" - value="<?=$this->escapeHtmlAttr($current['value'])?>"<?=$current['selected'] ? ' checked="checked"' : ''?>/> + <input id="illustrated_<?=$this->escapeHtmlAttr($current['value'])?>" type="radio" name="illustration" value="<?=$this->escapeHtmlAttr($current['value'])?>"<?=$current['selected']?' checked="checked"':''?>/> <label for="illustrated_<?=$this->escapeHtmlAttr($current['value'])?>"><?=$this->transEsc($current['text'])?></label><br/> <?php endforeach; ?> </fieldset> diff --git a/themes/finc/templates/search/bulk-action-buttons.phtml b/themes/finc/templates/search/bulk-action-buttons.phtml index caa79a1ba818f44c22f100ed276a2323a0b07411..7584246ad3860e954ab4858d8c71c2eacf1a2a22 100644 --- a/themes/finc/templates/search/bulk-action-buttons.phtml +++ b/themes/finc/templates/search/bulk-action-buttons.phtml @@ -1,28 +1,39 @@ <!-- finc: search - bulk-action-buttons --> <?php if (isset($this->showCheckboxes) && $this->showCheckboxes): ?> - <div class="bulkActionButtons hidden-print"> +<nav class="bulkActionButtons hidden-print"> <div class="bulk-checkbox"> - <input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <input type="checkbox" class="checkbox-select-all" name="selectAll" id="<?=$this->idPrefix?>addFormCheckboxSelectAll"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> <label for="<?=$this->idPrefix?>addFormCheckboxSelectAll"> <?=$this->transEsc('select_page')?> | <?=$this->transEsc('with_selected')?>: </label> </div> - <div class="btn-group"> + <ul class="action-toolbar"> <?php if (isset($this->showBulkOptions) && $this->showBulkOptions): ?> - <input id="<?=$this->idPrefix?>ribbon-email" class="btn btn-transparent" type="submit" name="email" title="<?=$this->transEsc('bookbag_email_selected')?>" value="<?=$this->transEsc('Email')?>"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> - <?php $exportOptions = $this->export()->getBulkOptions(); if (count($exportOptions) > 0): ?> - <input id="<?=$this->idPrefix?>ribbon-export" class="btn btn-transparent" type="submit" name="export" title="<?=$this->transEsc('bookbag_export_selected')?>" value="<?=$this->transEsc('Export')?>"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li> + <?php /* keep id="<?=$this->idPrefix?>... on all buttons for proper id assignment for top and bottom toolbar */ ?> + <button id="<?=$this->idPrefix?>ribbon-email" class="toolbar-btn btn-type-email" type="submit" name="email" value="1" title="<?=$this->transEsc('bookbag_email_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Email')?></button> + </li> + <?php $exportOptions = $this->export()->getActiveFormats('bulk'); if (count($exportOptions) > 0): ?> + <li> + <button id="<?=$this->idPrefix?>ribbon-export" class="toolbar-btn btn-type-export" type="submit" name="export" value="1" title="<?=$this->transEsc('bookbag_export_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Export')?></button> + </li> <?php endif; ?> - <input id="<?=$this->idPrefix?>ribbon-print" class="btn btn-transparent" type="submit" name="print" title="<?=$this->transEsc('bookbag_print_selected')?>" value="<?=$this->transEsc('Print')?>"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li> + <button id="<?=$this->idPrefix?>ribbon-print" class="toolbar-btn btn-type-print" type="submit" name="print" value="1" title="<?=$this->transEsc('bookbag_print_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Print')?></button> + </li> <?php if ($this->userlist()->getMode() !== 'disabled'): ?> - <input id="<?=$this->idPrefix?>ribbon-save" class="btn btn-transparent" type="submit" name="saveCart" title="<?=$this->transEsc('bookbag_save_selected')?>" value="<?=$this->transEsc('Save')?>"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li> + <button id="<?=$this->idPrefix?>ribbon-save" class="toolbar-btn btn-type-save" type="submit" name="saveCart" value="1" title="<?=$this->transEsc('bookbag_save_selected')?>"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Save')?></button> + </li> <?php endif; ?> <?php endif; ?> <?php if (isset($this->showCartControls) && $this->showCartControls): ?> - <input id="<?=$this->idPrefix?>updateCart" type="submit" class="btn btn-transparent" name="add" value="<?=$this->transEsc('Add to Book Bag')?>"<?php if($this->formAttr):?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>/> + <li> + <button id="<?=$this->idPrefix?><?=$this->idPrefix?>updateCart" type="submit" class="toolbar-btn btn-type-add" name="add" value="1"<?php if ($this->formAttr): ?> form="<?=$this->escapeHtmlAttr($this->formAttr) ?>"<?php endif; ?>><?=$this->transEsc('Add to Book Bag')?></button> + </li> <?php endif; ?> - </div> - </div> + </ul> + </nav> <?php endif; ?> -<!-- finc: search - bulk-action-buttons - END --> +<!-- finc: search - bulk-action-buttons - END --> \ No newline at end of file diff --git a/themes/finc/templates/search/controls/limit.phtml b/themes/finc/templates/search/controls/limit.phtml index d90750005b5f49a86ae0a44a6965f312c3bffddc..50ffafe7d112d932b26bd4f6530d4bc5ab49a484 100644 --- a/themes/finc/templates/search/controls/limit.phtml +++ b/themes/finc/templates/search/controls/limit.phtml @@ -1,20 +1,21 @@ <!-- finc: search - controls - limit --> -<?php /* Add div #18016 - HR */ ?> <?php $limitList = $this->params->getLimitList(); ?> <?php if (count($limitList) > 1): ?> - <?php /* finc: DO NOT use class 'form-inline' as it messes up the select box */ ?> - <form class="limit" action="<?=$this->currentPath() . $this->results->getUrlQuery()->setLimit(null)?>" method="post"> + <?php /* finc: DO NOT use class 'form-inline' as it messes up the select box */ + /* finc: remove classname search-result-limit */ ?> + <form class="limit-form" action="<?=$this->currentPath() . $this->results->getUrlQuery()->setLimit(null)?>" method="post"> <label for="limit"><?=$this->transEsc('Results per page')?></label> <div class="limit-inner"> <?php /* finc: DO NOT use class 'form-control' as it messes up the select box */ ?> - <select id="limit" name="limit"> - <?php foreach ($limitList as $limitVal => $limitData): ?> - <option value="<?=$this->escapeHtmlAttr($limitVal)?>" <?=$limitData['selected']? ' selected="selected" ':'' ?>><?=$this->escapeHtml($limitData['desc'])?></option> - <?php endforeach; ?> - </select> - <button type="submit" class="btn btn-primary" aria-label="<?=$this->transEsc("Set")?>"> - <i class="fa fa-refresh" aria-hidden="true"></i> - </button> + <select id="limit" name="limit" class="jumpMenu"> + <?php foreach ($limitList as $limitVal => $limitData): ?> + <option value="<?=$this->escapeHtmlAttr($limitVal)?>"<?=$limitData['selected']?' selected="selected"':''?>><?=$this->escapeHtml($limitData['desc'])?></option> + <?php endforeach; ?> + </select> + <?php /* finc: add button for accessibility #18016 */ ?> + <button type="submit" class="btn btn-primary" aria-label="<?=$this->transEsc("Set")?>"> + <i class="fa fa-refresh" aria-hidden="true"></i> + </button> </div> </form> <?php endif; ?> diff --git a/themes/finc/templates/search/controls/sort.phtml b/themes/finc/templates/search/controls/sort.phtml index ba9d20221c30bfdea7103b55cdf4ec63204bc914..086d23c9a732ad10cbb8a24a1192d99a84395746 100644 --- a/themes/finc/templates/search/controls/sort.phtml +++ b/themes/finc/templates/search/controls/sort.phtml @@ -1,5 +1,4 @@ <!-- finc: search - controls - sort --> -<?php /* Add div #18016 - HR */ ?> <?php $list = $this->params->getSortList(); if (!empty($list)): ?> <?php /* finc: we use class 'text-right' */ ?> <form class="search-sort text-right" action="<?=$this->currentPath()?>" method="get" name="sort"> @@ -7,14 +6,15 @@ <label for="sort_options_1"><?=$this->transEsc('Sort')?></label> <div class="sort-inner"> <?php /* finc: DO NOT use class 'form-control' as it messes up the select box */ ?> - <select id="sort_options_1" name="sort"> - <?php foreach ($list as $sortType => $sortData): ?> - <option value="<?=$this->escapeHtmlAttr($sortType)?>" <?=$sortData['selected']?' selected="selected"':''?>><?=$this->transEsc($sortData['desc'])?></option> - <?php endforeach; ?> - </select> - <button type="submit" class="btn btn-primary" aria-label="<?=$this->transEsc("Set")?>"> - <i class="fa fa-refresh" aria-hidden="true"></i> - </button> + <select id="sort_options_1" name="sort" class="jumpMenu"> + <?php foreach ($list as $sortType => $sortData): ?> + <option value="<?=$this->escapeHtmlAttr($sortType)?>"<?=$sortData['selected']?' selected="selected"':''?>><?=$this->transEsc($sortData['desc'])?></option> + <?php endforeach; ?> + </select> + <?php /* finc: add button for accessibility #18016 */ ?> + <button type="submit" class="btn btn-primary" aria-label="<?=$this->transEsc("Set")?>"> + <i class="fa fa-refresh" aria-hidden="true"></i> + </button> </div> </form> <?php endif; ?> diff --git a/themes/finc/templates/search/filters.phtml b/themes/finc/templates/search/filters.phtml new file mode 100644 index 0000000000000000000000000000000000000000..6d365819d79cb616c77265579ce677bee9ea1ad4 --- /dev/null +++ b/themes/finc/templates/search/filters.phtml @@ -0,0 +1,169 @@ +<!-- finc - search - filters --> +<?php + if (!isset($this->params)) { + // No current search, use last search in memory + $params = $this->searchMemory()->getLastSearchParams($this->searchClassId); + } else { + // clone params so that the manipulation doesn't cause trouble e.g. for facets + $params = clone $this->params; + } + + $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); + $options = $this->searchOptions($this->searchClassId); + $hasDefaultsApplied = $params->hasDefaultsApplied(); + $filterCount = $this->searchbox()->getFilterCount($checkboxFilters, $filterList); + + // Determine whether the only filters applied are the default ones; this controls + // when we display or hide the reset button: + $defaultFilters = $options->getDefaultFilters(); + $onlyDefaultsApplied = count($defaultFilters) == $filterCount; + foreach ($defaultFilters as $currentDefault) { + if (!$params->hasFilter($currentDefault)) { + $onlyDefaultsApplied = false; + break; + } + } + + $advancedSearch = $this->searchType === 'advanced'; +?> +<?php ob_start(); ?> + <?php foreach ($checkboxFilters as $filter): ?> + <?php if ($filter['selected']): ?> + <span class="filter-value"> + <?php + $removeLink = isset($urlQuery) + ? $urlQuery->removeFilter($filter['filter']) + : $this->searchMemory()->getEditLink( + $this->searchClassId, 'removeFilter', $filter['filter'] + ); + $desc = $this->translate($filter['desc']); + $ariaLabel = $this->translate('Remove filter') . ' ' . $this->escapeHtmlAttr($desc); + ?> + <?=$desc?> + <?php if($removeLink): ?> + <a class="search-filter-remove" aria-label="<?=$ariaLabel?>" href="<?=$removeLink?>"><!--icon from css --></a> + <?php endif; ?> + </span> + <?php endif ?> + <?php endforeach; ?> + + <?php foreach ($filterList as $field => $data): ?> + <div class="title-value-pair"> + <span class="filters-title"><?=$this->transEsc($field)?>:</span> + <?php /* finc: set number of filters from one group for display in dropdown to '> 1' + so 2 and more filters from one group will be presented in a dropdown rather + than as a long chain of terms - + bootstrap3 has '<?php if (count($data) > 3): ?>' + CK */ ?> + <?php if (count($data) > 1): ?> + <div class="search-filter-dropdown dropdown"> + <?php $dropdown = true; ?> + <?php $safeId = preg_replace('/[^a-zA-Z0-9]/', '', $field); ?> + <button id="dropdown-toggle-<?=$safeId?>" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"> + <?=$this->transEsc('filter_toggle_entries', ['%%count%%' => count($data)])?> + </button> + <ul class="dropdown-menu" role="menu" aria-labelledby="dropdown-toggle-<?=$safeId?>"> + <?php else: ?> + <?php $dropdown = false; ?> + <?php endif; ?> + <?php foreach ($data as $index => $value): ?> + <?php if ($dropdown): ?> + <li> + <?php endif; ?> + <?php + switch ($value['operator']) { + case 'NOT': + $operatorChar = '-'; + $join = $this->transEsc('NOT'); + break; + case 'OR': + $operatorChar = '~'; + $join = $this->transEsc('OR'); + break; + case 'AND': + $operatorChar = ''; + $join = $this->transEsc('AND'); + break; + default: + $operatorChar = $join = ''; + } + $operatorClass = $this->escapeHtmlAttr(strtolower($value['operator'])); + ?> + <span class="filters-term filters-<?=$operatorClass?>"> + <?=($index > 0 || 'NOT' === $value['operator']) ? $join : '' ?> + </span> + <span class="filter-value filters-<?=$operatorClass?>"> + <?php /* finc uses entire text + icon as link for larger interaction area; adds more detailed aria-label */ ?> + <?php + $removeLink = isset($this->urlQuery) + ? $urlQuery->removeFacet($value['field'], $value['value'], $value['operator']) + : $this->searchMemory()->getEditLink($this->searchClassId, 'removeFacet', $value); + ?> + <a class="search-filter-remove" aria-label="<?=$this->transEsc('clear_tag_filter') ?> – <?=$this->transEsc('page_reload_on_deselect_hint', ['%%filter_name%%' => $value['displayText']])?>" + href="<?=$removeLink?>"> + <span class="text"> + <?=$this->escapeHtml($value['displayText'])?> + </span> + </a> + </span> + <?php if ($dropdown): ?> + </li> + <?php endif; ?> + <?php endforeach; ?> + <?php if ($dropdown): ?> + </ul> + </div> + <?php endif; ?> + </div> + <?php endforeach; ?> +<?php $filters = ob_get_contents(); ?> +<?php ob_end_clean(); ?> + +<?php if ($hasDefaultsApplied || $filterCount > 0): ?> + <?php + $resetLink = null; + if (!$onlyDefaultsApplied) { + $resetLink = isset($urlQuery) + ? $urlQuery->removeAllFilters()->resetDefaultFilters() + : $this->searchMemory()->getEditLink($this->searchClassId, 'removeAllFilters', 1); + } + ?> + <?php // Normal view ?> + <div class="active-filters hidden-xs"> + <?php if ($resetLink && $options->getRetainFilterSetting()): ?> + <?php /* finc adds verbose reload warning via aria-label */ ?> + <a class="reset-filters-btn" href="<?=$resetLink?>" + aria-label="<?=$this->transEsc('clear_tag_filter') ?> – <?=$this->transEsc('page_reload_on_deselect_all_hint', ['%%filter_name%%' => $value['displayText']])?>" + > + <?=$this->transEsc('reset_filters_button') ?> + </a> + <?php elseif ($advancedSearch): ?> + <p class="adv_search_filters"><?=$this->transEsc('adv_search_filters')?>:</p> + <?php endif; ?> + <div class="filters"> + <?=$filters ?> + </div> + <div class="clearfix"></div> + </div> + <?php // Narrow view ?> + <div class="active-filters visible-xs"> + <div class="filters-toggle-bar"> + <?php if ($resetLink && $options->getRetainFilterSetting()): ?> + <?php /* finc adds verbose reload warning via aria-label */ ?> + <a class="reset-filters-btn active" href="<?=$resetLink?>" + aria-label="<?=$this->transEsc('clear_tag_filter') ?> – <?=$this->transEsc('page_reload_on_deselect_all_hint', ['%%filter_name%%' => $value['displayText']])?>"> + <?=$this->transEsc('reset_filters_button')?> + </a> + <?php endif; ?> + <div class="filters-toggle collapsed" data-toggle="collapse" data-target="#active-filters-mobile"> + <?=$this->transEsc('show_filters_html', ['%%count%%' => $filterCount])?> + </div> + <div class="clearfix"></div> + </div> + <div id="active-filters-mobile" class="filters filters-bar collapse"> + <?=$filters ?> + </div> + <div class="clearfix"></div> + </div> +<?php endif; ?> +<!-- finc - search - filters - END --> diff --git a/themes/finc/templates/search/history-table.phtml b/themes/finc/templates/search/history-table.phtml index ff9e0554e3200b859b4c7653449aedc013ad92c8..af9dbf8c32764854c543b69b87ccec88bc853af5 100644 --- a/themes/finc/templates/search/history-table.phtml +++ b/themes/finc/templates/search/history-table.phtml @@ -1,55 +1,70 @@ <!-- finc - templates - search - history-table --> -<?php $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> -<?php /* include responsive data table - CK */ ?> -<table class="table table-striped table-resp-data-md"> +<?php + $scheduleSupported = !empty($this->schedule); + $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; +?> +<?php /* finc adds code for responsive data table here - CK */ ?> +<table class="search-history-table table-striped table-resp-data-md" id="<?=$this->showSaved ? 'saved-searches' : 'recent-searches'?>"> <tr> - <th><?= $this->transEsc("history_time") ?></th> - <th><?= $this->transEsc("history_search") ?></th> - <th><?= $this->transEsc("history_limits") ?></th> - <th><?= $this->transEsc("history_results") ?></th> - <?php if ($saveSupported): ?> - <th><?= $this->transEsc($this->showSaved ? "history_delete" : "history_save") ?></th><?php endif; ?> + <th><?=$this->transEsc("history_time")?></th> + <th><?=$this->transEsc("history_search")?></th> + <th><?=$this->transEsc("history_limits")?></th> + <th><?=$this->transEsc("history_results")?></th> + <?php if ($scheduleSupported): ?><th class="search-schedule-header"><?=$this->transEsc('history_schedule')?></th><?php endif; ?> + <?php if ($saveSupported): ?><th><?=$this->transEsc($this->showSaved ? "history_delete" : "history_save")?></th><?php endif; ?> </tr> - <?php foreach (($this->showSaved ? array_reverse($this->saved) : array_reverse($this->unsaved)) as $iteration => $info): ?> - <tr class="<?= $iteration % 2 == 1 ? 'even' : 'odd' ?>row"> - <td data-title="<?= $this->transEsc('history_time') ?>:"><?= $this->escapeHtml($this->dateTime()->convertToDisplayDateAndTime("U", $info->getStartTime())) ?></td> - - <td data-title="<?= $this->transEsc('history_search') ?>:"> - <?= $this->historylabel($info->getParams()->getSearchClassId()) ?> - <a href="<?= $this->url($info->getOptions()->getSearchAction()) . $info->getUrlQuery()->getParams() ?>" lang=""><?php $desc = $info->getParams()->getDisplayQuery(); + <?php foreach (($this->showSaved ? array_reverse($this->saved) : array_reverse($this->unsaved)) as $iteration => $info): ?> + <tr class="<?=$iteration % 2 == 1 ? 'even' : 'odd'?>row"> + <td data-title="<?= $this->transEsc('history_time') ?>:"><?=$this->escapeHtml($this->dateTime()->convertToDisplayDateAndTime("U", $info->getStartTime()))?></td> + <td data-title="<?= $this->transEsc('history_search') ?>:"> + <?=$this->historylabel($info->getParams()->getSearchClassId())?> + <?php /* add lang undefines tag */ ?> + <a href="<?=$this->url($info->getOptions()->getSearchAction()) . $info->getUrlQuery()->getParams()?>" lang=""><?php + $desc = $info->getParams()->getDisplayQuery(); echo empty($desc) ? $this->transEsc("history_empty_search") : $this->escapeHtml($desc); - ?> - </a> - </td> - - <td data-title="<?= $this->transEsc('history_limits') ?>:"> - <?php $info->getParams()->activateAllFacets(); - foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> - <?php foreach ($filters as $i => $filter): ?> - <?php if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; - if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?> - <strong><?= $this->transEsc($field) ?></strong>: <?= $this->escapeHtml($filter['displayText']) ?><br/> - <?php endforeach; ?> + ?></a> + </td> + <td data-title="<?= $this->transEsc('history_limits') ?>:"> + <?php foreach ($info->getParams()->getFilterList(true) as $field => $filters): ?> + <?php foreach ($filters as $i => $filter): ?> + <?php if ($filter['operator'] == 'NOT') echo $this->transEsc('NOT') . ' '; if ($filter['operator'] == 'OR' && $i > 0) echo $this->transEsc('OR') . ' '; ?> + <strong><?=$this->transEsc($field)?></strong>: <?=$this->escapeHtml($filter['displayText'])?><br/> + <?php endforeach; ?> + <?php endforeach; ?> + <?php foreach($info->getParams()->getCheckboxFacets() as $facet): ?> + <?php if ($facet['selected']): ?> + <strong><?=$this->transEsc($facet['desc'])?></strong><br/> + <?php endif; ?> + <?php endforeach; ?> + </td> + <td data-title="<?= $this->transEsc('history_results') ?>:"><?=$this->escapeHtml($this->localizedNumber($info->getResultTotal()))?></td> + <?php if ($scheduleSupported): ?> + <td> + <?php if (isset($this->schedule[$info->getSearchId()])): ?> + <?php $schedule = $this->schedule[$info->getSearchId()]; ?> + <form class="form-inline jumpMenuForm" action="<?= $this->url('myresearch-savesearch')?>" method="get" name="schedule"> + <select name="schedule" class="jumpMenu form-control" aria-haspopup="true" title="<?=$this->transEsc("history_schedule")?>"> + <?php foreach ($scheduleOptions as $scheduleValue => $scheduleLabel): ?> + <option value="<?=$this->escapeHtmlAttr($scheduleValue)?>"<?=($schedule == $scheduleValue) ? (' selected') : ('')?>><?=$this->transEsc($scheduleLabel)?></option> <?php endforeach; ?> - <?php foreach ($info->getParams()->getCheckboxFacets() as $facet): ?> - <?php if ($facet['selected']): ?> - <strong><?= $this->transEsc($facet['desc']) ?></strong><br/> - <?php endif; ?> - <?php endforeach; ?> + </select> + <input type="hidden" name="searchid" value="<?= $info->getSearchId(); ?>"/> + </form> + <?php else: ?> + <span class="disable"><?=$this->transEsc("cannot set")?></span> + <?php endif; ?> </td> - - <td data-title="<?= $this->transEsc('history_results') ?>:"><?= $this->escapeHtml($this->localizedNumber($info->getResultTotal())) ?></td> - <?php if ($saveSupported): ?> - - <td data-title="<?= $this->transEsc($this->showSaved ? "history_delete" : "history_save") ?>"> - <?php if ($this->showSaved): ?> - <a href="<?= $this->url('myresearch-savesearch') ?>?delete=<?= urlencode($info->getSearchId()) ?>&mode=history"><i class="fa fa-remove" aria-hidden="true"></i> <?= $this->transEsc('history_delete_link') ?></a> - <?php else: ?> - <a href="<?= $this->url('myresearch-savesearch') ?>?save=<?= urlencode($info->getSearchId()) ?>&mode=history"><i class="fa fa-save" aria-hidden="true"></i> <?= $this->transEsc("history_save_link") ?></a> - <?php endif; ?> - </td> + <?php endif; ?> + <?php if ($saveSupported): ?> + <td data-title="<?= $this->transEsc($this->showSaved ? "history_delete" : "history_save") ?>"> + <?php if ($this->showSaved): ?> + <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($info->getSearchId())?>&mode=history" class="text-danger"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_delete_link")?></a> + <?php else: ?> + <a href="<?=$this->url('myresearch-savesearch')?>?save=<?=urlencode($info->getSearchId())?>&mode=history"><i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc("history_save_link")?></a> <?php endif; ?> - </tr> - <?php endforeach; ?> + </td> + <?php endif; ?> + </tr> + <?php endforeach; ?> </table> <!-- finc - templates - search - history-table - END --> diff --git a/themes/finc/templates/search/history.phtml b/themes/finc/templates/search/history.phtml index 04a7ee298e0aba39bd1d24479b4888e5c268933f..09b11449d0677292ce8770da80f1b2cbd3d65011 100644 --- a/themes/finc/templates/search/history.phtml +++ b/themes/finc/templates/search/history.phtml @@ -3,41 +3,50 @@ // Set page title. $this->headTitle($this->translate('Search History')); + // finc: handle disable account capabilities $loginEnabled = is_object($account = $this->auth()->getManager()) && $account->loginEnabled(); // Set up breadcrumbs: + // finc: check for $loginEnabled $this->layout()->breadcrumbs = ($loginEnabled ? '<li><a href="' . $this->url('myresearch-home') . '">' . $this->transEsc('Your Account') . '</a></li>' : '') . '<li class="active">' . $this->transEsc('History') . '</li>'; $saveSupported = $this->accountCapabilities()->getSavedSearchSetting() === 'enabled'; ?> +<?php /* finc: show only when if $saveSupported */ ?> <?php if ($saveSupported): ?> + <?php /* finc removes title and uses aria-labek */ ?> <a class="search-filter-toggle visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand')?>"> <?=$this->transEsc('Your Account') ?> </a> <?php endif; ?> <div class="<?=$this->layoutClass('mainbody')?>"> - <?=$this->flashmessages()?> - <?php if ($saveSupported && !empty($this->saved)): ?> - <h1 class="sr-only"><?=$this->transEsc('Search History')?></h1> + <?php if (!empty($this->alertemail)): ?> + <div class="alert alert-info alert-email-notification"> + <?=$this->transEsc("alert_email_address") . ': ' . $this->alertemail ?> + <?php if ($this->auth()->getManager()->supportsEmailChange()): ?> + (<a href="<?=$this->url('myresearch-profile');?>"><?=$this->transEsc("edit");?></a>) + <?php endif; ?> + </div> + <?php elseif (!empty($this->schedule) && $this->auth()->isLoggedIn()): ?> + <div class="alert alert-danger alert-email-notification"> + <?=$this->transEsc("no_email_address") . ' ';?><a href="<?=$this->url('myresearch-profile');?>"><?=$this->transEsc("check_profile");?></a> + </div> <?php endif; ?> - + <?=$this->flashmessages()?> + <?php /* finc: add h1 heading for correct headings' hierarchy */ ?> + <h1><?=$this->transEsc('Search History')?></h1> <?php if ($saveSupported && !empty($this->saved)): ?> <h2><?=$this->transEsc("history_saved_searches")?></h2> <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => true]);?> <?php endif; ?> - <?php if ($saveSupported && !empty($this->saved)): ?> - <h2><?=$this->transEsc("history_recent_searches")?></h2> - <?php else: ?> - <h1><?=$this->transEsc("history_recent_searches")?></h1> - <?php endif; ?> + <h2><?=$this->transEsc("history_recent_searches")?></h2> <?php if (!empty($this->unsaved)): ?> <?=$this->context()->renderInContext('search/history-table.phtml', ['showSaved' => false]);?> - <?php /* refs #17072: added url - GG */ ?> - <a href="<?= $this->url('search-history') ?>?purge=true"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_purge")?></a> + <a href="?purge=true"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc("history_purge")?></a> <?php else: ?> <?=$this->transEsc("history_no_searches")?> <?php endif; ?> @@ -53,4 +62,17 @@ ?> </div> <?php endif; ?> + +<?php + $explanation = $this->transEsc('schedule_explanation'); + $script = <<<JS +$(".search-schedule-header").popover({ + content: "$explanation", + placement: "top", + trigger: "hover", + container: "body", +}) +JS; +?> +<?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET') ?> <!-- finc - templates - search - history - END --> diff --git a/themes/finc/templates/search/home.phtml b/themes/finc/templates/search/home.phtml index 4fc8d20045f806bc73c723704385410dd97f7cd5..f77f0e14b892cd28e1931cf39b21b4c28892ed52 100644 --- a/themes/finc/templates/search/home.phtml +++ b/themes/finc/templates/search/home.phtml @@ -1,29 +1,27 @@ <!-- finc: search - home --> <?php - // Set page title. - $this->headTitle($this->translate('Search Home')); - - // finc: disable top search box here if you want the old look, see also below - // $this->layout()->searchbox = false; - - // Set default value if necessary: - if (!isset($this->searchClassId)) { - $this->searchClassId = 'Solr'; - } - - $this->layout()->breadcrumbs = false; + // Set page title. + $this->headTitle($this->translate('Search Home')); + + // finc: disable top search box here if you want the old look, see also below + // $this->layout()->searchbox = false; + + // Set default value if necessary: + if (!isset($this->searchClassId)) { + $this->searchClassId = 'Solr'; + } + + $this->layout()->breadcrumbs = false; ?> <div class="searchHomeContent"> <h1><?= $this->translate("LibraryName") ?></h1> - <?php /* finc: Activate search box below if you want the old look -- otherwise we keep the consistent look with searchbox in header */ ?> - <?php /* + <?php /* finc: Activate search box below if you want the old look -- otherwise we keep the consistent look with searchbox in header */ ?> + <?php /* <?=$this->context($this)->renderInContext("search/searchbox.phtml", ['ignoreHiddenFilterMemory' => true])?> <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, '$("#searchForm_lookfor").focus();', 'SET'); ?> + */ ?> </div> - */ ?> -</div> - -<?= implode('', array_map([$this, 'contentBlock'], $blocks ?? [])) ?> +<?=implode('', array_map([$this, 'contentBlock'], $blocks ?? []))?> <!-- finc: search - home - END --> diff --git a/themes/finc/templates/search/list-authorfacets.phtml b/themes/finc/templates/search/list-authorfacets.phtml index 22d89f7d59451aa88f3e9d4e8d5283fc763cb165..438eacc056e4d064e08d4e3119b2efb4b111345b 100644 --- a/themes/finc/templates/search/list-authorfacets.phtml +++ b/themes/finc/templates/search/list-authorfacets.phtml @@ -1,5 +1,5 @@ <!-- finc - templates - search - list-authorfacets --> -<?php /* include responsive data table - CK */ ?> +<?php /* finc adds code for responsive data table here - CK */ ?> <table class="table table-striped table-resp-data"> <tbody> <tr> diff --git a/themes/finc/templates/search/newitem.phtml b/themes/finc/templates/search/newitem.phtml index a4af3a11f2d6a141fb86eef281d19a124529a40b..3ce7c3d7fcc7b9ac96a66ad5d6e39ffd5698a58c 100644 --- a/themes/finc/templates/search/newitem.phtml +++ b/themes/finc/templates/search/newitem.phtml @@ -2,24 +2,28 @@ <?php // Set up page title: $this->headTitle($this->translate('New Item Search')); - + // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li class="active">' . $this->transEsc('New Items') . '</li>'; - + // Convenience variable: $offlineMode = $this->ils()->getOfflineMode(); ?> <div class="mainbody"> + <?php /* finc uses h1 here */ ?> <h1><?= $this->transEsc('Find New Items') ?></h1> <?php if ($offlineMode == "ils-offline"): ?> <?= $this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message']) ?> <?php endif; ?> <form method="get" class="form-search-newitem"> <div class="form-group"> + <?php /* finc uses legend instead of label for form consistency */ ?> <legend><?= $this->transEsc("Choose Period") ?>:</legend> <?php foreach ($this->ranges as $key => $range): ?> <input type="radio" name="range" id="newitem_range_<?= $this->escapeHtmlAttr($key) ?>" value="<?= $this->escapeHtmlAttr($range) ?>"<?= ($key == 0) ? ' checked="checked"' : '' ?>/> + <?php /* finc: adapt label #17613 */ ?> <label for="newitem_range_<?= $this->escapeHtmlAttr($key) ?>"> + <?php /* finc: add 'From' #17613 */ ?> <?= ($range == 1) ? $this->transEsc('From') . " " . $this->transEsc('Yesterday') : $this->transEsc('past_days', ['%%range%%' => $this->escapeHtml($range)]) ?> </label> <?php endforeach; ?> @@ -34,6 +38,7 @@ </select> </div> <?php endif; ?> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?= $this->transEsc('Find') ?>" aria-label="<?= $this->transEsc('Find')?>"/> </form> </div> diff --git a/themes/finc/templates/search/pagination.phtml b/themes/finc/templates/search/pagination.phtml index 9d64ed8107b1ae206f39625d16ba35616a23a231..b2e9b787d5e91a5f9605701334b758aad8f77389 100644 --- a/themes/finc/templates/search/pagination.phtml +++ b/themes/finc/templates/search/pagination.phtml @@ -1,7 +1,9 @@ <!-- finc - templates - search - pagination --> <?php if ($this->pageCount > 1): ?> <?php /* Adapted to the requirements of https://a11y-style-guide.com/style-guide/section-navigation.html#kssref-navigation-pagination - CK */ ?> + <?php /* finc: aria-label in nav instead of ul */ ?> <nav aria-label="<?=$this->transEsc('pagination_label')?>"> + <?php /* finc: remove role="none" of li-elements */ ?> <ul class="pagination"> <?php if (isset($this->previous)): ?> <?php if (!isset($this->options['disableFirst']) || !$this->options['disableFirst']): ?> diff --git a/themes/finc/templates/search/reserves.phtml b/themes/finc/templates/search/reserves.phtml index 723436e1af346726ff1688b88290f313811629a6..b4accad7c2efd4efe94195c2d2e654d9140368e1 100644 --- a/themes/finc/templates/search/reserves.phtml +++ b/themes/finc/templates/search/reserves.phtml @@ -11,10 +11,10 @@ // Convenience variable: $offlineMode = $this->ils()->getOfflineMode(); ?> - <?php if ($offlineMode == "ils-offline"): ?> <?=$this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message'])?> <?php else: ?> + <?php /* finc uses h1 here */ ?> <h1><?=$this->transEsc('Search For Items on Reserve')?></h1> <form method="get" name="searchForm" class="form-search-reserves"> <?php if (is_array($this->courseList)): ?> @@ -26,6 +26,7 @@ <option value="<?=$this->escapeHtmlAttr($courseId)?>"><?=$this->escapeHtml($courseName)?></option> <?php endforeach; ?> </select> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>" aria-label="<?= $this->transEsc('Find')?>"/> </div> <?php endif; ?> @@ -39,6 +40,7 @@ <option value="<?=$this->escapeHtmlAttr($instId)?>"><?=$this->escapeHtml($instName)?></option> <?php endforeach; ?> </select> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>" aria-label="<?= $this->transEsc('Find')?>"/> </div> <?php endif; ?> @@ -52,6 +54,7 @@ <option value="<?=$this->escapeHtmlAttr($deptId)?>"><?=$this->escapeHtml($deptName)?></option> <?php endforeach; ?> </select> + <?php /* finc adds aria-label */ ?> <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc('Find')?>" aria-label="<?= $this->transEsc('Find')?>"/> </div> <?php endif; ?> diff --git a/themes/finc/templates/search/reservessearch.phtml b/themes/finc/templates/search/reservessearch.phtml index a106b10f9690941375ee7fcfc65267c91493e42f..8bb5b976a78a564581cd5f06dd87cc806415f0b1 100644 --- a/themes/finc/templates/search/reservessearch.phtml +++ b/themes/finc/templates/search/reservessearch.phtml @@ -2,90 +2,89 @@ <?php // Set up page title: $this->headTitle($this->translate('Reserves Search')); - + // Set up breadcrumbs: $this->layout()->breadcrumbs = '<li class="active">' . $this->transEsc('Reserves') . '</li>'; - + // Convenience variables: $reservesLookfor = $this->params->getDisplayQuery(); - + $this->searchClassId = 'SolrReserves'; ?> -<div class="<?= $this->layoutClass('mainbody') ?>"> - <h3><?= $this->transEsc('Search For Items on Reserve') ?></h3> +<div class="<?=$this->layoutClass('mainbody')?>"> + <h3><?=$this->transEsc('Search For Items on Reserve')?></h3> <form class="form-inline" method="get" name="reservesSearchForm"> - <label for="reservesSearchForm_lookfor"><?= $this->transEsc("Your search terms") ?></label> - <input id="reservesSearchForm_lookfor" type="text" name="lookfor" size="40" - value="<?= $this->escapeHtmlAttr($reservesLookfor) ?>" <?= $this->searchOptions('SolrReserves')->autocompleteEnabled() ? ' class="autocomplete searcher:SolrReserves type:Reserves"' : '' ?> /> - <input class="btn btn-primary" type="submit" name="submit" value="<?= $this->transEsc("Find") ?>"/> + <label for="reservesSearchForm_lookfor"><?=$this->transEsc("Your search terms")?></label> + <input id="reservesSearchForm_lookfor" type="text" name="lookfor" size="40" value="<?=$this->escapeHtmlAttr($reservesLookfor)?>" <?=$this->searchOptions('SolrReserves')->autocompleteEnabled() ? ' class="autocomplete searcher:SolrReserves type:Reserves"' : ''?> /> + <input class="btn btn-primary" type="submit" name="submit" value="<?=$this->transEsc("Find")?>"/> </form> - <script type="text/javascript">$("#reservesSearchForm_lookfor").focus()</script> + <script>$("#reservesSearchForm_lookfor").focus()</script> <div class="resulthead"> <div class="pull-left flip"> - <?php $qtime = $this->results->getQuerySpeed(); ?> - <?php if (($recordTotal = $this->results->getResultTotal()) > 0): ?> - <?php - $transParams = [ - '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), - '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), - '%%total%%' => $this->localizedNumber($recordTotal), - '%%lookfor%%' => $this->escapeHtml($reservesLookfor) - ]; - ?> - <?= $this->translate('showing_results_of_for_html', $transParams); ?><?php if ($qtime): ?>,<?php endif; ?> - <?php endif; ?> - <?php if ($qtime): ?> - <?= $this->transEsc('query time') ?>: <?= $this->localizedNumber($qtime, 2) . $this->transEsc('seconds_abbrev') ?> - <?php endif; ?> - </div> - - <?php if ($recordTotal > 0): ?> - <div class="pull-right flip"> - <?= $this->render('search/controls/sort.phtml') ?> - </div> + <?php $qtime = $this->results->getQuerySpeed(); ?> + <?php if (($recordTotal = $this->results->getResultTotal()) > 0): ?> + <?php + $transParams = [ + '%%start%%' => $this->localizedNumber($this->results->getStartRecord()), + '%%end%%' => $this->localizedNumber($this->results->getEndRecord()), + '%%total%%' => $this->localizedNumber($recordTotal), + '%%lookfor%%' => $this->escapeHtml($reservesLookfor) + ]; + ?> + <?=$this->translate('showing_results_of_for_html', $transParams); ?><?php if($qtime): ?>,<?php endif; ?> + <?php endif; ?> + <?php if ($qtime): ?> + <?=$this->transEsc('query time')?>: <?=$this->localizedNumber($qtime, 2) . $this->transEsc('seconds_abbrev')?> <?php endif; ?> + </div> + + <?php if ($recordTotal > 0): ?> + <div class="pull-right flip"> + <?=$this->render('search/controls/sort.phtml')?> + </div> + <?php endif; ?> </div> - <?php if ($recordTotal < 1): ?> - <p class="error"><?= $this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($reservesLookfor)]) ?></p> - <?php if (isset($this->parseError)): ?> - <p class="error"><?= $this->transEsc('nohit_parse_error') ?></p> - <?php endif; ?> - <?php else: ?> - <?php /* include responsive data table - CK */ ?> - <table class="table table-striped table-resp-data"> - <tr> - <th class="department"><?= $this->transEsc('Department') ?></th> - <th class="course"><?= $this->transEsc('Course') ?></th> - <th class="instructor"><?= $this->transEsc('Instructor') ?></th> - <th class="items"><?= $this->transEsc('Items') ?></th> - </tr> - <?php foreach ($this->results->getResults() as $record): ?> - <?php - $url = $this->currentPath() . $this->escapeHtmlAttr( - '?inst=' . urlencode($record->getInstructorId()) - . '&course=' . urlencode($record->getCourseId()) - . '&dept=' . urlencode($record->getDepartmentId()) - ); - ?> - <tr> - <td data-title="<?= $this->transEsc("Department") ?>:" class="department"><a href="<?= $url ?>"><?= $this->escapeHtml($record->getDepartment()) ?></a></td> - <td data-title="<?= $this->transEsc("Course") ?>:" class="course"><a href="<?= $url ?>"><?= $this->escapeHtml($record->getCourse()) ?></a></td> - <td data-title="<?= $this->transEsc("Instructor") ?>:" class="instructor"><a href="<?= $url ?>"><?= $this->escapeHtml($record->getInstructor()) ?></a></td> - <td data-title="<?= $this->transEsc("Items") ?>:" class="items"><?= $this->escapeHtml($record->getItemCount()) ?></td> - </tr> - <?php endforeach; ?> - </table> - <?= $this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results]) ?> + <?php if ($recordTotal < 1): ?> + <p class="error"><?=$this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($reservesLookfor)]) ?></p> + <?php if (isset($this->parseError)): ?> + <p class="error"><?=$this->transEsc('nohit_parse_error')?></p> <?php endif; ?> + <?php else: ?> + <?php /* finc adds code for responsive data table here - CK */ ?> + <table class="table table-striped table-resp-data"> + <tr> + <th class="department"><?=$this->transEsc('Department')?></th> + <th class="course"><?=$this->transEsc('Course')?></th> + <th class="instructor"><?=$this->transEsc('Instructor')?></th> + <th class="items"><?=$this->transEsc('Items')?></th> + </tr> + <?php foreach ($this->results->getResults() as $record): ?> + <?php + $url = $this->currentPath() . $this->escapeHtmlAttr( + '?inst=' . urlencode($record->getInstructorId()) + . '&course=' . urlencode($record->getCourseId()) + . '&dept=' . urlencode($record->getDepartmentId()) + ); + ?> + <tr> + <td data-title="<?= $this->transEsc("Department") ?>:" class="department"><a href="<?=$url?>"><?=$this->escapeHtml($record->getDepartment())?></a></td> + <td data-title="<?= $this->transEsc("Course") ?>:" class="course"><a href="<?=$url?>"><?=$this->escapeHtml($record->getCourse())?></a></td> + <td data-title="<?= $this->transEsc("Instructor") ?>:" class="instructor"><a href="<?=$url?>"><?=$this->escapeHtml($record->getInstructor())?></a></td> + <td data-title="<?= $this->transEsc("Items") ?>:" class="items"><?=$this->escapeHtml($record->getItemCount())?></td> + </tr> + <?php endforeach; ?> + </table> + <?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results])?> + <?php endif; ?> </div> <?php /* Narrow Search Options */ ?> -<div class="<?= $this->layoutClass('sidebar') ?>"> - <?php foreach ($this->results->getRecommendations('side') as $current): ?> - <?= $this->recommend($current) ?> - <?php endforeach; ?> +<div class="<?=$this->layoutClass('sidebar')?>"> + <?php foreach ($this->results->getRecommendations('side') as $current): ?> + <?=$this->recommend($current)?> + <?php endforeach; ?> </div> <?php /* End Narrow Search Options */ ?> <!-- finc - templates - search - reservessearch - END --> diff --git a/themes/finc/templates/search/results.phtml b/themes/finc/templates/search/results.phtml index ac1463ded86c138a15cb829b1375f04ab491f445..a693c86001aebb551519df7a8f84038d7c2bdb3a 100644 --- a/themes/finc/templates/search/results.phtml +++ b/themes/finc/templates/search/results.phtml @@ -1,86 +1,118 @@ <!-- finc: search - results --> <?php -$this->context = 'search_result'; + // finc-specific #18754 + $this->context = 'search_result'; -// Set up page title: -$lookfor = $this->results->getUrlQuery()->isQuerySuppressed() ? '' : $this->params->getDisplayQuery(); -if (isset($this->overrideTitle)) { - $this->headTitle($this->overrideTitle); -} else { - $this->headTitle($this->translate('Search Results') . (empty($lookfor) ? '' : " - {$lookfor}")); -} + // Set up page title: + $lookfor = $this->results->getUrlQuery()->isQuerySuppressed() ? '' : $this->params->getDisplayQuery(); + if (isset($this->overrideTitle)) { + $this->headTitle($this->overrideTitle); + } else { + $this->headTitle($this->translate('Search Results') . (empty($lookfor) ? '' : " - {$lookfor}")); + } -// Set up search box: -$this->layout()->searchbox = $this->context($this)->renderInContext( - 'search/searchbox.phtml', - [ - 'lookfor' => $lookfor, - 'searchIndex' => $this->params->getSearchHandler(), - 'searchType' => $this->params->getSearchType(), - 'searchId' => $this->results->getSearchId(), - 'searchClassId' => $this->params->getSearchClassId(), - 'checkboxFilters' => $this->params->getCheckboxFacets(), - 'filterList' => $this->params->getFilters(), - 'hasDefaultsApplied' => $this->params->hasDefaultsApplied(), - 'selectedShards' => $this->params->getSelectedShards(), - 'ignoreHiddenFiltersInRequest' => isset($this->ignoreHiddenFiltersInRequest) ? $this->ignoreHiddenFiltersInRequest : false, - 'ignoreHiddenFilterMemory' => isset($this->ignoreHiddenFilterMemory) ? $this->ignoreHiddenFilterMemory : false, - ] -); + // Set up search box: + $this->layout()->searchbox = $this->context($this)->renderInContext( + 'search/searchbox.phtml', + [ + 'lookfor' => $lookfor, + 'searchIndex' => $this->params->getSearchHandler(), + 'searchType' => $this->params->getSearchType(), + 'searchId' => $this->results->getSearchId(), + 'searchClassId' => $this->params->getSearchClassId(), + 'checkboxFilters' => $this->params->getCheckboxFacets(), + 'filterList' => $this->params->getFilterList(true), + 'hasDefaultsApplied' => $this->params->hasDefaultsApplied(), + 'selectedShards' => $this->params->getSelectedShards(), + 'ignoreHiddenFiltersInRequest' => isset($this->ignoreHiddenFiltersInRequest) ? $this->ignoreHiddenFiltersInRequest : false, + 'ignoreHiddenFilterMemory' => isset($this->ignoreHiddenFilterMemory) ? $this->ignoreHiddenFilterMemory : false, + ] + ); -// Set up breadcrumbs: -if (isset($this->overrideTitle)) { - $this->layout()->breadcrumbs .= '<li class="active">' . $this->escapeHtml($this->overrideTitle) . '</li>'; -} else { - $this->layout()->breadcrumbs .= '<li class="active">' . $this->transEsc('Search') . ': ' . $this->escapeHtml($lookfor) . '</li>'; -} + // Set up breadcrumbs: + if (isset($this->overrideTitle)) { + $this->layout()->breadcrumbs .= '<li class="active">' . $this->escapeHtml($this->overrideTitle) . '</li>'; + } else { + $this->layout()->breadcrumbs .= '<li class="active">' . $this->transEsc('Search') . ': ' . $this->escapeHtml($lookfor) . '</li>'; + } -// Enable cart if appropriate: -$this->showBulkOptions = $this->params->getOptions()->supportsCart() && $this->showBulkOptions; -// Checkboxes if appropriate: -$this->showCartControls = $this->params->getOptions()->supportsCart() && $this->cart()->isActive() - && ($this->showBulkOptions || !$this->cart()->isActiveInSearch()); -// Enable bulk options if appropriate: -$this->showCheckboxes = $this->showCartControls || $this->showBulkOptions; + // Enable cart if appropriate: + $this->showBulkOptions = $this->params->getOptions()->supportsCart() && $this->showBulkOptions; + // Checkboxes if appropriate: + $this->showCartControls = $this->params->getOptions()->supportsCart() && $this->cart()->isActive() + && ($this->showBulkOptions || !$this->cart()->isActiveInSearch()); + // Enable bulk options if appropriate: + $this->showCheckboxes = $this->showCartControls || $this->showBulkOptions; -// Load Javascript only if list view parameter is NOT full: -if ($this->params->getOptions()->getListViewOption() != "full") { - $this->headScript()->appendFile("record.js"); - $this->headScript()->appendFile("embedded_record.js"); -} + // Load Javascript only if list view parameter is NOT full: + if ($this->params->getOptions()->getListViewOption() != "full") { + $this->headScript()->appendFile("record.js"); + $this->headScript()->appendFile("embedded_record.js"); + } -// Load Javascript dependencies into header: -$this->headScript()->appendFile("vendor/hunt.min.js"); -$this->headScript()->appendFile("check_item_statuses.js"); -$this->headScript()->appendFile("check_save_statuses.js"); + // Load Javascript dependencies into header: + $this->headScript()->appendFile("vendor/hunt.min.js"); + $this->headScript()->appendFile("check_item_statuses.js"); + $this->headScript()->appendFile("check_save_statuses.js"); + + // finc: make active filters available here + if (!isset($this->filterList) || !isset($this->checkboxFilters)) { + $params = $this->searchMemory()->getLastSearchParams($this->searchClassId); + $filterList = $params->getFilterList(true); + $checkboxFilters = $params->getCheckboxFacets(); + } else { + $filterList = is_array($this->filterList) ? $this->filterList : []; + $checkboxFilters = is_array($this->checkboxFilters) ? $this->checkboxFilters : []; + } + + $filterDetails = $this->searchbox()->getFilterDetails($filterList, $checkboxFilters); + $showFilters = $filterDetails && (isset($results) || $options->getRetainFilterSetting()); + // finc: make active filters available here - END ?> <?php /* finc: we need search-results-col to pull content to full width, also used in print styles! - CK */?> <div class="<?=$this->layoutClass('mainbody')?> search-results-col"> <h1 class="sr-only"><?=$this->transEsc('Search Results')?></h1> - <?php if (($recordTotal = $this->results->getResultTotal()) > 0): // only display these at very top if we have results ?> + <?php if (($recordTotal = $this->results->getResultTotal()) > 0): // only display these at very top if we have results ?> <?php foreach ($this->results->getRecommendations('top') as $index => $current): ?> <?=$this->recommend($current, 'top', $index)?> - <?php endforeach; ?> - <?php endif; ?> - <?=$this->flashmessages()?> - <?php /* finc: remove 'hidden' below to show search-stats; we also hide the entire bar on xs + sm - CK */ ?> - <div class="search-header hidden-print"> - <div class="search-stats"> - <?php /* finc: use spans for easier to show/hide choices - CK */ ?> - <?php if ($recordTotal > 0): ?> - <span class="hit-stats"> + <?php endforeach; ?> + <?php endif; ?> + <?=$this->flashmessages()?> + + <?php /* finc: Show active filters here and not in searchbox.phtml */ ?> + <?=$this->context($this)->renderInContext( + 'search/filters.phtml', + [ + 'params' => $params ?? null, + 'urlQuery' => isset($results) ? $results->getUrlQuery() : null, + 'filterList' => $showFilters ? $filterList : [], + 'checkboxFilters' => $showFilters ? $checkboxFilters : [], + 'searchClassId' => $this->searchClassId, + 'searchType' => $this->searchType, + ] + );?> + <?php /* finc: Show active filters here and not in searchbox.phtml - END */ ?> + + <?php /* finc: remove 'hidden' below to show search-stats; we also hide the entire bar on xs + sm - CK */ ?> + <div class="search-header hidden-print"> + <div class="search-stats"> + <?php /* finc: use spans for easier to show/hide choices - CK */ ?> + <?php if ($recordTotal > 0): ?> + <span class="hit-stats"> <?=$this->context()->renderInContext('search/controls/showing.phtml', ['lookfor' => $lookfor, 'recordTotal' => $recordTotal]) ?> - </span> - <span class="offcanvas-toggler"> - <a class="search-filter-toggle btn btn-primary visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand') ?>"> + </span> + <span class="offcanvas-toggler"> + <?php /* finc: remove title, add aria-label */ ?> + <?php /* finc: change href #search-sidebar to #myresearch-sidebar, #17908 */ ?> + <a class="search-filter-toggle btn btn-primary visible-xs" href="#myresearch-sidebar" data-toggle="offcanvas" aria-label="<?=$this->transEsc('sidebar_expand') ?>"> <?=$this->transEsc('Refine Results') ?> - </a> - </span> - <?php else: ?> - <h2><?=$this->transEsc('nohit_heading')?></h2> - <?php endif; ?> - </div> + </a> + </span> + <?php else: ?> + <h2><?=$this->transEsc('nohit_heading')?></h2> + <?php endif; ?> + </div> <?php if ($recordTotal > 0): ?> <?php /* finc: use spans for easier to show/hide choices - CK */ ?> @@ -88,7 +120,7 @@ $this->headScript()->appendFile("check_save_statuses.js"); <div class="limit"> <?=$this->render('search/controls/limit.phtml')?> </div> - <div class="sort right"> + <div class="sort"> <?=$this->render('search/controls/sort.phtml')?> </div> <div class="view"> @@ -105,11 +137,11 @@ $this->headScript()->appendFile("check_save_statuses.js"); <?=$this->overrideEmptyMessage?> <?php else: ?> <?php $this->layout()->srmessage = $this->translate('nohit_lookfor_html', ['%%lookfor%%' => $this->escapeHtml($lookfor)]); ?> - <?=$this->layout()->srmessage?> + <?=$this->layout()->srmessage ?> <?php endif; ?> </p> <?php if (isset($this->parseError)): ?> - <p class="alert alert-danger"><?=$this->transEsc('nohit_parse_error')?></p> + <p class="alert alert-danger" aria-live="polite"><?=$this->transEsc('nohit_parse_error')?></p> <?php endif; ?> <?php foreach (($top = $this->results->getRecommendations('top')) as $index => $current): ?> <?=$this->recommend($current, 'top', $index)?> @@ -123,10 +155,30 @@ $this->headScript()->appendFile("check_save_statuses.js"); <form id="search-cart-form" method="post" name="bulkActionForm" action="<?=$this->url('cart-searchresultsbulk')?>" data-lightbox data-lightbox-onsubmit="bulkFormHandler"> <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => ''])?> </form> - <?=$this->render('search/list-' . $this->params->getView() . '.phtml')?> + <?=$this->render('search/list-' . $this->params->getView() . '.phtml')?> <?=$this->context($this)->renderInContext('search/bulk-action-buttons.phtml', ['idPrefix' => 'bottom_', 'formAttr' => 'search-cart-form'])?> <?=$this->paginationControl($this->results->getPaginator(), 'Sliding', 'search/pagination.phtml', ['results' => $this->results, 'options' => isset($this->paginationOptions) ? $this->paginationOptions : []])?> + <?php /* finc: remove searchtools #18164 *//* + <div class="searchtools hidden-print"> + <strong><?=$this->transEsc('Search Tools')?>:</strong> + <a href="<?=$this->results->getUrlQuery()->setViewParam('rss')?>"><i class="fa fa-bell" aria-hidden="true"></i> <?=$this->transEsc('Get RSS Feed')?></a> + — + <a href="<?=$this->url('search-email')?>" class="mailSearch" data-lightbox id="mailSearch<?=$this->escapeHtmlAttr($this->results->getSearchId())?>"> + <i class="fa fa-envelope" aria-hidden="true"></i> <?=$this->transEsc('Email this Search')?> + </a> + <?php if ($this->accountCapabilities()->getSavedSearchSetting() === 'enabled'): ?> + — + <?php if (is_numeric($this->results->getSearchId())): ?> + <?php if ($this->results->isSavedSearch()): ?> + <a href="<?=$this->url('myresearch-savesearch')?>?delete=<?=urlencode($this->results->getSearchId())?>"><i class="fa fa-remove" aria-hidden="true"></i> <?=$this->transEsc('save_search_remove')?></a> + <?php else: ?> + <a href="<?=$this->url('myresearch-savesearch')?>?save=<?=urlencode($this->results->getSearchId())?>"><i class="fa fa-save" aria-hidden="true"></i> <?=$this->transEsc('save_search')?></a> + <?php endif; ?> + <?php endif; ?> + <?php endif; ?> + </div> + */ ?> <?php endif; ?> </div> <?php /* End Main Listing */ ?> diff --git a/themes/finc/templates/search/searchTabs.phtml b/themes/finc/templates/search/searchTabs.phtml index f5be8f1aaa1fb5a512e0a272b97ebad9c24e0fa7..04624f4e05f5a84344d107f5c6c24ebb1e28036f 100644 --- a/themes/finc/templates/search/searchTabs.phtml +++ b/themes/finc/templates/search/searchTabs.phtml @@ -1,16 +1,16 @@ <!-- finc: search - searchTabs --> +<?php /* finc: check isset($searchTabs) */ ?> <?php if (isset($searchTabs) && count($searchTabs) > 0): ?> <ul class="nav nav-tabs"> <?php foreach ($searchTabs as $tab): ?> - <?php if ($this->permission()->allowDisplay($tab['permission'])): ?> - <li<?=$tab['selected'] ? ' class="active"' : ''?>> - <a <?=$tab['selected'] ? '' : 'href="' . $this->escapeHtmlAttr($tab['url']) . '"'?>><?=$this->transEsc($tab['label']);?></a> - </li> - <?php elseif ($block = $this->permission()->getAlternateContent($tab['permission'])): ?> - <?=$block?> - <?php endif; ?> + <?php if ($this->permission()->allowDisplay($tab['permission'])): ?> + <li<?=$tab['selected'] ? ' class="active"' : ''?>> + <a <?=$tab['selected'] ? '' : 'href="' . $this->escapeHtmlAttr($tab['url']) . '"' ?>><?=$this->transEsc($tab['label']); ?></a> + </li> + <?php elseif ($block = $this->permission()->getAlternateContent($tab['permission'])): ?> + <?=$block?> + <?php endif; ?> <?php endforeach; ?> </ul> <?php endif; ?> - <!-- finc: search - searchTabs - END --> diff --git a/themes/finc/templates/search/searchbox.phtml b/themes/finc/templates/search/searchbox.phtml index 64bebce700d217d0c641d7a26c3623d0cc6628e9..5a34b3543f1057d51ea7dcad1469637986aef0e0 100644 --- a/themes/finc/templates/search/searchbox.phtml +++ b/themes/finc/templates/search/searchbox.phtml @@ -1,40 +1,54 @@ <!-- finc: search - searchbox --> <?php -// Set default value if necessary: -if (!isset($this->searchClassId)) { - $this->searchClassId = 'Solr'; -} + // Set default value if necessary: + if (!isset($this->searchClassId)) { + $config = $this->config()->get('config'); + $this->searchClassId = $config->Site->defaultSearchBackend ?? 'Solr'; + } -// Load search actions and settings (if any): -$options = $this->searchOptions($this->searchClassId); -$handlers = $this->searchbox()->getHandlers( - $this->searchClassId, - isset($this->searchIndex) ? $this->searchIndex : null -); -$handlerCount = count($handlers); -$basicSearch = $this->searchbox()->combinedHandlersActive() ? 'combined-searchbox' : $options->getSearchAction(); -$searchHome = $options->getSearchHomeAction(); -$advSearch = $options->getAdvancedSearchAction(); -$lastSort = $this->searchMemory()->getLastSort($this->searchClassId); -$lastLimit = $this->searchMemory()->getLastLimit($this->searchClassId); -$ignoreHiddenFilterMemory = isset($this->ignoreHiddenFilterMemory) && $this->ignoreHiddenFilterMemory; -$ignoreHiddenFiltersInRequest = isset($this->ignoreHiddenFiltersInRequest) && $this->ignoreHiddenFiltersInRequest; -$hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, $ignoreHiddenFilterMemory, $ignoreHiddenFiltersInRequest); -if (empty($hiddenFilters) && !$ignoreHiddenFilterMemory) { - $hiddenFilters = $this->searchMemory()->getLastHiddenFilters($this->searchClassId); - if (empty($hiddenFilters)) { - $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId); - } -} -$hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->searchClassId, $ignoreHiddenFilterMemory, '?'); + // Load search actions and settings (if any): + $options = $this->searchOptions($this->searchClassId); + $handlers = $this->searchbox()->getHandlers( + $this->searchClassId, $this->searchIndex ?? null + ); + $handlerCount = count($handlers); + $basicSearch = $this->searchbox()->combinedHandlersActive() ? 'combined-searchbox' : $options->getSearchAction(); + $searchHome = $options->getSearchHomeAction(); + $advSearch = $options->getAdvancedSearchAction(); + $lastSort = $this->searchMemory()->getLastSort($this->searchClassId); + $lastLimit = $this->searchMemory()->getLastLimit($this->searchClassId); + $ignoreHiddenFilterMemory = $this->ignoreHiddenFilterMemory ?? false; + $ignoreHiddenFiltersInRequest = $this->ignoreHiddenFiltersInRequest ?? false; + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId, $ignoreHiddenFilterMemory, $ignoreHiddenFiltersInRequest); + if (empty($hiddenFilters) && !$ignoreHiddenFilterMemory) { + $hiddenFilters = $this->searchMemory()->getLastHiddenFilters($this->searchClassId); + if (empty($hiddenFilters)) { + $hiddenFilters = $this->searchTabs()->getHiddenFilters($this->searchClassId); + } + } + $hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->searchClassId, $ignoreHiddenFilterMemory, '?'); + + /* finc: Don't load active filters here but above search results + * uncommend following block and upcoming linked filter templates for displaying filter in header */ + /* + if (!isset($this->filterList) || !isset($this->checkboxFilters)) { + $params = $this->searchMemory()->getLastSearchParams($this->searchClassId); + $filterList = $params->getFilterList(true); + $checkboxFilters = $params->getCheckboxFacets(); + } else { + $filterList = is_array($this->filterList) ? $this->filterList : []; + $checkboxFilters = is_array($this->checkboxFilters) ? $this->checkboxFilters : []; + } + $filterDetails = $this->searchbox()->getFilterDetails($filterList, $checkboxFilters); + $showFilters = $filterDetails && (isset($results) || $options->getRetainFilterSetting()); + */ ?> <?php $tabConfig = $this->searchTabs()->getTabConfig($this->searchClassId, $this->lookfor, $this->searchIndex, $this->searchType, $hiddenFilters); ?> <?php if ($this->searchType == 'advanced'): ?> <?php /* finc: keep .no-margin-t or advanced search box will be pushed down too far (navbar-form) */ ?> <div class="navbar-form navbar-left flip no-margin-t"> <?php $tabs = $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $tabConfig['tabs']]); ?> - <?php if (!empty($tabs)): ?><?=$tabs?> - <div class="tab-content clearfix no-gutter-all"><?php endif; ?> + <?php if (!empty($tabs)): ?><?=$tabs ?><div class="tab-content clearfix no-gutter-all"><?php endif; ?> <?php /* Do not show the advanced search terms (adv_search_terms) because they will be shown within the breadcrumb - #17720 - HR */ ?> <?php /* <p class="adv_search_terms"><?=$this->transEsc("Your search terms")?> : "<strong><?=$this->escapeHtml($this->lookfor)?></strong>"</p> */ ?> <p class="adv_search_links"> @@ -42,9 +56,23 @@ $hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->s <a href="<?=$this->url($advSearch) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Advanced Search")?></a> <a href="<?=$this->url($searchHome) . $hiddenFilterParams?>"><?=$this->transEsc("Start a new Basic Search")?></a> </p> - <?php if (!empty($tabs)): ?></div><?php endif; ?> + <?php /* finc: Don't load active filters here but above search results */ /* + <?=$this->context($this)->renderInContext( + 'search/filters.phtml', + [ + 'params' => $params ?? null, + 'urlQuery' => isset($results) ? $results->getUrlQuery() : null, + 'filterList' => $showFilters ? $filterList : [], + 'checkboxFilters' => $showFilters ? $checkboxFilters : [], + 'searchClassId' => $this->searchClassId, + 'searchType' => $this->searchType, + ] + );?> + */ ?> + <?php if (!empty($tabs)): ?></div><?php endif; ?> </div> <?php else: ?> + <?php /* finc adds role='search' */ ?> <form id="searchForm" class="searchForm navbar-form navbar-left flip" method="get" action="<?=$this->url($basicSearch)?>" name="searchForm" autocomplete="off" role="search"> <?= $this->context($this)->renderInContext('search/searchTabs', ['searchTabs' => $tabConfig['tabs']]); ?> <?php $placeholder = $this->searchbox()->getPlaceholderText($tabConfig['selected']['id'] ?? null); ?> @@ -62,82 +90,101 @@ $hiddenFilterParams = $this->searchTabs()->getCurrentHiddenFilterParams($this->s role="searchbox" /> <?php if ($handlerCount > 1): ?> <select id="searchForm_type" class="searchForm_type form-control" name="type" data-native-menu="false" aria-label="<?=$this->transEsc("Search type")?>"> + <?php $currentGroup = $insideGroup = false; ?> <?php foreach ($handlers as $handler): ?> + <?php + if ($currentGroup !== ($handler['group'] ?? false)) { + $currentGroup = $handler['group']; + if ($insideGroup) { + echo '</optgroup>'; + } + if ($currentGroup) { + echo '<optgroup label="' . $this->escapeHtmlAttr($currentGroup) . '">'; + $insideGroup = true; + } else { + $insideGroup = false; + } + } + ?> <option value="<?=$this->escapeHtmlAttr($handler['value'])?>"<?=$handler['selected'] ? ' selected="selected"' : ''?>><?=$handler['indent'] ? '-- ' : ''?><?=$this->transEsc($handler['label'])?></option> <?php endforeach; ?> + <?php if ($insideGroup): ?> + </optgroup> + <?php endif; ?> </select> <?php elseif ($handlerCount == 1): ?> - <input type="hidden" name="type" value="<?=$this->escapeHtmlAttr($handlers[0]['value'])?>"/> + <input type="hidden" name="type" value="<?=$this->escapeHtmlAttr($handlers[0]['value'])?>" /> <?php endif; ?> + <?php /* finc adds aria-label */ ?> <button type="submit" class="btn btn-primary" aria-label="<?=$this->transEsc("Find")?>"><i class="fa fa-search" aria-hidden="true"></i></button> <?php if ($advSearch): ?> - <a href="<?=$this->url($advSearch) . ((isset($this->searchId) && $this->searchId) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams)?>" class="btn btn-transparent" role="button" rel="nofollow" aria-label="<?=$this->transEsc("Advanced")?>"><i class="fa fa-cog"></i> <span class="hidden-xs hidden-sm hidden-md"><?=$this->transEsc("Advanced")?></span></a> + <?php /* finc: change btn-link to btn-transparent, add role, add aria-label, add i-element, add span-element */ ?> + <a href="<?=$this->url($advSearch) . (($this->searchId ?? false) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams)?>" class="btn btn-transparent" role="button" rel="nofollow" aria-label="<?=$this->transEsc("Advanced")?>"><i class="fa fa-cog"></i> <span class="hidden-xs hidden-sm hidden-md"><?=$this->transEsc("Advanced")?></span></a> <?php endif; ?> <?php if ($geoUrl = $this->geocoords()->getSearchUrl($options)) : ?> - <a href="<?=$geoUrl?>" class="btn btn-link"><?=$this->transEsc('Geographic Search')?></a> + <a href="<?=$geoUrl ?>" class="btn btn-link"><?=$this->transEsc('Geographic Search')?></a> <?php endif; ?> - <?php /* finc: We hide this for small and keep the solution using labels, same below - CK */ ?> + <?php /* finc-specific: We hide this for small and keep the solution using labels, same below - CK */ ?> <div class="checkbox hidden-xs"> - <?php $shards = $options->getShards(); - if ($options->showShardCheckboxes() && !empty($shards)): ?> + <?php $shards = $options->getShards(); if ($options->showShardCheckboxes() && !empty($shards)): ?> <?php $selectedShards = isset($this->selectedShards) - ? $this->selectedShards : $options->getDefaultSelectedShards(); + ? $this->selectedShards : $options->getDefaultSelectedShards(); ?> <?php foreach ($shards as $shard => $val): ?> <?php $isSelected = empty($selectedShards) || in_array($shard, $selectedShards); ?> <label for="checkbox_<?=$this->escapeHtmlAttr($shard)?>"><?=$this->transEsc($shard)?> - <input type="checkbox" <?=$isSelected ? 'checked="checked" ' : ''?>name="shard[]" value='<?=$this->escapeHtmlAttr($shard)?>' id='checkbox_<?=$this->escapeHtmlAttr($shard)?>'/> + <input type="checkbox" <?=$isSelected ? 'checked="checked" ' : ''?>name="shard[]" value='<?=$this->escapeHtmlAttr($shard)?>' id='checkbox_<?=$this->escapeHtmlAttr($shard)?>'/> </label> <?php endforeach; ?> <?php endif; ?> </div> + <?php /* finc-specific - END */ ?> - <?php - $filterDetails = $this->searchbox()->getFilterDetails( - isset($this->filterList) && is_array($this->filterList) ? $this->filterList : [], - isset($this->checkboxFilters) && is_array($this->checkboxFilters) ? $this->checkboxFilters : [] - ); - ?> - <?php if ((isset($hasDefaultsApplied) && $hasDefaultsApplied) || !empty($filterDetails)): ?> - <?php $defaultFilterState = $options->getRetainFilterSetting() ? ' checked="checked"' : ''; ?> - <div class="checkbox"> - <label> - <input type="checkbox"<?=$defaultFilterState?> class="searchFormKeepFilters"/> - <?=$this->transEsc("basic_search_keep_filters")?> - </label> - </div> - <div class="hidden"> + <?php if (($hasDefaultsApplied ?? false) || !empty($filterDetails)): ?> + <?php if ($options->getRetainFilterSetting()): ?> <?php foreach ($filterDetails as $current): ?> - <input class="applied-filter" id="<?=$this->escapeHtmlAttr($current['id'])?>" type="checkbox"<?=$defaultFilterState?> name="filter[]" value="<?=$this->escapeHtmlAttr($current['value'])?>"/> - <label for="<?=$this->escapeHtmlAttr($current['id'])?>"><?=$this->escapeHtml($current['value'])?></label> + <input class="applied-filter" id="<?=$this->escapeHtmlAttr($current['id'])?>" type="hidden" name="filter[]" value="<?=$this->escapeHtmlAttr($current['value'])?>" /> <?php endforeach; ?> - <?php if (isset($hasDefaultsApplied) && $hasDefaultsApplied): ?> - <!-- this is a hidden element that flags whether or not default filters have been applied; - it is intentionally unlabeled, as users are not meant to manipulate it directly. --> - <input class="applied-filter" id="dfApplied" type="checkbox" name="dfApplied" value="1"<?=$defaultFilterState?> /> + <?php if ($hasDefaultsApplied ?? false): ?> + <input class="applied-filter" id="dfApplied" type="hidden" name="dfApplied" value="1" /> <?php endif; ?> - </div> + <?php endif; ?> <?php endif; ?> <?php foreach ($hiddenFilters as $key => $filter): ?> <?php foreach ($filter as $value): ?> - <input type="hidden" name="hiddenFilters[]" value="<?=$this->escapeHtmlAttr($key) . ':' . $this->escapeHtmlAttr($value)?>"/> + <input type="hidden" name="hiddenFilters[]" value="<?=$this->escapeHtmlAttr($key) . ':' . $this->escapeHtmlAttr($value)?>" /> <?php endforeach; ?> <?php endforeach; ?> <?php - /* Show hidden field for active search class when in combined handler mode. */ - if ($this->searchbox()->combinedHandlersActive()) { - echo '<input type="hidden" name="activeSearchClassId" value="' . $this->escapeHtmlAttr($this->searchClassId) . '" />'; - } - /* Load hidden limit preference from Session */ - if (!empty($lastLimit)) { - echo '<input type="hidden" name="limit" value="' . $this->escapeHtmlAttr($lastLimit) . '" />'; - } - if (!empty($lastSort)) { - echo '<input type="hidden" name="sort" value="' . $this->escapeHtmlAttr($lastSort) . '" />'; - } + /* Show hidden field for active search class when in combined handler mode. */ + if ($this->searchbox()->combinedHandlersActive()) { + echo '<input type="hidden" name="activeSearchClassId" value="' . $this->escapeHtmlAttr($this->searchClassId) . '" />'; + } + /* Load hidden limit preference from Session */ + if (!empty($lastLimit)) { + echo '<input type="hidden" name="limit" value="' . $this->escapeHtmlAttr($lastLimit) . '" />'; + } + if (!empty($lastSort)) { + echo '<input type="hidden" name="sort" value="' . $this->escapeHtmlAttr($lastSort) . '" />'; + } ?> + + <?php /* finc: Don't load active filters here but above search results */ /* + <?=$this->context($this)->renderInContext( + 'search/filters.phtml', + [ + 'params' => $params ?? null, + 'urlQuery' => isset($results) ? $results->getUrlQuery() : null, + 'filterList' => $showFilters ? $filterList : [], + 'checkboxFilters' => $showFilters ? $checkboxFilters : [], + 'searchClassId' => $this->searchClassId, + 'searchType' => $this->searchType, + ] + );?> + + */ ?> </form> <?php endif; ?> <!-- finc: search - searchbox - END --> diff --git a/themes/finc/theme.config.php b/themes/finc/theme.config.php index 3ef3ecf92de34db3394a590422ae5dcfa8c41f7f..1fc2b5728fcd8a38dce3c4d0daea207e71e1b432 100644 --- a/themes/finc/theme.config.php +++ b/themes/finc/theme.config.php @@ -22,21 +22,21 @@ return [ ], 'factories' => [ 'finc\View\Helper\Root\BranchInfo' => - 'finc\View\Helper\Root\Factory::getBranchInfo', + 'finc\View\Helper\Root\BranchInfoViewHelperFactory', 'finc\View\Helper\Root\ExternalCatalogueLink' => - 'finc\View\Helper\Root\Factory::getExternalCatalogueLink', + 'finc\View\Helper\Root\ExternalCatalogueLinkHelperFactory', 'finc\View\Helper\Root\InterlibraryLoanLink' => - 'finc\View\Helper\Root\Factory::getInterlibraryLoanLink', + 'finc\View\Helper\Root\ViewHelperFactory', 'finc\View\Helper\Root\SideFacet' => - 'finc\View\Helper\Root\Factory::getSideFacet', + 'finc\View\Helper\Root\SideFacetViewHelperFactory', 'finc\View\Helper\Root\Record' => - 'finc\View\Helper\Root\Factory::getRecord', + 'finc\View\Helper\Root\RecordViewHelperFactory', 'finc\View\Helper\Root\RecordLink' => - 'finc\View\Helper\Root\Factory::getRecordLink', + 'finc\View\Helper\Root\RecordLinkViewHelperFactory', 'VuFind\View\Helper\Root\Citation' => - 'finc\View\Helper\Root\Factory::getCitation', + 'finc\View\Helper\Root\CitationViewHelperFactory', 'VuFind\View\Helper\Root\OpenUrl' => - 'finc\View\Helper\Root\Factory::getOpenUrl', + 'finc\View\Helper\Root\OpenUrlViewHelperFactory', 'VuFind\View\Helper\Root\RecordDataFormatter' => 'finc\View\Helper\Root\RecordDataFormatterFactory', 'finc\View\Helper\Root\ResultFeed' => @@ -44,9 +44,9 @@ return [ 'finc\View\Helper\Root\Flashmessages' => 'VuFind\View\Helper\Root\FlashmessagesFactory', 'finc\View\Helper\Root\ExternalLink' => - 'finc\View\Helper\Root\Factory::getExternalLink', + \finc\View\Helper\Root\ViewHelperFactory::class, 'finc\View\Helper\Root\RenderArray' => - 'finc\View\Helper\Root\Factory::getRenderArray', + \finc\View\Helper\Root\ViewHelperFactory::class ] ] ]; diff --git a/themes/root/templates/Email/form.phtml b/themes/root/templates/Email/form.phtml index 2075c6dbfd383d223fcfc9a7aabe1f58d0ea35ee..8df740fbbf52871737d005c3faa13d822c6d3f61 100644 --- a/themes/root/templates/Email/form.phtml +++ b/themes/root/templates/Email/form.phtml @@ -1,12 +1,13 @@ -<?php foreach($this->fields as $key => $data): ?> +<?php foreach($this->fields as $data): ?> +<?php $label = $data['label'] ?? null ?> <?php $isTextarea = $data['type'] === 'textarea'; ?> <?php if ($isTextarea): ?> ----- -<?=$key?>: +<?=$label ? "$label: " : ''?> <?=$data['value']?> ----- <?php else: ?> -<?= "$key: " . $data['value'] . PHP_EOL ?> +<?= ($label ? "$label: " : '') . $data['value'] . PHP_EOL ?> <?php endif ?> <?php endforeach ?> diff --git a/themes/root/templates/Email/login-link.phtml b/themes/root/templates/Email/login-link.phtml new file mode 100644 index 0000000000000000000000000000000000000000..83cc607865166e2c6ef0e50efc1b13439c4f9f6c --- /dev/null +++ b/themes/root/templates/Email/login-link.phtml @@ -0,0 +1,8 @@ +<?php // This is a text-only email template; do not include HTML! ?> +<?=$this->translate('email_login_requested', ['%%title%%' => $this->title])?> + + +<?=$this->translate('email_login_desc')?> + + +<?=$this->translate('email_login_link', ['%%url%%' => $this->url])?> diff --git a/themes/root/templates/Email/notify-email-change.phtml b/themes/root/templates/Email/notify-email-change.phtml new file mode 100644 index 0000000000000000000000000000000000000000..6ed60aaf2b49fe210e8793176a968d45a74c7290 --- /dev/null +++ b/themes/root/templates/Email/notify-email-change.phtml @@ -0,0 +1,4 @@ +<?=$this->translate( + 'change_notification_email_message', + ['%%library%%' => $this->library, '%%url%%' => $this->url, '%%email%%' => $this->email]) +?> diff --git a/themes/root/templates/Email/record-sms.phtml b/themes/root/templates/Email/record-sms.phtml index 268c4a5cd5e0ad8539c336c54bbc863759b26f79..71cb8a8a7815526f827b4f5bd3c1b80645924b86 100644 --- a/themes/root/templates/Email/record-sms.phtml +++ b/themes/root/templates/Email/record-sms.phtml @@ -6,6 +6,9 @@ // we'll just show URL and title. This prioritization is important, // since text messages can be short, and we want the most important stuff // at the top! + $recordUrl = $this->shortenUrl( + $this->serverUrl($this->recordLink()->getUrl($this->driver)) + ); if ($this->driver->supportsAjaxStatus()) { $holdings = $this->driver->getRealTimeHoldings(); @@ -46,9 +49,9 @@ echo $this->translate('Location') . ': ' . trim($location) . "\n"; } echo $this->driver->getBreadcrumb() . "\n"; - echo $this->serverUrl($this->recordLink()->getUrl($this->driver)) . "\n"; + echo $recordUrl . "\n"; } else { - echo $this->serverUrl($this->recordLink()->getUrl($this->driver)) . "\n"; + echo $recordUrl . "\n"; echo $this->driver->getBreadcrumb() . "\n"; } ?> diff --git a/themes/root/templates/Email/scheduled-alert.phtml b/themes/root/templates/Email/scheduled-alert.phtml new file mode 100644 index 0000000000000000000000000000000000000000..04393fa4055d6aace746d7b3416c779be9329d27 --- /dev/null +++ b/themes/root/templates/Email/scheduled-alert.phtml @@ -0,0 +1,34 @@ +<?=$this->translate('New results found for search') ?>: + <?=empty($info['description']) ? $this->translate('history_empty_search') : $this->escapeHtml($info['description'])?> + +<?php if (count($info['checkboxFilters']) || count($info['filters'])): ?> + +<?=$this->translate('history_limits') ?>: +<?php foreach ($info['checkboxFilters'] as $checkbox): ?> + <?=$this->translate($checkbox['desc']) ?> +<?php endforeach; ?> + +<?php foreach ($info['filters'] as $format => $filters): ?> + <?=$this->translate($format) . ': ' ?> +<?php $index = 0; ?> +<?php foreach ($filters as $f): ?> +<?=(($f['operator'] == 'NOT' || $index++ > 0) ? ($index > 0 ? ' ' : '') . $this->translate($f['operator']) . ' ' : '') . $f['displayText']; ?> +<?php endforeach; ?> + +<?php endforeach; ?> +<?php endif; ?> + +<?=$this->translate('Link to full results') ?>: +<?=$info['url'] ?> + + + +<?=$this->translate('new_results_heading', ['%%count%%' => $info['recordCount']]) ?>: + +<?php foreach ($records as $result): ?> + <?=$this->record($result)->getSearchResult('email') . PHP_EOL; ?> +<?php endforeach; ?> + + +<?=$this->translate('unsubscribe_description') ?>: +<?=$info['unsubscribeUrl'] ?> diff --git a/themes/root/templates/Email/share-link.phtml b/themes/root/templates/Email/share-link.phtml index bcfca31ed209b944c6962da8e891dbe0314f48ef..80839e3cf4ba6239e2a4914820053cec8b1954b4 100644 --- a/themes/root/templates/Email/share-link.phtml +++ b/themes/root/templates/Email/share-link.phtml @@ -7,7 +7,7 @@ <?php endif; ?> - <?=$this->translate('email_link')?>: <<?=$this->msgUrl?>> + <?=$this->translate('email_link')?>: <<?=$this->shortenUrl($this->msgUrl)?>> ------------------------------------------------------------ diff --git a/themes/root/templates/Email/verify-email.phtml b/themes/root/templates/Email/verify-email.phtml new file mode 100644 index 0000000000000000000000000000000000000000..817217761013a60fcd5aba1fa43bc19d55d99c43 --- /dev/null +++ b/themes/root/templates/Email/verify-email.phtml @@ -0,0 +1,3 @@ +<?=$this->translate('verification_email_notification', ['%%library%%' => $this->library]) ?> + +<?=$this->translate('verification_email_url_pretext', ['%%url%%' => $this->url]) ?> diff --git a/themes/root/templates/HelpTranslations/pt-br/advsearch.phtml b/themes/root/templates/HelpTranslations/pt-br/advsearch.phtml index 8fab823b349b9d3b72ae75e6dc783a91d4a469ce..4cac351c8ca2ce85f25769236c06b4764b8fdc05 100644 --- a/themes/root/templates/HelpTranslations/pt-br/advsearch.phtml +++ b/themes/root/templates/HelpTranslations/pt-br/advsearch.phtml @@ -8,35 +8,35 @@ <dl class="Content"> <dt><a name="Search Fields"></a>Campos de Busca</dt> <dd> - <p>A página de Busca Avançada apresenta a vários campos de busca. - Em cada campo pode-se digitar termos, a qual deseja buscar itens, relacioando-os a determinado metadado, - podendo fazer uso de <href="<?=$this->url('help-home')?>?topic=search">operadores de busca</a> + <p>A página de Busca Avançada apresenta a vários campos de busca. + Em cada campo pode-se digitar termos, a qual deseja buscar itens, relacioando-os a determinado metadado, + podendo fazer uso de <a href="<?=$this->url('help-home')?>?topic=search">operadores de busca</a> </p> - <p>Pode-se selecionar o tipo de metadado (tÃtulo, autor, etc.) para cada campo de busca, - tornando a busca mais precisa. + <p>Pode-se selecionar o tipo de metadado (tÃtulo, autor, etc.) para cada campo de busca, + tornando a busca mais precisa. </p> <p>Da mesma forma, pode-se combimar mais que um campo de busca.</p> - + <ul> <li>Todos os termos - Retorna somente os itens que possuam todos os termos inseridos nos campos.</li> <li>Algum termo - Retorna os itens que possuam pelo menos um dos termos inseridos nos campos.</li> <li>Sem os termos - Retorna todos os intens, exceto os que possuem os termos inseridos no campo.</li> </ul> - <p>O Botão "Adicionar campo de busca" pode ser usado para adicionar campos de busca extras ao formulário. + <p>O Botão "Adicionar campo de busca" pode ser usado para adicionar campos de busca extras ao formulário. Pode-se adicionar quantos campos forem necessários.</p> </dd> - + <dt><a name="Search Groups"></a>Grupos de Busca</dt> <dd> - <p>Para certas buscas complexas, um conjunto simples de campos de busca podem não ser o bastante. - Por exemplo, Suponhe que deseje encontrar os itens sobre a história de São Paulo ou Rio de Janeiro. - Caso use a opção "Todos os termos" recuperará itens que contenham história de São Paulo E Rio de Janeiro. - Caso use a opção "Algum termo" recuperará itens que não contem nada de São Paulo ou Rio de Janeiro. + <p>Para certas buscas complexas, um conjunto simples de campos de busca podem não ser o bastante. + Por exemplo, Suponhe que deseje encontrar os itens sobre a história de São Paulo ou Rio de Janeiro. + Caso use a opção "Todos os termos" recuperará itens que contenham história de São Paulo E Rio de Janeiro. + Caso use a opção "Algum termo" recuperará itens que não contem nada de São Paulo ou Rio de Janeiro. </p> - <p>Grupos de busca prove um meio de construir buscas agrupando campos de busca. - Cada vez que pressionar o botão "Adicionar Grupo de Busca" um novo grupo de campos será adicionado. - Uma vez que tenha múltiplos grupos grupos de busca, pode-se remover grupos indesejados utilizando o botão - "Retirar o grupo de busca". Pode-se especificar se deseja busca em todos os grupos ou em alguns dos grupos. + <p>Grupos de busca prove um meio de construir buscas agrupando campos de busca. + Cada vez que pressionar o botão "Adicionar Grupo de Busca" um novo grupo de campos será adicionado. + Uma vez que tenha múltiplos grupos grupos de busca, pode-se remover grupos indesejados utilizando o botão + "Retirar o grupo de busca". Pode-se especificar se deseja busca em todos os grupos ou em alguns dos grupos. </p> <p>No exemplo anterior, pode-se resolver com a seguinte estratégia de busca, utilizanod os grupos de busca:</p> <ul> diff --git a/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml b/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml index bb3f3ef6c6dcf1b7fce670817fc3ef41aad70a0d..26693164e15c6226bf8b11a72d038f7451a1b8f5 100644 --- a/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml +++ b/themes/root/templates/RecordDriver/AbstractBase/export-ris.phtml @@ -157,6 +157,10 @@ foreach ($this->record($this->driver)->getUrlList() as $url) { echo 'UR - ' . "$url\r\n"; } +if ($doi = $this->driver->tryMethod('getCleanDOI')) { + echo "DO - $doi\r\n"; +} + // End of Record: echo "ER -\r\n\r\n"; ?> diff --git a/themes/root/templates/RecordDriver/DefaultRecord/result-email.phtml b/themes/root/templates/RecordDriver/DefaultRecord/result-email.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d07ae290927ee3b8cea3f77f6f1e9b9e74a1fbfd --- /dev/null +++ b/themes/root/templates/RecordDriver/DefaultRecord/result-email.phtml @@ -0,0 +1,46 @@ +<?php + $authors = $this->driver->getPrimaryAuthors(); + $title = $this->driver->getTitle(); + $journalTitle = $this->driver->getContainerTitle(); + $publicationDate = $this->driver->getPublicationDates(); + $isbn = $this->driver->tryMethod('getISBNs'); + $issn = $this->driver->tryMethod('getISSNs'); + $collection = $this->driver->getContainingCollections(); + $containerTitle = $this->driver->getContainerTitle(); + $containerReference = $this->driver->getContainerReference(); + $formats = $this->driver->getFormats(); + $format = end($formats); + $url = $this->recordLink()->getUrl($this->driver); + // NOTE: use $info['baseUrl'] instead of serverUrl view helper when this template is + // rendered by scheduled alerts console action. + $url = isset($info['baseUrl']) ? ($info['baseUrl'] . $url) : $this->serverUrl($url); +?> + +<?= $this->translate('Title'); ?>: <?= !empty($title) ? $title : $this->translate('Title not available'); ?> +<?php if (!empty($authors)): ?> +<?= PHP_EOL . $this->translate('by') . ': ' . $authors[0]; ?><?php if (count($authors) > 1): ?>, <?=$this->transEsc('more_authors_abbrev')?><?php endif; ?> +<?php endif; ?> +<?php if (!empty($publicationDate)): ?> +<?= PHP_EOL . $this->translate('Published') . ': ' . $publicationDate[0]; ?> +<?php endif; ?> +<?php if (!empty($journalTitle)): ?> +<?= PHP_EOL . $this->translate('Published in') . ": $journalTitle"; ?> +<?php endif; ?> +<?php if (!empty($isbn)): ?> +<?= PHP_EOL . $this->translate('ISBN') . ': ' . implode(', ', $isbn); ?> +<?php endif; ?> +<?php if (!empty($issn)): ?> +<?= PHP_EOL . $this->translate('ISSN') . ': ' . implode(', ', $issn); ?> +<?php endif; ?> +<?php if (!empty($collection)): ?> +<?php foreach ($collection as $collId => $collText): ?> +<?= PHP_EOL . $this->translate("in_collection_label") . ' ' . $collText; ?> +<?php endforeach; ?> +<?php elseif (!empty($containerTitle)): ?> +<?= PHP_EOL . $this->translate('component_is_part_of') . ": $containerTitle" . !empty($containerReference) ? " $containerReference" : '' ?> +<?php endif; ?> +<?php if (!empty($format)): ?> +<?= PHP_EOL . $this->translate($format) ?> +<?php endif; ?> + +<?= $this->translate('View Full Record') . ": $url"; ?> \ No newline at end of file diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index 0f2215e799b60b4cc73c83bd9f2037e3e9841463..1818208fc95cf33dfff4cecc06c9bac7a1f459cb 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -30,8 +30,11 @@ return [ 'VuFind\View\Helper\Root\Ils' => 'VuFind\View\Helper\Root\IlsFactory', 'VuFind\View\Helper\Root\JsTranslations' => 'VuFind\View\Helper\Root\JsTranslationsFactory', 'VuFind\View\Helper\Root\KeepAlive' => 'VuFind\View\Helper\Root\KeepAliveFactory', + 'VuFind\View\Helper\Root\Linkify' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\View\Helper\Root\LocalizedNumber' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Metadata' => 'VuFind\View\Helper\Root\MetadataFactory', 'VuFind\View\Helper\Root\OpenUrl' => 'VuFind\View\Helper\Root\OpenUrlFactory', + 'VuFind\View\Helper\Root\Overdrive' => 'VuFind\View\Helper\Root\OverdriveFactory', 'VuFind\View\Helper\Root\Permission' => 'VuFind\View\Helper\Root\PermissionFactory', 'VuFind\View\Helper\Root\Piwik' => 'VuFind\View\Helper\Root\PiwikFactory', 'VuFind\View\Helper\Root\Printms' => 'Zend\ServiceManager\Factory\InvokableFactory', @@ -51,16 +54,20 @@ return [ 'VuFind\View\Helper\Root\SearchOptions' => 'VuFind\View\Helper\Root\SearchOptionsFactory', 'VuFind\View\Helper\Root\SearchParams' => 'VuFind\View\Helper\Root\SearchParamsFactory', 'VuFind\View\Helper\Root\SearchTabs' => 'VuFind\View\Helper\Root\SearchTabsFactory', + 'VuFind\View\Helper\Root\ShortenUrl' => 'VuFind\View\Helper\Root\ShortenUrlFactory', 'VuFind\View\Helper\Root\SortFacetList' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\View\Helper\Root\Summaries' => 'VuFind\View\Helper\Root\ContentLoaderFactory', 'VuFind\View\Helper\Root\Summon' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\View\Helper\Root\SyndeticsPlus' => 'VuFind\View\Helper\Root\SyndeticsPlusFactory', 'VuFind\View\Helper\Root\SystemEmail' => 'VuFind\View\Helper\Root\SystemEmailFactory', 'VuFind\View\Helper\Root\TransEsc' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\TransEscWithPrefix' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\View\Helper\Root\Translate' => 'Zend\ServiceManager\Factory\InvokableFactory', 'VuFind\View\Helper\Root\Truncate' => 'Zend\ServiceManager\Factory\InvokableFactory', + 'VuFind\View\Helper\Root\Url' => 'VuFind\View\Helper\Root\UrlFactory', 'VuFind\View\Helper\Root\UserList' => 'VuFind\View\Helper\Root\UserListFactory', 'VuFind\View\Helper\Root\UserTags' => 'VuFind\View\Helper\Root\UserTagsFactory', + 'Zend\View\Helper\ServerUrl' => 'VuFind\View\Helper\Root\ServerUrlFactory', ], 'aliases' => [ 'accountCapabilities' => 'VuFind\View\Helper\Root\AccountCapabilities', @@ -90,8 +97,11 @@ return [ 'ils' => 'VuFind\View\Helper\Root\Ils', 'jsTranslations' => 'VuFind\View\Helper\Root\JsTranslations', 'keepAlive' => 'VuFind\View\Helper\Root\KeepAlive', + 'linkify' => 'VuFind\View\Helper\Root\Linkify', 'localizedNumber' => 'VuFind\View\Helper\Root\LocalizedNumber', + 'metadata' => 'VuFind\View\Helper\Root\Metadata', 'openUrl' => 'VuFind\View\Helper\Root\OpenUrl', + 'overdrive' => 'VuFind\View\Helper\Root\Overdrive', 'permission' => 'VuFind\View\Helper\Root\Permission', 'piwik' => 'VuFind\View\Helper\Root\Piwik', 'printms' => 'VuFind\View\Helper\Root\Printms', @@ -111,16 +121,19 @@ return [ 'searchParams' => 'VuFind\View\Helper\Root\SearchParams', 'searchTabs' => 'VuFind\View\Helper\Root\SearchTabs', 'searchbox' => 'VuFind\View\Helper\Root\SearchBox', + 'shortenUrl' => 'VuFind\View\Helper\Root\ShortenUrl', 'sortFacetList' => 'VuFind\View\Helper\Root\SortFacetList', 'summaries' => 'VuFind\View\Helper\Root\Summaries', 'summon' => 'VuFind\View\Helper\Root\Summon', 'syndeticsPlus' => 'VuFind\View\Helper\Root\SyndeticsPlus', 'systemEmail' => 'VuFind\View\Helper\Root\SystemEmail', 'transEsc' => 'VuFind\View\Helper\Root\TransEsc', + 'transEscWithPrefix' => 'VuFind\View\Helper\Root\TransEscWithPrefix', 'translate' => 'VuFind\View\Helper\Root\Translate', 'truncate' => 'VuFind\View\Helper\Root\Truncate', 'userlist' => 'VuFind\View\Helper\Root\UserList', 'usertags' => 'VuFind\View\Helper\Root\UserTags', + 'Zend\View\Helper\Url' => 'VuFind\View\Helper\Root\Url', ] ], ]; diff --git a/themes/sandal/less/colors.less b/themes/sandal/less/colors.less index 125691d93ec92360dee0c540050b4277f0faf6c1..ee99e14ab793c690127de3739b66cedbdb762d9c 100644 --- a/themes/sandal/less/colors.less +++ b/themes/sandal/less/colors.less @@ -21,7 +21,7 @@ @sidebar-title-bg: @white; @sidebar-title-color: @near-black; -@sidebar-title-icon-color: @light-gray; +@sidebar-title-icon-color: @light-silver; @sidebar-item-bg: @white; @sidebar-item-color: @black; @sidebar-item-hover-bg: @lightest-blue; diff --git a/themes/sandal/less/search.less b/themes/sandal/less/search.less index 42dc30cc666894335bc7e0e0ecbd7d8bf9efce35..8655e718720311b20780973c07acbda132d528bd 100644 --- a/themes/sandal/less/search.less +++ b/themes/sandal/less/search.less @@ -4,12 +4,13 @@ } .list-group-item.title { color: @dark-gray; } .bulkActionButtons { + margin-bottom: 0; label { font-weight: 500; color: @mid-gray; } } -.search-header.search-header { padding-top: 0; } // Duplicate to increase specificity +.search-header.search-header { padding: 0; } // Duplicate to increase specificity .search-stats > h2 { margin: 0; } // No results padding .author-facets { font-size: @reduced-font-size; } @@ -266,3 +267,55 @@ width: 100%; } } + +/* ------ CURRENT FILTERS ------ */ +.active-filters { + button { + background: @list-group-active-bg; + } + .dropdown { + .filters-term { + color: #000; + } + a:hover { + background-color: transparent; + } + } +} + +.reset-filters-btn { + font-weight: bold; + color: #fff; + background-color: rgba(255,255,255,.1); + border-radius: 4px; +} +.active-filters .reset-filters-btn:hover { + color: #fff; + background-color: rgba(255,255,255,.2); +} +.active-filters .filters .filter-value, +.search-filter-dropdown .btn, +.search-filter-dropdown .btn:focus { + color: @dark-gray; + background-color: @header-light; +} +.active-filters .filters .filter-value { + border-radius: 2px; +} +.active-filters .search-filter-remove::after { + content: "\f00d"; /* fa-close */ + color: @mid-gray; +} +.search-filter-dropdown .btn { + box-shadow: 0 3px 3px rgba(0,0,0,.3); + + &::after { + display: inline-block; + width: 1rem; + margin-right: -0.5rem; + } +} +.search-filter-dropdown .btn:active { + box-shadow: none; + background-color: @light-silver; +} diff --git a/themes/sandal/scss/search.scss b/themes/sandal/scss/search.scss index 04af0a1a57829de5c5ae69f65d765673494c780e..b865e81269a24f54668c3052377f8e096ca538d5 100644 --- a/themes/sandal/scss/search.scss +++ b/themes/sandal/scss/search.scss @@ -4,12 +4,13 @@ } .list-group-item.title { color: $dark-gray; } .bulkActionButtons { + margin-bottom: 0; label { font-weight: 500; color: $mid-gray; } } -.search-header.search-header { padding-top: 0; } // Duplicate to increase specificity +.search-header.search-header { padding: 0; } // Duplicate to increase specificity .search-stats > h2 { margin: 0; } // No results padding .author-facets { font-size: $reduced-font-size; } @@ -266,3 +267,55 @@ width: 100%; } } + +/* ------ CURRENT FILTERS ------ */ +.active-filters { + button { + background: $list-group-active-bg; + } + .dropdown { + .filters-term { + color: #000; + } + a:hover { + background-color: transparent; + } + } +} + +.reset-filters-btn { + font-weight: bold; + color: #fff; + background-color: rgba(255,255,255,.1); + border-radius: 4px; +} +.active-filters .reset-filters-btn:hover { + color: #fff; + background-color: rgba(255,255,255,.2); +} +.active-filters .filters .filter-value, +.search-filter-dropdown .btn, +.search-filter-dropdown .btn:focus { + color: $dark-gray; + background-color: $header-light; +} +.active-filters .filters .filter-value { + border-radius: 2px; +} +.active-filters .search-filter-remove::after { + content: "\f00d"; /* fa-close */ + color: $mid-gray; +} +.search-filter-dropdown .btn { + box-shadow: 0 3px 3px rgba(0,0,0,.3); + + &::after { + display: inline-block; + width: 1rem; + margin-right: -0.5rem; + } +} +.search-filter-dropdown .btn:active { + box-shadow: none; + background-color: $light-silver; +}