diff --git a/composer.json b/composer.json index fc09866766b4c50791f269d042c3afea2316408d..be2debc55591d53ee4d976b7b485e62ec80c2a05 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,6 @@ "laminas/laminas-captcha": "2.9.0", "laminas/laminas-code": "3.4.1", "laminas/laminas-config": "3.3.0", - "laminas/laminas-console": "2.8.0", "laminas/laminas-crypt": "3.3.1", "laminas/laminas-db": "2.11.2", "laminas/laminas-dependency-plugin": "^1.0", @@ -41,7 +40,6 @@ "laminas/laminas-mail": "2.10.0", "laminas/laminas-modulemanager": "2.8.4", "laminas/laminas-mvc": "3.1.1", - "laminas/laminas-mvc-console": "1.2.0", "laminas/laminas-mvc-i18n": "1.1.1", "laminas/laminas-mvc-plugin-flashmessenger": "1.2.0", "laminas/laminas-paginator": "2.8.2", @@ -65,11 +63,12 @@ "phing/phing": "2.16.2", "ppito/laminas-whoops": "2.0.0", "serialssolutions/summon": "1.3.0", + "symfony/console": "4.4.4", "symfony/yaml": "3.4.36", "swagger-api/swagger-ui": "3.25.0", "vufind-org/vufindcode": "1.2", "vufind-org/vufinddate": "1.0.0", - "vufind-org/vufindharvest": "3.0.0", + "vufind-org/vufindharvest": "4.0.1", "vufind-org/vufindhttp": "3.0.0", "wikimedia/composer-merge-plugin": "1.4.1", "yajra/laravel-pdo-via-oci8": "2.1.1", diff --git a/composer.lock b/composer.lock index c19db06244119db55ddff1f7bbd1407a5330d640..9a6a68a36ad9f028c1116f25526a82d80cfc3981 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": "8b23a38fc87e962f97cdf4c957610846", + "content-hash": "f1e891ccb58b8552a078f9b99dbbf290", "packages": [ { "name": "ahand/mobileesp", @@ -438,16 +438,16 @@ }, { "name": "khanamiryan/qrcode-detector-decoder", - "version": "1.0.2", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/khanamiryan/php-qrcode-detector-decoder.git", - "reference": "a75482d3bc804e3f6702332bfda6cccbb0dfaa76" + "reference": "89b57f2d9939dd57394b83f6ccbd3e1a74659e34" }, "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/89b57f2d9939dd57394b83f6ccbd3e1a74659e34", + "reference": "89b57f2d9939dd57394b83f6ccbd3e1a74659e34", "shasum": "" }, "require": { @@ -484,7 +484,7 @@ "qr", "zxing" ], - "time": "2018-04-26T11:41:33+00:00" + "time": "2020-04-19T16:18:51+00:00" }, { "name": "laminas/laminas-cache", @@ -1916,16 +1916,16 @@ }, { "name": "laminas/laminas-mime", - "version": "2.7.2", + "version": "2.7.4", "source": { "type": "git", "url": "https://github.com/laminas/laminas-mime.git", - "reference": "2dbace2c69542e5a251af3becb6d7209ac9fb42b" + "reference": "e45a7d856bf7b4a7b5bd00d6371f9961dc233add" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/2dbace2c69542e5a251af3becb6d7209ac9fb42b", - "reference": "2dbace2c69542e5a251af3becb6d7209ac9fb42b", + "url": "https://api.github.com/repos/laminas/laminas-mime/zipball/e45a7d856bf7b4a7b5bd00d6371f9961dc233add", + "reference": "e45a7d856bf7b4a7b5bd00d6371f9961dc233add", "shasum": "" }, "require": { @@ -1934,12 +1934,12 @@ "php": "^5.6 || ^7.0" }, "replace": { - "zendframework/zend-mime": "self.version" + "zendframework/zend-mime": "^2.7.2" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", "laminas/laminas-mail": "^2.6", - "phpunit/phpunit": "^5.7.21 || ^6.3" + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20" }, "suggest": { "laminas/laminas-mail": "Laminas\\Mail component" @@ -1966,7 +1966,7 @@ "laminas", "mime" ], - "time": "2019-12-31T17:25:27+00:00" + "time": "2020-03-29T13:12:07+00:00" }, { "name": "laminas/laminas-modulemanager", @@ -2107,76 +2107,6 @@ ], "time": "2019-12-31T17:33:14+00:00" }, - { - "name": "laminas/laminas-mvc-console", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-mvc-console.git", - "reference": "0c16223557fdb9bba853f6de22e1040824c1c966" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-mvc-console/zipball/0c16223557fdb9bba853f6de22e1040824c1c966", - "reference": "0c16223557fdb9bba853f6de22e1040824c1c966", - "shasum": "" - }, - "require": { - "container-interop/container-interop": "^1.1", - "laminas/laminas-console": "^2.6", - "laminas/laminas-eventmanager": "^2.6.2 || ^3.0", - "laminas/laminas-modulemanager": "^2.7.1", - "laminas/laminas-mvc": "^3.0.3", - "laminas/laminas-router": "^3.0", - "laminas/laminas-servicemanager": "^2.7.5 || ^3.0.3", - "laminas/laminas-stdlib": "^2.7.5 || ^3.0", - "laminas/laminas-text": "^2.6", - "laminas/laminas-view": "^2.6.3", - "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^5.6 || ^7.0" - }, - "conflict": { - "laminas/laminas-mvc": "<3.0.0" - }, - "replace": { - "zendframework/zend-mvc-console": "self.version" - }, - "require-dev": { - "laminas/laminas-coding-standard": "~1.0.0", - "laminas/laminas-filter": "^2.6.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.4" - }, - "suggest": { - "laminas/laminas-filter": "^2.6.1, to filter rendered results" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev", - "dev-develop": "1.3.x-dev" - }, - "laminas": { - "component": "Laminas\\Mvc\\Console" - } - }, - "autoload": { - "psr-4": { - "Laminas\\Mvc\\Console\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Integration between laminas-mvc and laminas-console", - "homepage": "https://laminas.dev", - "keywords": [ - "console", - "laminas", - "mvc" - ], - "time": "2019-12-31T17:33:37+00:00" - }, { "name": "laminas/laminas-mvc-i18n", "version": "1.1.1", @@ -2433,16 +2363,16 @@ }, { "name": "laminas/laminas-router", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/laminas/laminas-router.git", - "reference": "c94f13f39dfbc4313efdbfcd9772487b4b009026" + "reference": "01a6905202ad41a42ba63d60260eba32b89e18c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-router/zipball/c94f13f39dfbc4313efdbfcd9772487b4b009026", - "reference": "c94f13f39dfbc4313efdbfcd9772487b4b009026", + "url": "https://api.github.com/repos/laminas/laminas-router/zipball/01a6905202ad41a42ba63d60260eba32b89e18c7", + "reference": "01a6905202ad41a42ba63d60260eba32b89e18c7", "shasum": "" }, "require": { @@ -2457,7 +2387,7 @@ "laminas/laminas-mvc": "<3.0.0" }, "replace": { - "zendframework/zend-router": "self.version" + "zendframework/zend-router": "^3.3.0" }, "require-dev": { "laminas/laminas-coding-standard": "~1.0.0", @@ -2494,7 +2424,7 @@ "mvc", "routing" ], - "time": "2020-01-03T17:19:34+00:00" + "time": "2020-03-29T13:21:03+00:00" }, { "name": "laminas/laminas-serializer", @@ -3183,16 +3113,16 @@ }, { "name": "laminas/laminas-zendframework-bridge", - "version": "1.0.1", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d" + "reference": "bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/0fb9675b84a1666ab45182b6c5b29956921e818d", - "reference": "0fb9675b84a1666ab45182b6c5b29956921e818d", + "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9", + "reference": "bfbbdb6c998d50dbf69d2187cb78a5f1fa36e1e9", "shasum": "" }, "require": { @@ -3231,7 +3161,7 @@ "laminas", "zf" ], - "time": "2020-01-07T22:58:31+00:00" + "time": "2020-04-03T16:01:00+00:00" }, { "name": "league/commonmark", @@ -4198,12 +4128,12 @@ "source": { "type": "git", "url": "https://github.com/pear/Validate_ISPN.git", - "reference": "9ea9312a0841b5d745742c737772aeffa6d06e96" + "reference": "9b06777cc7b6fea4d6d5c9469f21c4b029d58ab5" }, "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/9b06777cc7b6fea4d6d5c9469f21c4b029d58ab5", + "reference": "9b06777cc7b6fea4d6d5c9469f21c4b029d58ab5", "shasum": "" }, "require": { @@ -4238,7 +4168,7 @@ } ], "description": "More info available on: http://pear.php.net/package/Validate_ISPN", - "time": "2015-04-14T04:17:31+00:00" + "time": "2020-04-19T19:25:23+00:00" }, { "name": "phing/phing", @@ -4477,16 +4407,16 @@ }, { "name": "psr/log", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", - "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", "shasum": "" }, "require": { @@ -4520,7 +4450,7 @@ "psr", "psr-3" ], - "time": "2019-11-01T11:05:21+00:00" + "time": "2020-03-23T09:12:05+00:00" }, { "name": "psr/simple-cache", @@ -4660,18 +4590,94 @@ ], "time": "2020-01-17T21:39:28+00:00" }, + { + "name": "symfony/console", + "version": "v4.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "f512001679f37e6a042b51897ed24a2f05eba656" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/f512001679f37e6a042b51897ed24a2f05eba656", + "reference": "f512001679f37e6a042b51897ed24a2f05eba656", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2020-01-25T12:44:29+00:00" + }, { "name": "symfony/inflector", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/inflector.git", - "reference": "f419ab2853cc00471ffd7fc18e544b5f5a90adb1" + "reference": "53cfa47fe9142f39b5605df67bada3893dd4f46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/inflector/zipball/f419ab2853cc00471ffd7fc18e544b5f5a90adb1", - "reference": "f419ab2853cc00471ffd7fc18e544b5f5a90adb1", + "url": "https://api.github.com/repos/symfony/inflector/zipball/53cfa47fe9142f39b5605df67bada3893dd4f46c", + "reference": "53cfa47fe9142f39b5605df67bada3893dd4f46c", "shasum": "" }, "require": { @@ -4716,20 +4722,34 @@ "symfony", "words" ], - "time": "2020-01-04T13:00:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/options-resolver", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0" + "reference": "ade3d89dd3b875b83c8cff2980c9bb0daf6ef297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", - "reference": "9a02d6662660fe7bfadad63b5f0b0718d4c8b6b0", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/ade3d89dd3b875b83c8cff2980c9bb0daf6ef297", + "reference": "ade3d89dd3b875b83c8cff2980c9bb0daf6ef297", "shasum": "" }, "require": { @@ -4770,20 +4790,34 @@ "configuration", "options" ], - "time": "2020-01-04T13:00:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-06T10:16:26+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", - "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", "shasum": "" }, "require": { @@ -4795,7 +4829,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4828,20 +4862,20 @@ "polyfill", "portable" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", - "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", "shasum": "" }, "require": { @@ -4853,7 +4887,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -4887,20 +4921,78 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-03-09T19:04:49+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.15.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/property-access", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "090b4bc92ded1ec512f7e2ed1691210769dffdb3" + "reference": "f6a51bd76a3a5c36c57221a4f491b9cf02663672" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/090b4bc92ded1ec512f7e2ed1691210769dffdb3", - "reference": "090b4bc92ded1ec512f7e2ed1691210769dffdb3", + "url": "https://api.github.com/repos/symfony/property-access/zipball/f6a51bd76a3a5c36c57221a4f491b9cf02663672", + "reference": "f6a51bd76a3a5c36c57221a4f491b9cf02663672", "shasum": "" }, "require": { @@ -4954,7 +5046,79 @@ "property path", "reflection" ], - "time": "2020-01-04T13:00:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-15T15:55:41+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" }, { "name": "symfony/yaml", @@ -5157,22 +5321,22 @@ }, { "name": "vufind-org/vufindharvest", - "version": "v3.0.0", + "version": "v4.0.1", "source": { "type": "git", "url": "https://github.com/vufind-org/vufindharvest.git", - "reference": "f0cb7188be3f6edd68f89962d7d3d771b2108775" + "reference": "8e12f40fd444a033178a836517973643dc04622e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/f0cb7188be3f6edd68f89962d7d3d771b2108775", - "reference": "f0cb7188be3f6edd68f89962d7d3d771b2108775", + "url": "https://api.github.com/repos/vufind-org/vufindharvest/zipball/8e12f40fd444a033178a836517973643dc04622e", + "reference": "8e12f40fd444a033178a836517973643dc04622e", "shasum": "" }, "require": { - "laminas/laminas-console": ">=2.2", "laminas/laminas-http": ">=2.2", - "php": ">=7.0.8" + "php": ">=7.0.8", + "symfony/console": "^4.0||^5.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "2.16.1", @@ -5180,8 +5344,8 @@ "phing/phing": "2.16.2", "phploc/phploc": "4.0.1", "phpmd/phpmd": "2.8.1", - "phpunit/phpunit": "6.5.14", - "sebastian/phpcpd": "3.0.1", + "phpunit/phpunit": "8.5.2", + "sebastian/phpcpd": "4.1.0", "squizlabs/php_codesniffer": "3.5.3" }, "type": "library", @@ -5203,7 +5367,7 @@ ], "description": "VuFind Harvest Tools", "homepage": "https://vufind.org/", - "time": "2020-01-27T21:06:16+00:00" + "time": "2020-03-23T14:30:57+00:00" }, { "name": "vufind-org/vufindhttp", @@ -5857,20 +6021,21 @@ }, { "name": "doctrine/annotations", - "version": "v1.8.0", + "version": "1.10.2", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc" + "reference": "b9d758e831c70751155c698c2f7df4665314a1cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/904dca4eb10715b92569fbcd79e201d5c349b6bc", - "reference": "904dca4eb10715b92569fbcd79e201d5c349b6bc", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/b9d758e831c70751155c698c2f7df4665314a1cb", + "reference": "b9d758e831c70751155c698c2f7df4665314a1cb", "shasum": "" }, "require": { "doctrine/lexer": "1.*", + "ext-tokenizer": "*", "php": "^7.1" }, "require-dev": { @@ -5880,7 +6045,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7.x-dev" + "dev-master": "1.9.x-dev" } }, "autoload": { @@ -5921,7 +6086,7 @@ "docblock", "parser" ], - "time": "2019-10-01T18:55:10+00:00" + "time": "2020-04-20T09:18:32+00:00" }, { "name": "doctrine/instantiator", @@ -6439,24 +6604,21 @@ }, { "name": "phpdocumentor/reflection-common", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a" + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a", - "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/6568f4687e5b41b054365f9ae03fcb1ed5f2069b", + "reference": "6568f4687e5b41b054365f9ae03fcb1ed5f2069b", "shasum": "" }, "require": { "php": ">=7.1" }, - "require-dev": { - "phpunit/phpunit": "~6" - }, "type": "library", "extra": { "branch-alias": { @@ -6487,7 +6649,7 @@ "reflection", "static analysis" ], - "time": "2018-08-07T13:53:10+00:00" + "time": "2020-04-27T09:25:28+00:00" }, { "name": "phpdocumentor/reflection-docblock", @@ -6709,16 +6871,16 @@ }, { "name": "phpspec/prophecy", - "version": "v1.10.2", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { @@ -6768,7 +6930,7 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -7867,16 +8029,16 @@ }, { "name": "symfony/config", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9" + "reference": "8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", - "reference": "cbfef5ae91ccd3b06621c18d58cd355c68c87ae9", + "url": "https://api.github.com/repos/symfony/config/zipball/8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0", + "reference": "8ba41fe053683e1e6e3f6fa21f07ea5c4dd9e4c0", "shasum": "" }, "require": { @@ -7927,96 +8089,34 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2020-02-04T09:32:40+00:00" - }, - { - "name": "symfony/console", - "version": "v4.4.5", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", - "reference": "4fa15ae7be74e53f6ec8c83ed403b97e23b665e9", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.8", - "symfony/service-contracts": "^1.1|^2" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/event-dispatcher": "<4.3|>=5", - "symfony/lock": "<4.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "^3.4|^4.0|^5.0", - "symfony/dependency-injection": "^3.4|^4.0|^5.0", - "symfony/event-dispatcher": "^4.3", - "symfony/lock": "^4.4|^5.0", - "symfony/process": "^3.4|^4.0|^5.0", - "symfony/var-dumper": "^4.3|^5.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.4-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "url": "https://github.com/fabpot", + "type": "github" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "time": "2020-02-24T13:10:00+00:00" + "time": "2020-04-15T15:56:18+00:00" }, { "name": "symfony/css-selector", - "version": "v3.4.38", + "version": "v3.4.40", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "ee9b946e7223b11257329a054c64396b19d619e1" + "reference": "9ccf6e78077a3fc1596e6c7b5958008965a11518" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/ee9b946e7223b11257329a054c64396b19d619e1", - "reference": "ee9b946e7223b11257329a054c64396b19d619e1", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/9ccf6e78077a3fc1596e6c7b5958008965a11518", + "reference": "9ccf6e78077a3fc1596e6c7b5958008965a11518", "shasum": "" }, "require": { @@ -8056,20 +8156,34 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2020-02-04T08:04:52+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-16T08:31:04+00:00" }, { "name": "symfony/dependency-injection", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342" + "reference": "9d0c2807962f7f12264ab459f48fb541dbd386bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ebb2e882e8c9e2eb990aa61ddcd389848466e342", - "reference": "ebb2e882e8c9e2eb990aa61ddcd389848466e342", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/9d0c2807962f7f12264ab459f48fb541dbd386bd", + "reference": "9d0c2807962f7f12264ab459f48fb541dbd386bd", "shasum": "" }, "require": { @@ -8129,20 +8243,34 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2020-02-29T09:50:10+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-16T16:36:56+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d" + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/4ad8e149799d3128621a3a1f70e92b9897a8930d", - "reference": "4ad8e149799d3128621a3a1f70e92b9897a8930d", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abc8e3618bfdb55e44c8c6a00abd333f831bbfed", + "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed", "shasum": "" }, "require": { @@ -8199,7 +8327,21 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2020-02-04T09:32:40+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -8261,16 +8403,16 @@ }, { "name": "symfony/filesystem", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd" + "reference": "a3ebf3bfd8a98a147c010a568add5a8aa4edea0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/266c9540b475f26122b61ef8b23dd9198f5d1cfd", - "reference": "266c9540b475f26122b61ef8b23dd9198f5d1cfd", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/a3ebf3bfd8a98a147c010a568add5a8aa4edea0f", + "reference": "a3ebf3bfd8a98a147c010a568add5a8aa4edea0f", "shasum": "" }, "require": { @@ -8307,20 +8449,34 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2020-01-21T08:20:44+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-04-12T14:39:55+00:00" }, { "name": "symfony/finder", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357" + "reference": "5729f943f9854c5781984ed4907bbb817735776b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ea69c129aed9fdeca781d4b77eb20b62cf5d5357", - "reference": "ea69c129aed9fdeca781d4b77eb20b62cf5d5357", + "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b", + "reference": "5729f943f9854c5781984ed4907bbb817735776b", "shasum": "" }, "require": { @@ -8356,20 +8512,34 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2020-02-14T07:42:58+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "419c4940024c30ccc033650373a1fe13890d3255" + "reference": "2a18e37a489803559284416df58c71ccebe50bf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/419c4940024c30ccc033650373a1fe13890d3255", - "reference": "419c4940024c30ccc033650373a1fe13890d3255", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/2a18e37a489803559284416df58c71ccebe50bf0", + "reference": "2a18e37a489803559284416df58c71ccebe50bf0", "shasum": "" }, "require": { @@ -8379,7 +8549,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -8415,20 +8585,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" + "reference": "37b0976c78b94856543260ce09b460a7bc852747" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", - "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747", + "reference": "37b0976c78b94856543260ce09b460a7bc852747", "shasum": "" }, "require": { @@ -8437,7 +8607,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.14-dev" + "dev-master": "1.15-dev" } }, "autoload": { @@ -8470,78 +8640,20 @@ "portable", "shim" ], - "time": "2020-01-13T11:15:53+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.14.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/5e66a0fa1070bf46bec4bea7962d285108edd675", - "reference": "5e66a0fa1070bf46bec4bea7962d285108edd675", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.14-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "time": "2020-01-13T11:15:53+00:00" + "time": "2020-02-27T09:26:54+00:00" }, { "name": "symfony/process", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7" + "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/bf9166bac906c9e69fb7a11d94875e7ced97bcd7", - "reference": "bf9166bac906c9e69fb7a11d94875e7ced97bcd7", + "url": "https://api.github.com/repos/symfony/process/zipball/4b6a9a4013baa65d409153cbb5a895bf093dc7f4", + "reference": "4b6a9a4013baa65d409153cbb5a895bf093dc7f4", "shasum": "" }, "require": { @@ -8577,78 +8689,34 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2020-02-07T20:06:44+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": [ + "funding": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "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-04-15T15:56:18+00:00" }, { "name": "symfony/stopwatch", - "version": "v4.4.5", + "version": "v4.4.8", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb" + "reference": "e0324d3560e4128270e3f08617480d9233d81cfc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/abc08d7c48987829bac301347faa10f7e8bbf4fb", - "reference": "abc08d7c48987829bac301347faa10f7e8bbf4fb", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e0324d3560e4128270e3f08617480d9233d81cfc", + "reference": "e0324d3560e4128270e3f08617480d9233d81cfc", "shasum": "" }, "require": { @@ -8685,7 +8753,21 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2020-01-04T13:00:46+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-03-27T16:54:36+00:00" }, { "name": "textalk/websocket", @@ -8808,16 +8890,16 @@ }, { "name": "webmozart/assert", - "version": "1.7.0", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", - "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", + "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6", + "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6", "shasum": "" }, "require": { @@ -8825,7 +8907,7 @@ "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "vimeo/psalm": "<3.6.0" + "vimeo/psalm": "<3.9.1" }, "require-dev": { "phpunit/phpunit": "^4.8.36 || ^7.5.13" @@ -8852,7 +8934,7 @@ "check", "validate" ], - "time": "2020-02-14T12:15:55+00:00" + "time": "2020-04-18T12:12:48+00:00" } ], "aliases": [], @@ -8869,5 +8951,6 @@ "platform-dev": [], "platform-overrides": { "php": "7.2" - } + }, + "plugin-api-version": "1.1.0" } diff --git a/config/application.config.php b/config/application.config.php index 04d3e88b76d508db42d48e382535fcbd944432e9..f9fa9b93b78008e1385d0e63e69f1ae834da5156 100644 --- a/config/application.config.php +++ b/config/application.config.php @@ -8,7 +8,6 @@ $modules = [ 'VuFindTheme', 'VuFindSearch', 'VuFind', 'VuFindAdmin', 'VuFindApi' ]; if (PHP_SAPI == 'cli' && APPLICATION_ENV !== 'testing') { - $modules[] = 'Laminas\Mvc\Console'; $modules[] = 'VuFindConsole'; } if (APPLICATION_ENV == 'development') { diff --git a/install.php b/install.php index d4c05aa88c022d49eb488319b5b3b7421ab53a29..a8c2e2a7e60acb2ef8289ed3207469d1c08b98b5 100644 --- a/install.php +++ b/install.php @@ -26,662 +26,15 @@ * @link https://vufind.org/wiki/installation Wiki */ -require_once __DIR__ . '/vendor/autoload.php'; - -use Laminas\Console\Getopt; - -define('MULTISITE_NONE', 0); -define('MULTISITE_DIR_BASED', 1); -define('MULTISITE_HOST_BASED', 2); - -$baseDir = str_replace('\\', '/', dirname(__FILE__)); -$overrideDir = $baseDir . '/local'; -$host = $module = ''; -$multisiteMode = MULTISITE_NONE; -$basePath = '/vufind'; - -try { - $opts = new Getopt( - array( - 'use-defaults' => - 'Use VuFind Defaults to Configure (ignores any other arguments passed)', - 'overridedir=s' => - "Where would you like to store your local settings? [{$baseDir}/local]", - 'module-name=s' => - 'What module name would you like to use? Use disabled, to not use', - 'basepath=s' => - "What base path should be used in VuFind's URL? [{$basePath}]", - 'multisite-w' => - 'Specify we are going to setup a multisite. Options: directory and host', - 'hostname=s' => - 'Specify the hostname for the VuFind Site, When multisite=host', - 'non-interactive' => - 'Use settings if provided via arguments, otherwise use defaults', - ) - ); - $opts->parse(); -} catch (Exception $e) { - echo is_callable([$e, 'getUsageMessage']) - ? $e->getUsageMessage() : $e->getMessage() . "\n"; - exit; -} - -echo "VuFind has been found in {$baseDir}.\n\n"; - -// Are we allowing user interaction? -$interactive = !$opts->getOption('non-interactive'); -$userInputNeeded = array(); - -// Load user settings if we are not forcing defaults: -if (!$opts->getOption('use-defaults')) { - if ($opts->getOption('overridedir')) { - $overrideDir = $opts->getOption('overridedir'); - } else if ($interactive) { - $userInputNeeded['overrideDir'] = true; - } - if ($opts->getOption('module-name')) { - if ($opts->getOption('module-name') !== 'disabled') { - $module = $opts->getOption('module-name'); - if (($result = validateModules($module)) !== true) { - die($result . "\n"); - } - } - } else if ($interactive) { - $userInputNeeded['module'] = true; - } - - if ($opts->getOption('basepath')) { - $basePath = $opts->getOption('basepath'); - if (($result = validateBasePath($basePath, true)) !== true) { - die($result . "\n"); - } - } else if ($interactive) { - $userInputNeeded['basePath'] = true; - } - - // We assume "single site" mode unless the --multisite switch is set: - if ($opts->getOption('multisite')) { - if ($opts->getOption('multisite') === 'directory') { - $multisiteMode = MULTISITE_DIR_BASED; - } else if ($opts->getOption('multisite') === 'host') { - $multisiteMode = MULTISITE_HOST_BASED; - } else if (($bad = $opts->getOption('multisite')) && $bad !== true) { - die('Unexpected multisite mode: ' . $bad . "\n"); - } else if ($interactive) { - $userInputNeeded['multisiteMode'] = true; - } - } - - // Now that we've validated as many parameters as possible, retrieve - // user input where needed. - if (isset($userInputNeeded['overrideDir'])) { - $overrideDir = getOverrideDir($overrideDir); - } - if (isset($userInputNeeded['module'])) { - $module = getModule(); - } - if (isset($userInputNeeded['basePath'])) { - $basePath = getBasePath($basePath); - } - if (isset($userInputNeeded['multisiteMode'])) { - $multisiteMode = getMultisiteMode(); - } - - // Load supplemental multisite parameters: - if ($multisiteMode == MULTISITE_HOST_BASED) { - if ($opts->getOption('hostname')) { - $host = $opts->getOption('hostname'); - } else if ($interactive) { - $host = getHost(); - } - } -} - -// Make sure the override directory is initialized (using defaults or CLI -// parameters will not have initialized it yet; attempt to reinitialize it -// here is harmless if it was already initialized in interactive mode): -initializeOverrideDir($overrideDir, true); - -// Normalize the module setting to remove whitespace: -$module = preg_replace('/\s/', '', $module); - -// Build the Windows start file in case we need it: -buildWindowsConfig($baseDir, $overrideDir, $module); - -// Build the import configuration: -buildImportConfig($baseDir, $overrideDir, 'import.properties'); -buildImportConfig($baseDir, $overrideDir, 'import_auth.properties'); - -// Build the custom module, if necessary: -if (!empty($module)) { - buildModules($baseDir, $module); -} - -// Build the final configuration: -buildApacheConfig($baseDir, $overrideDir, $basePath, $module, $multisiteMode, $host); - -// Report success: -echo "Apache configuration written to {$overrideDir}/httpd-vufind.conf.\n\n"; -echo "You now need to load this configuration into Apache.\n"; -getApacheLocation($overrideDir); -if (!empty($host)) { - echo "Since you are using a host-based multisite configuration, you will also" . - "\nneed to do some virtual host configuration. See\n" . - " http://httpd.apache.org/docs/2.2/vhosts/\n\n"; -} -if ('/' == $basePath) { - echo "Since you are installing VuFind at the root of your domain, you will also" - . "\nneed to edit your Apache configuration to change DocumentRoot to:\n" - . $baseDir . "/public\n\n"; -} -echo "Once the configuration is linked, restart Apache. You should now be able\n"; -echo "to access VuFind at http://localhost{$basePath}\n\n"; -echo "For proper use of command line tools, you should also ensure that your\n"; -if (empty($module)) { - echo "VUFIND_HOME and VUFIND_LOCAL_DIR environment variables are set to\n"; - echo "{$baseDir} and {$overrideDir} respectively.\n\n"; -} else { - echo "VUFIND_HOME, VUFIND_LOCAL_MODULES and VUFIND_LOCAL_DIR environment\n"; - echo "variables are set to {$baseDir}, {$module} and {$overrideDir} "; - echo "respectively.\n\n"; -} - -/** - * Display system-specific information for where configuration files are found and/or - * symbolic links should be created. - * - * @param string $overrideDir Path to VuFind's local override directory - * - * @return void - */ -function getApacheLocation($overrideDir) -{ - // There is one special case for Windows, and a variety of different - // Unix-flavored possibilities that all work similarly. - if (strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN') { // Windows - echo "Go to Start -> Apache HTTP Server -> Edit the Apache httpd.conf\n"; - echo "and add this line to your httpd.conf file: \n"; - echo " Include {$overrideDir}/httpd-vufind.conf\n\n"; - echo "If you are using a bundle like XAMPP and do not have this start\n"; - echo "menu option, you should find and edit your httpd.conf file manually\n"; - echo "(usually in a location like c:\\xampp\\apache\\conf).\n\n"; - } else { - if (is_dir('/etc/httpd/conf.d')) { // Mandriva / RedHat - $confD = '/etc/httpd/conf.d'; - $httpdConf = '/etc/httpd/conf/httpd.conf'; - } else if (is_dir('/etc/apache2/2.2/conf.d')) { // Solaris - $confD = '/etc/apache2/2.2/conf.d'; - $httpdConf = '/etc/apache2/2.2/httpd.conf'; - } else if (is_dir('/etc/apache2/conf-enabled')) { // new Ubuntu / OpenSUSE - $confD = '/etc/apache2/conf-enabled'; - $httpdConf = '/etc/apache2/apache2.conf'; - } else if (is_dir('/etc/apache2/conf.d')) { // old Ubuntu / OpenSUSE - $confD = '/etc/apache2/conf.d'; - $httpdConf = '/etc/apache2/httpd.conf'; - } else if (is_dir('/opt/local/apache2/conf/extra')) { // Mac with Mac Ports - $confD = '/opt/local/apache2/conf/extra'; - $httpdConf = '/opt/local/apache2/conf/httpd.conf'; - } else { - $confD = '/path/to/apache/conf.d'; - $httpdConf = false; - } - - // Check if httpd.conf really exists before recommending a specific path; - // if missing, just use the generic name: - $httpdConf = ($httpdConf && file_exists($httpdConf)) - ? $httpdConf : 'httpd.conf'; - - // Suggest a symlink name based on the local directory, so if running in - // multisite mode, we don't use the same symlink for multiple instances: - $symlink = basename($overrideDir); - $symlink = ($symlink == 'local') ? 'vufind' : ('vufind-' . $symlink); - $symlink .= '.conf'; - - echo "You can do it in either of two ways:\n\n"; - echo " a) Add this line to your {$httpdConf} file:\n"; - echo " Include {$overrideDir}/httpd-vufind.conf\n\n"; - echo " b) Link the configuration to Apache's config directory like this:"; - echo "\n ln -s {$overrideDir}/httpd-vufind.conf {$confD}/{$symlink}\n"; - echo "\nOption b is preferable if your platform supports it,\n"; - echo "but option a is more certain to be supported.\n\n"; - } -} - -/** - * Validate a base path. Returns true on success, message on failure. - * - * @param string $basePath String to validate. - * @param bool $allowEmpty Are empty values acceptable? - * - * @return bool|string - */ -function validateBasePath($basePath, $allowEmpty = false) -{ - if ($allowEmpty && empty($basePath)) { - return true; - } - return preg_match('/^\/\w*$/', $basePath) - ? true - : 'Error: Base path must be alphanumeric and start with a slash.'; -} - -/** - * Get a base path from the user (or return a default). - * - * @param string $basePath Default value - * - * @return string - */ -function getBasePath($basePath) -{ - // Get VuFind base path: - while (true) { - $basePathInput = getInput( - "What base path should be used in VuFind's URL? [{$basePath}] " - ); - if (!empty($basePathInput)) { - if (($result = validateBasePath($basePathInput)) !== true) { - echo "$result\n\n"; - } else { - return $basePathInput; - } - } else { - return $basePath; - } - } -} - -/** - * Initialize the override directory and report success or failure. - * - * @param string $dir Path to attempt to initialize - * @param bool $dieOnError Should we die outright if we fail? - * - * @return void - */ -function initializeOverrideDir($dir, $dieOnError = false) -{ - $dirStatus = buildDirs( - array( - $dir, - $dir . '/cache', - $dir . '/config', - $dir . '/harvest', - $dir . '/import' - ) - ); - if ($dieOnError && ($dirStatus !== true)) { - die("Cannot initialize local override directory: {$dir}\n"); - } - return $dirStatus === true; -} - -/** - * Get an override directory from the user (or return a default). - * - * @param string $overrideDir Default value - * - * @return string - */ -function getOverrideDir($overrideDir) -{ - // Get override directory path: - while (true) { - $overrideDirInput = getInput( - "Where would you like to store your local settings? [{$overrideDir}] " - ); - if (!empty($overrideDirInput)) { - if (!initializeOverrideDir($overrideDirInput)) { - echo "Error: Cannot initialize settings in '$overrideDirInput'.\n\n"; - } else { - return str_replace('\\', '/', realpath($overrideDirInput)); - } - } else { - return $overrideDir; - } - } -} - -/** - * Validate a comma-separated list of module names. Returns true on success, message - * on failure. - * - * @param string $modules Module name to validate. - * - * @return bool|string - */ -function validateModules($modules) -{ - foreach (explode(',', $modules) as $module) { - $result = validateModule(trim($module)); - if ($result !== true) { - return $result; - } - } - return true; -} - -/** - * Validate the custom module name. Returns true on success, message on failure. - * - * @param string $module Module name to validate. - * - * @return bool|string - */ -function validateModule($module) -{ - $regex = '/^[a-zA-Z][0-9a-zA-Z_]*$/'; - $illegalModules = array( - 'VuFind', 'VuFindAdmin', 'VuFindConsole', 'VuFindDevTools', - 'VuFindLocalTemplate', 'VuFindSearch', 'VuFindTest', 'VuFindTheme', - ); - if (in_array($module, $illegalModules)) { - return "{$module} is a reserved module name; please try another."; - } else if (empty($module) || preg_match($regex, $module)) { - return true; - } else { - return "Illegal name: {$module}; please use alphanumeric text."; - } -} - -/** - * Get the custom module name from the user (or blank for none). - * - * @return string - */ -function getModule() -{ - // Get custom module name: - echo "\nVuFind supports use of a custom module for storing local code "; - echo "changes.\nIf you do not plan to customize the code, you can "; - echo "skip this step.\nIf you decide to use a custom module, the name "; - echo "you choose will be used for\nthe module's directory name and its "; - echo "PHP namespace.\n"; - while (true) { - $moduleInput = trim( - getInput( - "\nWhat module name would you like to use? [blank for none] " - ) - ); - if (($result = validateModules($moduleInput)) === true) { - return $moduleInput; - } - echo "\n$result\n"; - } -} - -/** - * Get the user's preferred multisite mode. - * - * @return int - */ -function getMultisiteMode() -{ - echo "\nWhen running multiple VuFind sites against a single installation, you" - . "need\nto decide how to distinguish between instances. Choose an option:" - . "\n\n" . MULTISITE_DIR_BASED - . ".) Directory-based (i.e. http://server/vufind1 vs. http://server/vufind2)" - . "\n" . MULTISITE_HOST_BASED - . ".) Host-based (i.e. http://vufind1.server vs. http://vufind2.server)" - . "\n\nor enter " . MULTISITE_NONE . " to disable multisite mode.\n"; - $legal = array(MULTISITE_NONE, MULTISITE_DIR_BASED, MULTISITE_HOST_BASED); - while (true) { - $input = getInput("\nWhich option do you want? "); - if (!is_numeric($input) || !in_array(intval($input), $legal)) { - echo "Invalid selection."; - } else { - return intval($input); - } - } -} - -/** - * Validate the user's hostname input. Returns true on success, message on failure. - * - * @param string $host String to check - * - * @return bool|string - */ -function validateHost($host) -{ - // From http://stackoverflow.com/questions/106179/ - // regular-expression-to-match-hostname-or-ip-address - $valid = "/^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*" - . "([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/"; - return preg_match($valid, $host) - ? true - : 'Invalid hostname.'; -} - -/** - * Get the user's hostname preference. - * - * @return string - */ -function getHost() -{ - while (true) { - $input = getInput("\nPlease enter the hostname for your site: "); - if (($result = validateHost($input)) === true) { - return $input; - } else { - echo "$result\n"; - } - } -} - -/** - * readline() does not exist on Windows. This is a simple wrapper for portability. - * - * @param string $prompt Prompt to display to the user. - * - * @return string User-entered response. - */ -function getInput($prompt) -{ - return \Laminas\Console\Prompt\Line::prompt($prompt, true); -} - -/** - * Generate the Apache configuration. - * - * @param string $baseDir The VuFind base directory - * @param string $overrideDir The VuFind override directory - * @param string $basePath The VuFind URL base path - * @param string $module The VuFind custom module name (or empty for none) - * @param int $multi Multisite mode preference - * @param string $host Virtual host name (or empty for none) - * - * @return void - */ -function buildApacheConfig($baseDir, $overrideDir, $basePath, $module, $multi, $host) -{ - $baseConfig = $baseDir . '/config/vufind/httpd-vufind.conf'; - $config = @file_get_contents($baseConfig); - if (empty($config)) { - die("Problem reading {$baseConfig}.\n\n"); - } - $config = str_replace('/usr/local/vufind/local', '%override-dir%', $config); - $config = str_replace('/usr/local/vufind', '%base-dir%', $config); - $config = preg_replace('|([^/])\/vufind|', '$1%base-path%', $config); - $config = str_replace('%override-dir%', $overrideDir, $config); - $config = str_replace('%base-dir%', $baseDir, $config); - $config = str_replace('%base-path%', $basePath, $config); - // Special cases for root basePath: - if ('/' == $basePath) { - $config = str_replace('//', '/', $config); - $config = str_replace('Alias /', '#Alias /', $config); - } - if (!empty($module)) { - $config = str_replace( - "#SetEnv VUFIND_LOCAL_MODULES VuFindLocalTemplate", - "SetEnv VUFIND_LOCAL_MODULES {$module}", $config - ); - } - - // In multisite mode, we need to make environment variables conditional: - switch ($multi) { - case MULTISITE_DIR_BASED: - $config = preg_replace( - '/SetEnv\s+(\w+)\s+(.*)/', - 'SetEnvIf Request_URI "^' . $basePath . '" $1=$2', - $config - ); - break; - case MULTISITE_HOST_BASED: - if (($result = validateHost($host)) !== true) { - die($result . "\n"); - } - $config = preg_replace( - '/SetEnv\s+(\w+)\s+(.*)/', - 'SetEnvIfNoCase Host ' . str_replace('.', '\.', $host) . ' $1=$2', - $config - ); - break; - } - - $target = $overrideDir . '/httpd-vufind.conf'; - if (file_exists($target)) { - $bak = $target . '.bak.' . time(); - copy($target, $bak); - echo "Backed up existing Apache configuration to $bak.\n"; - } - if (!@file_put_contents($target, $config)) { - die("Problem writing {$overrideDir}/httpd-vufind.conf.\n\n"); - } -} - -/** - * Build the Windows-specific startup configuration. - * - * @param string $baseDir The VuFind base directory - * @param string $overrideDir The VuFind override directory - * @param string $module The VuFind custom module name (or empty for none) - * - * @return void - */ -function buildWindowsConfig($baseDir, $overrideDir, $module) -{ - $batch = "@set VUFIND_HOME={$baseDir}\n" . - "@set VUFIND_LOCAL_DIR={$overrideDir}\n" . - (empty($module) ? '' : "@set VUFIND_LOCAL_MODULES={$module}\n"); - if (!@file_put_contents($baseDir . '/env.bat', $batch)) { - die("Problem writing {$baseDir}/env.bat.\n\n"); - } -} - -/** - * Configure a SolrMarc properties file. - * - * @param string $baseDir The VuFind base directory - * @param string $overrideDir The VuFind override directory - * @param string $filename The properties file to configure - * - * @return void - */ -function buildImportConfig($baseDir, $overrideDir, $filename) -{ - $target = $overrideDir . '/import/' . $filename; - if (file_exists($target)) { - echo "Warning: $target already exists; skipping file creation.\n"; - } else { - $import = @file_get_contents($baseDir . '/import/' . $filename); - $import = str_replace("/usr/local/vufind", $baseDir, $import); - $import = preg_replace( - "/^\s*solrmarc.path\s*=.*$/m", - "solrmarc.path = {$overrideDir}/import|{$baseDir}/import", $import - ); - if (!@file_put_contents($target, $import)) { - die("Problem writing {$overrideDir}/import/{$filename}.\n\n"); - } - } -} - -/** - * Build a set of directories. - * - * @param array $dirs Directories to build - * - * @return bool|string True on success, name of problem directory on failure - */ -function buildDirs($dirs) -{ - foreach ($dirs as $dir) { - if (!is_dir($dir) && !@mkdir($dir)) { - return $dir; - } - } - return true; -} - -/** - * Make sure all modules exist (and create them if they do not. - * - * @param string $baseDir The VuFind base directory - * @param string $modules The comma-separated list of modules (assumed valid!) - * - * @return void - */ -function buildModules($baseDir, $modules) -{ - foreach (explode(',', $modules) as $module) { - $moduleDir = $baseDir . '/module/' . $module; - // Is module missing? If so, create it from the template: - if (!file_exists($moduleDir . '/Module.php')) { - buildModule($baseDir, $module); - } - } -} - -/** - * Build the module for storing local code changes. - * - * @param string $baseDir The VuFind base directory - * @param string $module The name of the new module (assumed valid!) - * - * @return void - */ -function buildModule($baseDir, $module) -{ - // Create directories: - $moduleDir = $baseDir . '/module/' . $module; - $dirStatus = buildDirs( - array( - $moduleDir, - $moduleDir . '/config', - $moduleDir . '/src', - $moduleDir . '/src/' . $module - ) - ); - if ($dirStatus !== true) { - die("Problem creating {$dirStatus}.\n"); - } - - // Copy configuration: - $configFile = $baseDir . '/module/VuFindLocalTemplate/config/module.config.php'; - $config = @file_get_contents($configFile); - if (!$config) { - die("Problem reading {$configFile}.\n"); - } - $success = @file_put_contents( - $moduleDir . '/config/module.config.php', - str_replace('VuFindLocalTemplate', $module, $config) - ); - if (!$success) { - die("Problem writing {$moduleDir}/config/module.config.php.\n"); - } - - // Copy PHP code: - $moduleFile = $baseDir . '/module/VuFindLocalTemplate/Module.php'; - $contents = @file_get_contents($moduleFile); - if (!$contents) { - die("Problem reading {$moduleFile}.\n"); - } - $success = @file_put_contents( - $moduleDir . '/Module.php', - str_replace('VuFindLocalTemplate', $module, $contents) - ); - if (!$success) { - die("Problem writing {$moduleDir}/Module.php.\n"); - } +if (!file_exists(__DIR__ . '/vendor/autoload.php')) { + die("Please run 'composer install' to load dependencies.\n"); } +require_once __DIR__ . '/vendor/autoload.php'; +require_once __DIR__ + . '/module/VuFindConsole/src/VuFindConsole/Command/Install/InstallCommand.php'; + +$command = new \VuFindConsole\Command\Install\InstallCommand($argv[0]); +$application = new \Symfony\Component\Console\Application(); +$application->add($command); +$application->setDefaultCommand($command->getName(), true); +return $application->run(); diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index e9bda4bd2194f17e41a7adedf6bd4ffd8b5d605d..c1ee018af9c4986de69bd6e3ba86c17ab7b21a2d 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -437,9 +437,6 @@ $config = [ 'Laminas\Mvc\I18n\Translator' => 'VuFind\I18n\Translator\TranslatorFactory', 'Laminas\Session\SessionManager' => 'VuFind\Session\ManagerFactory', ], - 'delegators' => [ - 'VuFind\Http\PhpEnvironment\Request' => [ \Laminas\Mvc\Console\Service\ConsoleRequestDelegatorFactory::class ], - ], 'initializers' => [ 'VuFind\ServiceManager\ServiceInitializer', ], diff --git a/module/VuFind/src/VuFind/Bootstrapper.php b/module/VuFind/src/VuFind/Bootstrapper.php index 60cef717526320aa02b9ab3f31d81a2bb60cc0b4..b5f0437ffbe7e089202db4e5de1cba1d3a78209c 100644 --- a/module/VuFind/src/VuFind/Bootstrapper.php +++ b/module/VuFind/src/VuFind/Bootstrapper.php @@ -27,7 +27,6 @@ */ namespace VuFind; -use Laminas\Console\Console; use Laminas\Mvc\MvcEvent; use Laminas\Router\Http\RouteMatch; @@ -133,7 +132,7 @@ class Bootstrapper { // 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)) { + if (PHP_SAPI !== 'cli' && !($this->config->System->available ?? true)) { $callback = function ($e) { $routeMatch = new RouteMatch( ['controller' => 'Error', 'action' => 'Unavailable'], 1 @@ -175,7 +174,7 @@ class Bootstrapper { $callback = function ($event) { $serviceManager = $event->getApplication()->getServiceManager(); - if (!Console::isConsole()) { + if (PHP_SAPI !== 'cli') { $viewModel = $serviceManager->get('ViewManager')->getViewModel(); // Grab the template name from the first child -- we can use this to @@ -278,7 +277,7 @@ class Bootstrapper protected function initLanguage() { // Language not supported in CLI mode: - if (Console::isConsole()) { + if (PHP_SAPI == 'cli') { return; } @@ -366,7 +365,7 @@ class Bootstrapper protected function initExceptionBasedHttpStatuses() { // HTTP statuses not needed in console mode: - if (Console::isConsole()) { + if (PHP_SAPI == 'cli') { return; } @@ -412,7 +411,7 @@ class Bootstrapper $exception = $event->getParam('exception'); // Console request does not include server, // so use a dummy in that case. - $server = Console::isConsole() + $server = (PHP_SAPI == 'cli') ? new \Laminas\Stdlib\Parameters(['env' => 'console']) : $event->getRequest()->getServer(); if (!empty($exception)) { diff --git a/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php index 08fe2cba934ed34f64ec99e3dde4d2f171534767..5c27503974d0b1f25d6a3f1d34b7f88370ef413c 100644 --- a/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php +++ b/module/VuFind/src/VuFind/Cookie/CookieManagerFactory.php @@ -28,7 +28,6 @@ namespace VuFind\Cookie; use Interop\Container\ContainerInterface; -use Laminas\Console\Console; use Laminas\ServiceManager\Factory\FactoryInterface; /** @@ -66,7 +65,7 @@ class CookieManagerFactory implements FactoryInterface ->get('config'); $path = '/'; if ($config->Cookies->limit_by_path ?? false) { - $path = Console::isConsole() + $path = (PHP_SAPI == 'cli') ? '' : $container->get('Request')->getBasePath(); if (empty($path)) { $path = '/'; diff --git a/module/VuFind/src/VuFind/Log/LoggerFactory.php b/module/VuFind/src/VuFind/Log/LoggerFactory.php index 7f74574448c760218edf42de6565d43c47cf2818..01525e6c3e3c59710c6e073a6d0e24f61d2f8999 100644 --- a/module/VuFind/src/VuFind/Log/LoggerFactory.php +++ b/module/VuFind/src/VuFind/Log/LoggerFactory.php @@ -29,7 +29,6 @@ namespace VuFind\Log; use Interop\Container\ContainerInterface; use Laminas\Config\Config; -use Laminas\Console\Console; use Laminas\Log\Writer\WriterInterface; use Laminas\ServiceManager\Factory\FactoryInterface; @@ -188,7 +187,7 @@ class LoggerFactory implements FactoryInterface // 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() + if (PHP_SAPI !== 'cli' && $container->get('Request')->getQuery()->get('debug') ) { return $container->get(\ZfcRbac\Service\AuthorizationService::class) diff --git a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php index cee13c4830613152b4275e7390e9efc58d3011c9..a4a3cdfe6e55ec5983c54e70e0a744fe2ac37e34 100644 --- a/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php +++ b/module/VuFind/src/VuFind/Role/PermissionProvider/IpRange.php @@ -30,7 +30,6 @@ */ namespace VuFind\Role\PermissionProvider; -use Laminas\Console\Console; use Laminas\Stdlib\RequestInterface; use VuFind\Net\IpAddressUtils; @@ -83,7 +82,7 @@ class IpRange implements PermissionProviderInterface */ public function getPermissions($options) { - if (Console::isConsole()) { + if (PHP_SAPI == 'cli') { return []; } // Check if any regex matches.... diff --git a/module/VuFind/src/VuFind/Sitemap/Generator.php b/module/VuFind/src/VuFind/Sitemap/Generator.php index e9505f6f3be4070dc93f445d279b3e8e86fbb3b6..e9c36f991dd8c4875ef909539704256bced1ccdc 100644 --- a/module/VuFind/src/VuFind/Sitemap/Generator.php +++ b/module/VuFind/src/VuFind/Sitemap/Generator.php @@ -28,7 +28,6 @@ namespace VuFind\Sitemap; use Laminas\Config\Config; -use Laminas\Console\Console; use VuFind\Search\BackendManager; use VuFindSearch\Backend\Solr\Backend; use VuFindSearch\Backend\Solr\Response\Json\RecordCollectionFactory; @@ -125,11 +124,11 @@ class Generator protected $warnings = []; /** - * Verbose mode + * Verbose callback * - * @var bool + * @var \Callable */ - protected $verbose = false; + protected $verbose = null; /** * Mode of retrieving IDs from the index (may be 'terms' or 'search') @@ -183,11 +182,12 @@ class Generator } /** - * Get/set verbose mode + * Get/set verbose callback * - * @param bool $newMode New verbose mode + * @param \Callable|null $newMode Callback for writing verbose messages (or null + * to disable them) * - * @return bool Current or new verbose mode + * @return \Callable|null Current verbose callback (null if disabled) */ public function setVerbose($newMode = null) { @@ -197,6 +197,20 @@ class Generator return $this->verbose; } + /** + * Write a verbose message (if configured to do so) + * + * @param string $msg Message to display + * + * @return void + */ + protected function verboseMsg($msg) + { + if (is_callable($this->verbose)) { + call_user_func($this->verbose, $msg); + } + } + /** * Get/set base url * @@ -266,11 +280,9 @@ class Generator $this->buildIndex($currentPage - 1); // Display total elapsed time in verbose mode: - if ($this->verbose) { - Console::writeLine( - 'Elapsed time (in seconds): ' . round($this->getTime() - $startTime) - ); - } + $this->verboseMsg( + 'Elapsed time (in seconds): ' . round($this->getTime() - $startTime) + ); } /** @@ -325,9 +337,7 @@ class Generator // Update total record count: $recordCount += count($result['ids']); - if ($this->verbose) { - Console::writeLine("Page $currentPage, $recordCount processed"); - } + $this->verboseMsg("Page $currentPage, $recordCount processed"); // Update counter: $currentPage++; diff --git a/module/VuFind/src/VuFind/XSLT/Importer.php b/module/VuFind/src/VuFind/XSLT/Importer.php index 3fd9a5b2c8307b73f9928c25e28916acd9aaf47f..d4a1263a85d97a5097eb53bfc426e954e37381d8 100644 --- a/module/VuFind/src/VuFind/XSLT/Importer.php +++ b/module/VuFind/src/VuFind/XSLT/Importer.php @@ -28,7 +28,6 @@ namespace VuFind\XSLT; use DOMDocument; -use Laminas\Console\Console; use Laminas\ServiceManager\ServiceLocatorInterface; use VuFind\Config\Locator as ConfigLocator; use VuFindSearch\Backend\Solr\Document\RawXMLDocument; @@ -71,7 +70,7 @@ class Importer * @param bool $testMode Are we in test-only mode? * * @throws \Exception - * @return void + * @return string Transformed XML */ public function save($xmlFile, $properties, $index = 'Solr', $testMode = false @@ -83,9 +82,8 @@ class Importer if (!$testMode) { $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); $solr->save($index, new RawXMLDocument($xml)); - } else { - Console::write($xml . "\n"); } + return $xml; } /** diff --git a/module/VuFindConsole/Module.php b/module/VuFindConsole/Module.php index 04768b93beded7611be0993af6e81c9b65d302f5..d2b517de4f10704ad97058021aa8cdbe0e5baa37 100644 --- a/module/VuFindConsole/Module.php +++ b/module/VuFindConsole/Module.php @@ -27,8 +27,6 @@ */ namespace VuFindConsole; -use Laminas\Console\Adapter\AdapterInterface as Console; - /** * Code module for VuFind's console functionality * @@ -38,8 +36,7 @@ use Laminas\Console\Adapter\AdapterInterface as Console; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development */ -class Module implements \Laminas\ModuleManager\Feature\ConsoleUsageProviderInterface, - \Laminas\ModuleManager\Feature\ConsoleBannerProviderInterface +class Module { /** * Get module configuration @@ -66,76 +63,4 @@ class Module implements \Laminas\ModuleManager\Feature\ConsoleUsageProviderInter ], ]; } - - /** - * Returns a string containing a banner text, that describes the module and/or - * the application. - * The banner is shown in the console window, when the user supplies invalid - * command-line parameters or invokes the application with no parameters. - * - * The method is called with active Laminas\Console\Adapter\AdapterInterface that - * can be used to directly access Console and send output. - * - * @param Console $console Console adapter - * - * @return string|null - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getConsoleBanner(Console $console) - { - return 'VuFind'; - } - - /** - * Return usage information - * - * @param Console $console Console adapter - * - * @return array - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function getConsoleUsage(Console $console) - { - return [ - 'compile theme' => 'Flatten a theme hierarchy for improved performance', - 'generate dynamicroute' => 'Add a dynamic route', - 'generate extendclass' => 'Subclass a service, w/ lookup by class name', - 'generate extendservice' => 'Override a service with a new child class', - 'generate nontabrecordaction' => 'Add routes for non-tab record action', - 'generate plugin' => 'Create a new plugin class', - 'generate recordroute' => 'Add a record route', - 'generate staticroute' => 'Add a static route', - 'generate theme' => 'Create and configure a new theme', - 'harvest harvest_oai' => 'OAI-PMH harvester', - 'harvest merge-marc' => 'MARC merge tool', - 'import import-xsl' => 'XSLT importer', - 'import webcrawl' => 'Web crawler', - 'language addusingtemplate' => 'Build new language strings from ' - . 'existing ones using a template', - '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', - 'util switch_db_hash' => 'Switch the hashing algorithm in the database ' - . 'and config. Expects new algorithm and (optional) new key as' - . ' parameters.', - ]; - } } diff --git a/module/VuFindConsole/config/module.config.php b/module/VuFindConsole/config/module.config.php index 7f694e5660855bce6d1b6413304fecbeee68e32d..af327c2de49224b16fda398df09c6503ca233724 100644 --- a/module/VuFindConsole/config/module.config.php +++ b/module/VuFindConsole/config/module.config.php @@ -24,25 +24,11 @@ $config = [ 'util' => 'VuFindConsole\Controller\UtilController', ], ], - 'console' => [ - 'router' => [ - 'routes' => [ - 'default-route' => [ - 'type' => 'catchall', - 'options' => [ - 'route' => '', - 'defaults' => [ - 'controller' => 'redirect', - 'action' => 'consoledefault', - ], - ], - ], - ], - ], - ], 'service_manager' => [ 'factories' => [ 'VuFind\Sitemap\Generator' => 'VuFind\Sitemap\GeneratorFactory', + 'VuFindConsole\Command\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFindConsole\ConsoleRunner' => 'VuFindConsole\ConsoleRunnerFactory', 'VuFindConsole\Generator\GeneratorTools' => 'VuFindConsole\Generator\GeneratorToolsFactory', ], ], @@ -50,46 +36,11 @@ $config = [ // CLI tools are admin-oriented, so we should always output full errors: 'display_exceptions' => true, ], + 'vufind' => [ + 'plugin_managers' => [ + 'command' => [ /* see VuFindConsole\Command\PluginManager for defaults */ ], + ], + ], ]; -$routes = [ - 'compile/theme' => 'compile theme [--force] [<source>] [<target>]', - 'generate/dynamicroute' => 'generate dynamicroute [<name>] [<newController>] [<newAction>] [<module>]', - 'generate/extendclass' => 'generate extendclass [--extendfactory] [<class>] [<target>]', - 'generate/extendservice' => 'generate extendservice [<source>] [<target>]', - 'generate/nontabrecordaction' => 'generate nontabrecordaction [<newAction>] [<module>]', - 'generate/plugin' => 'generate plugin [<class>] [<factory>]', - 'generate/recordroute' => 'generate recordroute [<base>] [<newController>] [<module>]', - 'generate/staticroute' => 'generate staticroute [<name>] [<module>]', - 'generate/theme' => 'generate theme [<themename>]', - 'generate/thememixin' => 'generate thememixin [<name>]', - 'harvest/harvest_oai' => 'harvest harvest_oai [...params]', - 'harvest/merge-marc' => 'harvest merge-marc [<dir>]', - 'import/import-xsl' => 'import import-xsl [--test-only] [--index=] [<xml>] [<properties>]', - 'import/webcrawl' => 'import webcrawl [--test-only] [--index=]', - 'language/addusingtemplate' => 'language addusingtemplate [<target>] [<template>]', - '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] [<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 [--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>]', -]; - -$routeGenerator = new \VuFindConsole\Route\RouteGenerator(); -$routeGenerator->addRoutes($config, $routes); - return $config; diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..13d5f3b6f353dba48530d3b44eaddd93f0da000f --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommand.php @@ -0,0 +1,125 @@ +<?php +/** + * Console command: Compile themes. + * + * 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 Console + * @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 VuFindConsole\Command\Compile; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindTheme\ThemeCompiler; + +/** + * Console command: Compile themes. + * + * @category VuFind + * @package Console + * @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 ThemeCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'compile/theme'; + + /** + * Theme compiler + * + * @var ThemeCompiler + */ + protected $compiler; + + /** + * Constructor + * + * @param ThemeCompiler $compiler Theme compiler + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(ThemeCompiler $compiler, $name = null) + { + $this->compiler = $compiler; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Theme compiler') + ->setHelp('Flattens a theme hierarchy for improved performance.') + ->addArgument( + 'source', + InputArgument::REQUIRED, + 'the source theme to compile' + )->addArgument( + 'target', + InputArgument::OPTIONAL, + 'the target name for the compiled theme ' + . '(defaults to <source> with _compiled appended)' + )->addOption( + 'force', + 'f', + InputOption::VALUE_NONE, + 'If <target> exists, it will only be overwritten when this is set' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $source = $input->getArgument('source'); + $target = $input->getArgument('target'); + if (empty($target)) { + $target = "{$source}_compiled"; + } + $force = $input->getOption('force') ? true : false; + if (!$this->compiler->compile($source, $target, $force)) { + $output->writeln($this->compiler->getLastError()); + return 1; + } + $output->writeln('Success.'); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..acb3b673b2afa2565d1bca597bb901ba1959eebc --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Compile/ThemeCommandFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for console command: Compile themes. + * + * 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 Console + * @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 VuFindConsole\Command\Compile; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for console command: Compile themes. + * + * @category VuFind + * @package Console + * @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 ThemeCommandFactory 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\ThemeCompiler::class) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommand.php similarity index 58% rename from module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php rename to module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommand.php index 60279b85378337af169e431ef50301562b36acae..5cd832a6c585b4a4018a4c47b6e5d5caaccc56d3 100644 --- a/module/VuFindConsole/src/VuFindConsole/Route/RouteGenerator.php +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommand.php @@ -1,10 +1,10 @@ <?php /** - * Route Generator Class + * Abstract base class for generator commands. * * PHP version 7 * - * Copyright (C) Villanova University 2010. + * 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, @@ -20,43 +20,44 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * @category VuFind - * @package Route + * @package Console * @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 VuFindConsole\Route; +namespace VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Command\Command; +use VuFindConsole\Generator\GeneratorTools; /** - * Route Generator Class + * Abstract base class for generator commands. * * @category VuFind - * @package Route + * @package Console * @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 RouteGenerator +abstract class AbstractCommand extends Command { /** - * Add console routes to the configuration. + * Generator tools * - * @param array $config Configuration array to update - * @param array $routes Array of Controller/Action strings => route values + * @var GeneratorTools + */ + protected $generatorTools; + + /** + * Constructor * - * @return void + * @param GeneratorTools $tools Generator tools + * @param string|null $name The name of the command; passing null means it + * must be set in configure() */ - public function addRoutes(& $config, $routes) + public function __construct(GeneratorTools $tools, $name = null) { - foreach ($routes as $key => $route) { - list($controller, $action) = explode('/', $key); - $name = $controller . '-' . $action; - $config['console']['router']['routes'][$name] = [ - 'options' => [ - 'route' => $route, - 'defaults' => compact('controller', 'action'), - ] - ]; - } + $this->generatorTools = $tools; + parent::__construct($name); } } diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..8b177c0c74aadca25bb756f550293214ad6baeb2 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Shared factory for generator commands. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Shared factory for generator commands. + * + * @category VuFind + * @package Console + * @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 AbstractCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFindConsole\Generator\GeneratorTools::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..ca54e8b58535688d13daa9b3a6f22df46f7065b7 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommand.php @@ -0,0 +1,65 @@ +<?php +/** + * Abstract base class for generator commands relying on the service container. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Abstract base class for generator commands relying on the service container. + * + * @category VuFind + * @package Console + * @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 AbstractContainerAwareCommand extends AbstractCommand +{ + /** + * Top-level service container + * + * @var ContainerInterface + */ + protected $container; + + /** + * Constructor + * + * @param GeneratorTools $tools Generator tools + * @param ContainerInterface $container Top-level service container + * @param string|null $name The name of the command; passing null + * means it must be set in configure() + */ + public function __construct(GeneratorTools $tools, ContainerInterface $container, + $name = null + ) { + $this->container = $container; + parent::__construct($tools, $name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..241901fc09d27825ba7b362e9e1d76ce9990ef54 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractContainerAwareCommandFactory.php @@ -0,0 +1,64 @@ +<?php +/** + * Factory for console generator commands that rely on a service container. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; + +/** + * Factory for console generator commands that rely on a service container. + * + * @category VuFind + * @package Console + * @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 AbstractContainerAwareCommandFactory extends AbstractCommandFactory +{ + /** + * 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 + ) { + return parent::__invoke( + $container, $requestedName, array_merge([$container], $options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..7ba12094c60bf3812029e63e4dcf6a51a6d72a69 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommand.php @@ -0,0 +1,65 @@ +<?php +/** + * Abstract base class for route generator commands. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use VuFind\Route\RouteGenerator; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Abstract base class for route generator commands. + * + * @category VuFind + * @package Console + * @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 AbstractRouteCommand extends AbstractCommand +{ + /** + * Route generator + * + * @var RouteGenerator + */ + protected $routeGenerator; + + /** + * Constructor + * + * @param GeneratorTools $tools Generator tools + * @param RouteGenerator $routeGen Route generator + * @param string|null $name The name of the command; passing null + * means it must be set in configure() + */ + public function __construct(GeneratorTools $tools, RouteGenerator $routeGen, + $name = null + ) { + $this->routeGenerator = $routeGen; + parent::__construct($tools, $name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..40ddaaf7a62d1928a05f416ffcba9147e1ab9087 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractRouteCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Shared factory for route generator commands. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; +use VuFind\Route\RouteGenerator; + +/** + * Shared factory for route generator commands. + * + * @category VuFind + * @package Console + * @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 AbstractRouteCommandFactory extends AbstractCommandFactory +{ + /** + * 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 + ) { + $generator = new RouteGenerator(); + return parent::__invoke( + $container, $requestedName, array_merge([$generator], $options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractThemeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractThemeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..591ae77eaa0df5c99307c95107ad6b8009516992 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/AbstractThemeCommand.php @@ -0,0 +1,136 @@ +<?php +/** + * Abstract base class for theme resource generator commands. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindTheme\GeneratorInterface; + +/** + * Abstract base class for theme resource generator commands. + * + * @category VuFind + * @package Console + * @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 AbstractThemeCommand extends Command +{ + /** + * Theme resource generator + * + * @var GeneratorInterface + */ + protected $generator; + + /** + * Type of resource being generated (used in help messages) + * + * @var string + */ + protected $type; + + /** + * Extra text to append to the output when generation is successful. + * + * @var string + */ + protected $extraSuccessMessage = ''; + + /** + * Constructor + * + * @param GeneratorInterface $generator Generator to call + * @param string|null $name The name of the command; passing null + * means it must be set in configure() + */ + public function __construct(GeneratorInterface $generator, $name = null) + { + $this->generator = $generator; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription(ucwords($this->type) . ' generator') + ->setHelp('Creates and configures a new ' . $this->type . '.') + ->addArgument( + 'name', + InputArgument::OPTIONAL, + 'name of ' . $this->type + . ' to generate. Defaults to custom if unspecified.' + ); + } + + /** + * Run the generator. + * + * @param string $name Name of resource to generate + * + * @return bool + */ + protected function generate($name) + { + return $this->generator->generate($name); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $name = $input->getArgument('name'); + if (empty($name)) { + $output->writeln("\tNo {$this->type} name provided, using \"custom\""); + $name = 'custom'; + } + + $this->generator->setOutputInterface($output); + + if (!$this->generate($name)) { + $output->writeln($this->generator->getLastError()); + return 1; + } + $output->writeln(rtrim("\tFinished. {$this->extraSuccessMessage}")); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/DynamicRouteCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/DynamicRouteCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..08624bdbc28878bc89b739338989f556d00534f8 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/DynamicRouteCommand.php @@ -0,0 +1,111 @@ +<?php +/** + * Console command: Generate dynamic route. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: Generate dynamic route. + * + * @category VuFind + * @package Console + * @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 DynamicRouteCommand extends AbstractRouteCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/dynamicroute'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Dynamic route generator') + ->setHelp('Adds a dynamic route.') + ->addArgument( + 'route', + InputArgument::REQUIRED, + 'the route name (used by router), e.g. customList' + )->addArgument( + 'controller', + InputArgument::REQUIRED, + 'the controller name (used in URL), e.g. MyResearch' + )->addArgument( + 'action', + InputArgument::REQUIRED, + 'the action and segment params, e.g. CustomList/[:id]' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new route will be generated' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $route = $input->getArgument('route'); + $controller = $input->getArgument('controller'); + $action = $input->getArgument('action'); + $module = $input->getArgument('target_module'); + + $this->generatorTools->setOutputInterface($output); + + // Create backup of configuration + $configPath = $this->generatorTools->getModuleConfigPath($module); + $this->generatorTools->backUpFile($configPath); + + // Append the route + $config = include $configPath; + $this->routeGenerator + ->addDynamicRoute($config, $route, $controller, $action); + + // Write updated configuration + $this->generatorTools->writeModuleConfig($configPath, $config); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendClassCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendClassCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..b999c1fc1329c867779006e5a8ea4ab121bf12b4 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendClassCommand.php @@ -0,0 +1,105 @@ +<?php +/** + * Console command: extend 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: extend class. + * + * @category VuFind + * @package Console + * @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 ExtendClassCommand extends AbstractContainerAwareCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/extendclass'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Subclass generator') + ->setHelp('Subclasses a service, with lookup by class name.') + ->addArgument( + 'class_name', + InputArgument::REQUIRED, + 'the name of the class you wish to extend' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new class will be generated' + )->addOption( + 'extendfactory', + null, + InputOption::VALUE_NONE, + 'when set, subclass the factory; otherwise, use existing factory' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $class = $input->getArgument('class_name'); + $target = $input->getArgument('target_module'); + $extendFactory = $input->getOption('extendfactory'); + + try { + $this->generatorTools->setOutputInterface($output); + $this->generatorTools->extendClass( + $this->container, $class, $target, $extendFactory + ); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendServiceCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendServiceCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..d41a7ab42f4d3fd63bba47a6fe8e02bcb6b24177 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ExtendServiceCommand.php @@ -0,0 +1,97 @@ +<?php +/** + * Console command: extend service. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: extend service. + * + * @category VuFind + * @package Console + * @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 ExtendServiceCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/extendservice'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Service generator') + ->setHelp('Override a service with a new child class.') + ->addArgument( + 'config_path', + InputArgument::REQUIRED, + "the path to the service in the framework config\ne.g." + . ' controllers/factories/VuFind\\\\Controller\\\\AjaxController' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new class will be generated' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $source = $input->getArgument('config_path'); + $target = $input->getArgument('target_module'); + + try { + $this->generatorTools->setOutputInterface($output); + $this->generatorTools->extendService($source, $target); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..e63b066f548aca958638543a3879fc9667e1e4da --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommand.php @@ -0,0 +1,138 @@ +<?php +/** + * Console command: Generate non-tab record action route. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Console command: Generate non-tab record action route. + * + * @category VuFind + * @package Console + * @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 NonTabRecordActionCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/nontabrecordaction'; + + /** + * Main framework configuration + * + * @var array + */ + protected $mainConfig; + + /** + * Constructor + * + * @param GeneratorTools $tools Generator tools + * @param array $mainConfig Main framework configuration + * @param string|null $name The name of the command; passing null + * means it must be set in configure() + */ + public function __construct(GeneratorTools $tools, array $mainConfig, + $name = null + ) { + $this->mainConfig = $mainConfig; + parent::__construct($tools, $name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Non-tab record action route generator') + ->setHelp('Adds routes for a non-tab record action.') + ->addArgument( + 'action', + InputArgument::REQUIRED, + 'new action to add' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new routes will be generated' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $action = $input->getArgument('action'); + $module = $input->getArgument('target_module'); + + $this->generatorTools->setOutputInterface($output); + + // Create backup of configuration + $configPath = $this->generatorTools->getModuleConfigPath($module); + $this->generatorTools->backUpFile($configPath); + + // Append the routes + $config = include $configPath; + foreach ($this->mainConfig['router']['routes'] as $key => $val) { + if (isset($val['options']['route']) + && substr($val['options']['route'], -14) == '[:id[/[:tab]]]' + ) { + $newRoute = $key . '-' . strtolower($action); + if (isset($this->mainConfig['router']['routes'][$newRoute])) { + $output->writeln($newRoute . ' already exists; skipping.'); + } else { + $val['options']['route'] = str_replace( + '[:id[/[:tab]]]', "[:id]/$action", $val['options']['route'] + ); + $val['options']['defaults']['action'] = $action; + $config['router']['routes'][$newRoute] = $val; + } + } + } + + // Write updated configuration + $this->generatorTools->writeModuleConfig($configPath, $config); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..1fe99c09fc8e70de3fee9c09c5f0ff04bd93e784 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/NonTabRecordActionCommandFactory.php @@ -0,0 +1,65 @@ +<?php +/** + * Factory for non-tab record action route generator command. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; + +/** + * Factory for non-tab record action route generator command. + * + * @category VuFind + * @package Console + * @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 NonTabRecordActionCommandFactory extends AbstractCommandFactory +{ + /** + * 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 + ) { + $config = $container->get('Config'); + return parent::__invoke( + $container, $requestedName, array_merge([$config], $options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/PluginCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/PluginCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..ec5ad658913d4b73e774b3666e4de13cd601f88a --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/PluginCommand.php @@ -0,0 +1,94 @@ +<?php +/** + * Console command: Generate plugin. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: Generate plugin. + * + * @category VuFind + * @package Console + * @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 PluginCommand extends AbstractContainerAwareCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/plugin'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Plugin generator') + ->setHelp('Creates a new plugin class.') + ->addArgument( + 'class_name', + InputArgument::REQUIRED, + 'the name of the class you wish to create' + )->addArgument( + 'factory', + InputArgument::OPTIONAL, + 'an existing factory to use (omit to generate a new one)' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $class = $input->getArgument('class_name'); + $factory = $input->getArgument('factory'); + try { + $this->generatorTools->setOutputInterface($output); + $this->generatorTools->createPlugin($this->container, $class, $factory); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/RecordRouteCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/RecordRouteCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..51e523ea39d75d81a8ca4810c0fcaddde1e718e0 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/RecordRouteCommand.php @@ -0,0 +1,105 @@ +<?php +/** + * Console command: Generate record route. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: Generate record route. + * + * @category VuFind + * @package Console + * @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 RecordRouteCommand extends AbstractRouteCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/recordroute'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Record route generator') + ->setHelp('Adds a record route.') + ->addArgument( + 'base', + InputArgument::REQUIRED, + 'the base route name (used by router), e.g. record' + )->addArgument( + 'controller', + InputArgument::REQUIRED, + 'the controller name (used in URL), e.g. MyResearch' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new route will be generated' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $base = $input->getArgument('base'); + $controller = $input->getArgument('controller'); + $module = $input->getArgument('target_module'); + + $this->generatorTools->setOutputInterface($output); + + // Create backup of configuration + $configPath = $this->generatorTools->getModuleConfigPath($module); + $this->generatorTools->backUpFile($configPath); + + // Append the route + $config = include $configPath; + $this->routeGenerator->addRecordRoute($config, $base, $controller); + + // Write updated configuration + $this->generatorTools->writeModuleConfig($configPath, $config); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/StaticRouteCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/StaticRouteCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..9328ac7d65d86e79a34edb06ddeee796e88d5cfd --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/StaticRouteCommand.php @@ -0,0 +1,100 @@ +<?php +/** + * Console command: Generate static route. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: Generate static route. + * + * @category VuFind + * @package Console + * @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 StaticRouteCommand extends AbstractRouteCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/staticroute'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Static route generator') + ->setHelp('Adds a static route.') + ->addArgument( + 'route_definition', + InputArgument::REQUIRED, + 'a Controller/Action string, e.g. Search/Home' + )->addArgument( + 'target_module', + InputArgument::REQUIRED, + 'the module where the new route will be generated' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $route = $input->getArgument('route_definition'); + $module = $input->getArgument('target_module'); + + $this->generatorTools->setOutputInterface($output); + + // Create backup of configuration + $configPath = $this->generatorTools->getModuleConfigPath($module); + $this->generatorTools->backUpFile($configPath); + + // Append the route + $config = include $configPath; + $this->routeGenerator->addStaticRoute($config, $route); + + // Write updated configuration + $this->generatorTools->writeModuleConfig($configPath, $config); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..01566d3a18cd2c369c1a78cd6cb30e5dd2653392 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommand.php @@ -0,0 +1,92 @@ +<?php +/** + * Theme generator command. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Laminas\Config\Config; +use VuFindTheme\ThemeGenerator; + +/** + * Theme generator command. + * + * @category VuFind + * @package Console + * @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 ThemeCommand extends AbstractThemeCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/theme'; + + /** + * Type of resource being generated (used in help messages) + * + * @var string + */ + protected $type = 'theme'; + + /** + * Configuration from config.ini + * + * @var Config + */ + protected $config; + + /** + * Constructor + * + * @param ThemeGenerator $generator Generator to call + * @param Config $config Configuration from config.ini + * @param string|null $name The name of the command; passing null + * means it must be set in configure() + */ + public function __construct(ThemeGenerator $generator, Config $config, + $name = null + ) { + $this->config = $config; + parent::__construct($generator, $name); + } + + /** + * Run the generator. + * + * @param string $name Name of resource to generate + * + * @return bool + */ + protected function generate($name) + { + return parent::generate($name) + && $this->generator->configure($this->config, $name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..190e40cca9d1aafeed208bedec4143da790bbeaa --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeCommandFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Factory for theme generator command. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; + +/** + * Factory for theme generator command. + * + * @category VuFind + * @package Console + * @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 ThemeCommandFactory extends AbstractCommandFactory +{ + /** + * 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 + ) { + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + return new $requestedName( + $container->get(\VuFindTheme\ThemeGenerator::class), + $config, + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..ea0fc0c4c120060b3e8aa2503f30bf6eac602162 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommand.php @@ -0,0 +1,64 @@ +<?php +/** + * Theme mixin generator command. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Symfony\Component\Console\Command\Command; + +/** + * Theme mixin generator command. + * + * @category VuFind + * @package Console + * @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 ThemeMixinCommand extends AbstractThemeCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'generate/thememixin'; + + /** + * Type of resource being generated (used in help messages) + * + * @var string + */ + protected $type = 'theme mixin'; + + /** + * Extra text to append to the output when generation is successful. + * + * @var string + */ + protected $extraSuccessMessage + = 'Add to your theme.config.php \'mixins\' setting to activate.'; +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..9c447764816fdcbb3e4390ff8f3769f381494623 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Generate/ThemeMixinCommandFactory.php @@ -0,0 +1,65 @@ +<?php +/** + * Factory for theme mixin generator command. + * + * 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 Console + * @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 VuFindConsole\Command\Generate; + +use Interop\Container\ContainerInterface; + +/** + * Factory for theme mixin generator command. + * + * @category VuFind + * @package Console + * @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 ThemeMixinCommandFactory extends AbstractCommandFactory +{ + /** + * 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 + ) { + return new $requestedName( + $container->get(\VuFindTheme\MixinGenerator::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..5164e497ad1aa152e75d85a761e3c6af51f9d0fa --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommand.php @@ -0,0 +1,94 @@ +<?php +/** + * Console command: VuFind-specific customizations to OAI-PMH harvest command + * + * 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 Console + * @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 VuFindConsole\Command\Harvest; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: VuFind-specific customizations to OAI-PMH harvest command + * + * @category VuFind + * @package Console + * @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 HarvestOaiCommand extends \VuFindHarvest\OaiPmh\HarvesterCommand +{ + /** + * The name of the command + * + * @var string + */ + protected static $defaultName = 'harvest/harvest_oai'; + + /** + * Warn the user if VUFIND_LOCAL_DIR is not set. + * + * @param OutputInterface $output Output object + * + * @return void + */ + protected function checkLocalSetting(OutputInterface $output) + { + if (!getenv('VUFIND_LOCAL_DIR')) { + $output->writeln( + 'WARNING: The VUFIND_LOCAL_DIR environment variable is not set.' + ); + $output->writeln( + 'This should point to your local configuration directory (i.e.' + ); + $output->writeln(realpath(APPLICATION_PATH . '/local') . ').'); + $output->writeln( + 'Without it, inappropriate default settings may be loaded.' + ); + $output->writeln(''); + } + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->checkLocalSetting($output); + + // Add the default --ini setting if missing: + if (!$input->getOption('ini')) { + $ini = \VuFind\Config\Locator::getConfigPath('oai.ini', 'harvest'); + $input->setOption('ini', $ini); + } + return parent::execute($input, $output); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..c7c35e1bc2a203b0fa4f04930894fe5965e2f341 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/HarvestOaiCommandFactory.php @@ -0,0 +1,90 @@ +<?php +/** + * Factory for OAI harvest command. + * + * 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 Console + * @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 VuFindConsole\Command\Harvest; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for OAI harvest command. + * + * @category VuFind + * @package Console + * @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 HarvestOaiCommandFactory implements FactoryInterface +{ + /** + * Get the base directory for harvesting OAI-PMH data. + * + * @return string + */ + protected function getHarvestRoot() + { + // Get the base VuFind path: + $home = strlen(LOCAL_OVERRIDE_DIR) > 0 + ? LOCAL_OVERRIDE_DIR + : realpath(APPLICATION_PATH . '/..'); + + // Build the full harvest path: + $dir = $home . '/harvest/'; + + // Create the directory if it does not already exist: + if (!is_dir($dir) && !mkdir($dir)) { + throw new \Exception("Problem creating directory {$dir}."); + } + + return $dir; + } + + /** + * 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 + ) { + return new $requestedName( + $container->get(\VuFindHttp\HttpService::class)->createClient(), + $this->getHarvestRoot(), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Harvest/MergeMarcCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/MergeMarcCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8872e35186282ff628e91c8693a9bacf14ae5d62 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Harvest/MergeMarcCommand.php @@ -0,0 +1,107 @@ +<?php +/** + * Console command: Merge MARC records. + * + * 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 Console + * @author Thomas Schwaerzler <thomas.schwaerzler@uibk.ac.at> + * @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 VuFindConsole\Command\Harvest; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindConsole\Command\RelativeFileAwareCommand; + +/** + * Console command: Merge MARC records. + * + * @category VuFind + * @package Console + * @author Thomas Schwaerzler <thomas.schwaerzler@uibk.ac.at> + * @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 MergeMarcCommand extends RelativeFileAwareCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'harvest/merge-marc'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('MARC merge tool') + ->setHelp( + 'Merges harvested MARCXML files into a single <collection>; ' + . 'writes to stdout.' + )->addArgument( + 'directory', + InputArgument::REQUIRED, + 'a directory containing MARC XML files to merge' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $dir = rtrim($input->getArgument('directory'), '/'); + + if (!($handle = @opendir($dir))) { + $output->writeln("Cannot open directory: {$dir}"); + return 1; + } + + $output->writeln('<collection>'); + while (false !== ($file = readdir($handle))) { + // Only operate on XML files: + if (pathinfo($file, PATHINFO_EXTENSION) === "xml") { + // get file content + $filePath = $dir . '/' . $file; + $fileContent = file_get_contents($filePath); + + // output content: + $output->writeln("<!-- $filePath -->"); + $output->write($fileContent); + } + } + $output->writeln('</collection>'); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..adad67904a042b6e104a4f5eeaba52dd457988b5 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommand.php @@ -0,0 +1,147 @@ +<?php +/** + * Console command: XSLT importer + * + * 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 Console + * @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 VuFindConsole\Command\Import; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\XSLT\Importer; +use VuFindConsole\Command\RelativeFileAwareCommand; + +/** + * Console command: XSLT importer + * + * @category VuFind + * @package Console + * @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 ImportXslCommand extends RelativeFileAwareCommand +{ + /** + * The name of the command + * + * @var string + */ + protected static $defaultName = 'import/import-xsl'; + + /** + * XSLT importer + * + * @var Importer + */ + protected $importer; + + /** + * Constructor + * + * @param Importer $importer XSLT importer + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Importer $importer, $name = null) + { + $this->importer = $importer; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('XSLT importer') + ->setHelp('Indexes XML into Solr using XSLT.') + ->addArgument( + 'XML_file', + InputArgument::REQUIRED, + 'source file to index' + )->addArgument( + 'properties_file', + InputArgument::REQUIRED, + 'import configuration file ($VUFIND_LOCAL_DIR/import and ' + . ' $VUFIND_HOME/import will' + . "\nbe searched for this filename; see ojs.properties " + . 'for configuration examples)' + )->addOption( + 'test-only', + null, + InputOption::VALUE_NONE, + 'activates test mode, which displays transformed XML without ' + . 'updating Solr' + )->addOption( + 'index', + null, + InputOption::VALUE_OPTIONAL, + 'name of search backend to index content into (could be overridden ' + . "with,\nfor example, SolrAuth to index authority records)", + 'Solr' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $testMode = $input->getOption('test-only') ? true : false; + $index = $input->getOption('index'); + $xml = $input->getArgument('XML_file'); + $properties = $input->getArgument('properties_file'); + // Try to import the document if successful: + try { + $result = $this->importer->save($xml, $properties, $index, $testMode); + if ($testMode) { + $output->writeln($result); + } + } catch (\Exception $e) { + $output->writeln('Fatal error: ' . $e->getMessage()); + if (is_callable([$e, 'getPrevious']) && $e = $e->getPrevious()) { + while ($e) { + $output->writeln('Previous exception: ' . $e->getMessage()); + $e = $e->getPrevious(); + } + } + return 1; + } + if (!$testMode) { + $output->writeln("Successfully imported $xml..."); + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5be0a27041de63e7afd43485394d28d9abeee46d --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Import/ImportXslCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for XSLT import command. + * + * 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 Console + * @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 VuFindConsole\Command\Import; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for XSLT import command. + * + * @category VuFind + * @package Console + * @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 ImportXslCommandFactory 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 + ) { + return new $requestedName( + new \VuFind\XSLT\Importer($container), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..f39d937116979bc6f8d05074890b30adac62b0e4 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommand.php @@ -0,0 +1,255 @@ +<?php +/** + * Console command: web crawler + * + * 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 Console + * @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 VuFindConsole\Command\Import; + +use Laminas\Config\Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Solr\Writer; +use VuFind\XSLT\Importer; + +/** + * Console command: web crawler + * + * @category VuFind + * @package Console + * @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 WebCrawlCommand extends Command +{ + /** + * The name of the command + * + * @var string + */ + protected static $defaultName = 'import/webcrawl'; + + /** + * XSLT importer + * + * @var Importer + */ + protected $importer; + + /** + * Solr writer + * + * @var Writer + */ + protected $solr; + + /** + * Configuration from webcrawl.ini + * + * @var Config + */ + protected $config; + + /** + * Constructor + * + * @param Importer $importer XSLT importer + * @param Writer $solr Solr writer + * @param Config $config Configuration from webcrawl.ini + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Importer $importer, Writer $solr, Config $config, + $name = null + ) { + $this->importer = $importer; + $this->solr = $solr; + $this->config = $config; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Web crawler') + ->setHelp('Crawls websites to populate VuFind\'s web index.') + ->addOption( + 'test-only', + null, + InputOption::VALUE_NONE, + 'activates test mode, which displays output without updating Solr' + )->addOption( + 'index', + null, + InputOption::VALUE_OPTIONAL, + 'name of search backend to index content into', + 'SolrWeb' + ); + } + + /** + * Download a URL to a temporary file. + * + * @param string $url URL to download + * + * @return string Filename of downloaded content + */ + protected function downloadFile($url) + { + $file = tempnam('/tmp', 'sitemap'); + file_put_contents($file, file_get_contents($url)); + return $file; + } + + /** + * Remove a temporary file. + * + * @param string $file Name of file to delete + * + * @return void + */ + protected function removeTempFile($file) + { + unlink($file); + } + + /** + * Process a sitemap URL, either harvesting its contents directly or recursively + * reading in child sitemaps. + * + * @param OutputInterface $output Output object + * @param string $url URL of sitemap to read. + * @param bool $verbose Are we in verbose mode? + * @param string $index Solr index to update + * @param bool $testMode Are we in test mode? + * + * @return bool True on success, false on error. + */ + protected function harvestSitemap(OutputInterface $output, $url, + $verbose = false, $index = 'SolrWeb', $testMode = false + ) { + if ($verbose) { + $output->writeln("Harvesting $url..."); + } + + $retVal = true; + + $file = $this->downloadFile($url); + $xml = simplexml_load_file($file); + if ($xml) { + // Are there any child sitemaps? If so, pull them in: + $results = $xml->sitemap ?? []; + foreach ($results as $current) { + if (isset($current->loc)) { + $success = $this->harvestSitemap( + $output, (string)$current->loc, $verbose, $index, $testMode + ); + if (!$success) { + $retVal = false; + } + } + } + // Only import the current sitemap if it contains URLs! + if (isset($xml->url)) { + try { + $result = $this->importer->save( + $file, 'sitemap.properties', $index, $testMode + ); + if ($testMode) { + $output->writeln($result); + } + } catch (\Exception $e) { + if ($verbose) { + $output->writeln(get_class($e) . ': ' . $e->getMessage()); + } + $retVal = false; + } + } + } + $this->removeTempFile($file); + return $retVal; + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Get command line parameters: + $testMode = $input->getOption('test-only') ? true : false; + $index = $input->getOption('index'); + + // Get the time we started indexing -- we'll delete records older than this + // date after everything is finished. Note that we subtract a few seconds + // for safety. + $startTime = date('Y-m-d\TH:i:s\Z', time() - 5); + + // Are we in verbose mode? + $verbose = ($this->config->General->verbose ?? false) + || ($input->hasOption('verbose') && $input->getOption('verbose')); + + // Loop through sitemap URLs in the config file. + $error = false; + foreach ($this->config->Sitemaps->url as $current) { + $error = $error || !$this->harvestSitemap( + $output, $current, $verbose, $index, $testMode + ); + } + if ($error) { + $output->writeln("Error encountered during harvest."); + } + + // Skip Solr operations if we're in test mode. + if (!$testMode) { + if ($verbose) { + $output->writeln("Deleting old records (prior to $startTime)..."); + } + // Perform the delete of outdated records: + $this->solr + ->deleteByQuery($index, 'last_indexed:[* TO ' . $startTime . ']'); + if ($verbose) { + $output->writeln('Committing...'); + } + $this->solr->commit($index); + if ($verbose) { + $output->writeln('Optimizing...'); + } + $this->solr->optimize($index); + } + return $error ? 1 : 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..6c9dda49774c9a9e7a140a41aeb57f9daf2276ea --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Import/WebCrawlCommandFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Factory for web crawl command. + * + * 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 Console + * @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 VuFindConsole\Command\Import; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for web crawl command. + * + * @category VuFind + * @package Console + * @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 WebCrawlCommandFactory 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 + ) { + $configLoader = $container->get(\VuFind\Config\PluginManager::class); + return new $requestedName( + new \VuFind\XSLT\Importer($container), + $container->get(\VuFind\Solr\Writer::class), + $configLoader->get('webcrawl'), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Install/InstallCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Install/InstallCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8f53eb805711177e138527af61a0a0b346305de2 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Install/InstallCommand.php @@ -0,0 +1,932 @@ +<?php +/** + * Console command: VuFind installer. + * + * 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 Console + * @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 VuFindConsole\Command\Install; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; + +/** + * Console command: VuFind installer. + * + * @category VuFind + * @package Console + * @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 InstallCommand extends Command +{ + const MULTISITE_NONE = 0; + const MULTISITE_DIR_BASED = 1; + const MULTISITE_HOST_BASED = 2; + + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'install/install'; + + /** + * Base directory of VuFind installation. + * + * @var string + */ + protected $baseDir; + + /** + * Local settings directory for VuFind installation. + * + * @var string + */ + protected $overrideDir; + + /** + * Hostname of VuFind installation (used for host-based multi-site). + * + * @var string + */ + protected $host = ''; + + /** + * Custom local code module name (if any). + * + * @var string + */ + protected $module = ''; + + /** + * Active multi-site mode. + * + * @var int + */ + protected $multisiteMode = self::MULTISITE_NONE; + + /** + * Base path for VuFind URLs. + * + * @var string + */ + protected $basePath = '/vufind'; + + /** + * Constructor + * + * @param string|null $name The name of the command; passing null means it must + * be set in configure() + */ + public function __construct($name = null) + { + $this->baseDir = str_replace( + '\\', '/', realpath(__DIR__ . '/../../../../../../') + ); + $this->overrideDir = $this->baseDir . '/local'; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('VuFind installer') + ->setHelp('Set up (or modify) initial VuFind installation.') + ->addOption( + 'use-defaults', + null, + InputOption::VALUE_NONE, + 'Use VuFind defaults to configure ' + . '(ignores any other arguments passed)' + )->addOption( + 'overridedir', + null, + InputOption::VALUE_REQUIRED, + 'Where would you like to store your local settings?' + . " (defaults to {$this->overrideDir} when --non-interactive is set)" + )->addOption( + 'module-name', + null, + InputOption::VALUE_REQUIRED, + 'What module name would you like to use? Specify "disabled" to skip' + )->addOption( + 'basepath', + null, + InputOption::VALUE_REQUIRED, + 'What base path should be used in VuFind\'s URL?' + . " (defaults to {$this->baseDir} when --non-interactive is set)" + )->addOption( + 'multisite', + null, + InputOption::VALUE_OPTIONAL, + 'Specify we are going to setup a multisite. ' + . 'Options: directory and host', + false + )->addOption( + 'hostname', + null, + InputOption::VALUE_REQUIRED, + 'Specify the hostname for the VuFind Site, when multisite=host' + )->addOption( + 'non-interactive', + null, + InputOption::VALUE_NONE, + 'Use settings if provided via arguments, otherwise use defaults' + ); + } + + /** + * Write file contents to disk. + * + * @param string $filename Filename + * @param string $content Content + * + * @return bool + */ + protected function writeFileToDisk($filename, $content) + { + return @file_put_contents($filename, $content); + } + + /** + * Get instructions for editing the Apache configuration under Windows. + * + * @return string + */ + protected function getWindowsApacheMessage() + { + return "Go to Start -> Apache HTTP Server -> Edit the Apache httpd.conf\n" + . "and add this line to your httpd.conf file: \n" + . " Include {$this->overrideDir}/httpd-vufind.conf\n\n" + . "If you are using a bundle like XAMPP and do not have this start\n" + . "menu option, you should find and edit your httpd.conf file manually\n" + . "(usually in a location like c:\\xampp\\apache\\conf).\n"; + } + + /** + * Get instructions for editing the Apache configuration under Linux. + * + * @return string + */ + protected function getLinuxApacheMessage() + { + if (is_dir('/etc/httpd/conf.d')) { // Mandriva / RedHat + $confD = '/etc/httpd/conf.d'; + $httpdConf = '/etc/httpd/conf/httpd.conf'; + } elseif (is_dir('/etc/apache2/2.2/conf.d')) { // Solaris + $confD = '/etc/apache2/2.2/conf.d'; + $httpdConf = '/etc/apache2/2.2/httpd.conf'; + } elseif (is_dir('/etc/apache2/conf-enabled')) { // new Ubuntu / OpenSUSE + $confD = '/etc/apache2/conf-enabled'; + $httpdConf = '/etc/apache2/apache2.conf'; + } elseif (is_dir('/etc/apache2/conf.d')) { // old Ubuntu / OpenSUSE + $confD = '/etc/apache2/conf.d'; + $httpdConf = '/etc/apache2/httpd.conf'; + } elseif (is_dir('/opt/local/apache2/conf/extra')) { // Mac with Mac Ports + $confD = '/opt/local/apache2/conf/extra'; + $httpdConf = '/opt/local/apache2/conf/httpd.conf'; + } else { + $confD = '/path/to/apache/conf.d'; + $httpdConf = false; + } + + // Check if httpd.conf really exists before recommending a specific path; + // if missing, just use the generic name: + $httpdConf = ($httpdConf && file_exists($httpdConf)) + ? $httpdConf : 'httpd.conf'; + + // Suggest a symlink name based on the local directory, so if running in + // multisite mode, we don't use the same symlink for multiple instances: + $symlink = basename($this->overrideDir); + $symlink = ($symlink == 'local') ? 'vufind' : ('vufind-' . $symlink); + $symlink .= '.conf'; + + return "You can do it in either of two ways:\n\n" + . " a) Add this line to your {$httpdConf} file:\n" + . " Include {$this->overrideDir}/httpd-vufind.conf\n\n" + . " b) Link the configuration to Apache's config directory like this:" + . "\n ln -s {$this->overrideDir}/httpd-vufind.conf " + . "{$confD}/{$symlink}\n" + . "\nOption b is preferable if your platform supports it,\n" + . "but option a is more certain to be supported.\n"; + } + + /** + * Display system-specific information for where configuration files are found + * and/or symbolic links should be created. + * + * @param OutputInterface $output Output object + * + * @return void + */ + protected function getApacheLocation(OutputInterface $output) + { + // There is one special case for Windows, and a variety of different + // Unix-flavored possibilities that all work similarly. + $msg = (strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN') + ? $this->getWindowsApacheMessage() : $this->getLinuxApacheMessage(); + $output->writeln($msg); + } + + /** + * Validate a base path. Returns true on success, message on failure. + * + * @param string $basePath String to validate. + * @param bool $allowEmpty Are empty values acceptable? + * + * @return bool|string + */ + protected function validateBasePath($basePath, $allowEmpty = false) + { + if ($allowEmpty && empty($basePath)) { + return true; + } + return preg_match('/^\/\w*$/', $basePath) + ? true + : 'Error: Base path must be alphanumeric and start with a slash.'; + } + + /** + * Get a base path from the user (or return a default). + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return string + */ + protected function getBasePath(InputInterface $input, OutputInterface $output) + { + // Get VuFind base path: + while (true) { + $basePathInput = $this->getInput( + $input, $output, + "What base path should be used in VuFind's URL? [{$this->basePath}] " + ); + if (empty($basePathInput)) { + return $this->basePath; + } elseif (($result = $this->validateBasePath($basePathInput)) === true) { + return $basePathInput; + } + $output->writeln($result); + } + } + + /** + * Initialize the override directory and report success or failure. + * + * @param string $dir Path to attempt to initialize + * + * @return void + */ + protected function initializeOverrideDir($dir) + { + return $this->buildDirs( + [ + $dir, + $dir . '/cache', + $dir . '/config', + $dir . '/harvest', + $dir . '/import' + ] + ); + } + + /** + * Get an override directory from the user (or return a default). + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return string + */ + protected function getOverrideDir(InputInterface $input, OutputInterface $output) + { + // Get override directory path: + while (true) { + $overrideDirInput = $this->getInput( + $input, $output, + 'Where would you like to store your local settings? ' + . "[{$this->overrideDir}] " + ); + if (empty($overrideDirInput)) { + return $this->overrideDir; + } elseif (!$this->initializeOverrideDir($overrideDirInput)) { + $output->writeln( + "Error: Cannot initialize settings in '$overrideDirInput'.\n" + ); + } + return str_replace('\\', '/', realpath($overrideDirInput)); + } + } + + /** + * Validate a comma-separated list of module names. Returns true on success, + * message on failure. + * + * @param string $modules Module names to validate. + * + * @return bool|string + */ + protected function validateModules($modules) + { + foreach (explode(',', $modules) as $module) { + $result = $this->validateModule(trim($module)); + if ($result !== true) { + return $result; + } + } + return true; + } + + /** + * Validate the custom module name. Returns true on success, message on failure. + * + * @param string $module Module name to validate. + * + * @return bool|string + */ + protected function validateModule($module) + { + $regex = '/^[a-zA-Z][0-9a-zA-Z_]*$/'; + $illegalModules = [ + 'VuFind', 'VuFindAdmin', 'VuFindConsole', 'VuFindDevTools', + 'VuFindLocalTemplate', 'VuFindSearch', 'VuFindTest', 'VuFindTheme', + ]; + if (in_array($module, $illegalModules)) { + return "{$module} is a reserved module name; please try another."; + } elseif (empty($module) || preg_match($regex, $module)) { + return true; + } + return "Illegal name: {$module}; please use alphanumeric text."; + } + + /** + * Get the custom module name from the user (or blank for none). + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return string + */ + protected function getModule(InputInterface $input, OutputInterface $output) + { + // Get custom module name: + $output->writeln( + "\nVuFind supports use of a custom module for storing local code " + . "changes.\nIf you do not plan to customize the code, you can " + . "skip this step.\nIf you decide to use a custom module, the name " + . "you choose will be used for\nthe module's directory name and its " + . "PHP namespace." + ); + while (true) { + $moduleInput = trim( + $this->getInput( + $input, $output, + "\nWhat module name would you like to use? [blank for none] " + ) + ); + if (($result = $this->validateModules($moduleInput)) === true) { + return $moduleInput; + } + $output->writeln("\n$result"); + } + } + + /** + * Get the user's preferred multisite mode. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int + */ + protected function getMultisiteMode(InputInterface $input, + OutputInterface $output + ) { + $output->writeln( + "\nWhen running multiple VuFind sites against a single installation, you" + . " need\nto decide how to distinguish between instances. Choose an " + . "option:\n\n" . self::MULTISITE_DIR_BASED . ".) Directory-based " + . "(i.e. http://server/vufind1 vs. http://server/vufind2)\n" + . self::MULTISITE_HOST_BASED + . ".) Host-based (i.e. http://vufind1.server vs. http://vufind2.server)" + . "\n\nor enter " . self::MULTISITE_NONE . " to disable multisite mode." + ); + $legal = [ + self::MULTISITE_NONE, + self::MULTISITE_DIR_BASED, + self::MULTISITE_HOST_BASED + ]; + while (true) { + $response = $this->getInput( + $input, $output, "\nWhich option do you want? " + ); + if (is_numeric($response) && in_array(intval($response), $legal)) { + return intval($response); + } + $output->writeln("Invalid selection."); + } + } + + /** + * Validate the user's hostname input. Returns true on success, message on + * failure. + * + * @param string $host String to check + * + * @return bool|string + */ + protected function validateHost($host) + { + // From http://stackoverflow.com/questions/106179/ + // regular-expression-to-match-hostname-or-ip-address + $valid = "/^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*" + . "([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$/"; + return preg_match($valid, $host) + ? true + : 'Invalid hostname.'; + } + + /** + * Get the user's hostname preference. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return string + */ + protected function getHost(InputInterface $input, OutputInterface $output) + { + while (true) { + $response = $this->getInput( + $input, $output, "\nPlease enter the hostname for your site: " + ); + if (($result = $this->validateHost($response)) === true) { + return $response; + } + $output->writeln($result); + } + } + + /** + * Fetch a single line of input from the user. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * @param string $prompt Prompt to display to the user. + * + * @return string User-entered response. + */ + protected function getInput(InputInterface $input, OutputInterface $output, + string $prompt + ): string { + $question = new Question($prompt, ''); + return $this->getHelper('question')->ask($input, $output, $question); + } + + /** + * Generate the Apache configuration. Returns true on success, error message + * otherwise. + * + * @param OutputInterface $output Output object + * + * @return bool|string + */ + protected function buildApacheConfig(OutputInterface $output) + { + $baseConfig = $this->baseDir . '/config/vufind/httpd-vufind.conf'; + $config = @file_get_contents($baseConfig); + if (empty($config)) { + return "Problem reading {$baseConfig}."; + } + $config = str_replace('/usr/local/vufind/local', '%override-dir%', $config); + $config = str_replace('/usr/local/vufind', '%base-dir%', $config); + $config = preg_replace('|([^/])\/vufind|', '$1%base-path%', $config); + $config = str_replace('%override-dir%', $this->overrideDir, $config); + $config = str_replace('%base-dir%', $this->baseDir, $config); + $config = str_replace('%base-path%', $this->basePath, $config); + // Special cases for root basePath: + if ('/' == $this->basePath) { + $config = str_replace('//', '/', $config); + $config = str_replace('Alias /', '#Alias /', $config); + } + if (!empty($this->module)) { + $config = str_replace( + "#SetEnv VUFIND_LOCAL_MODULES VuFindLocalTemplate", + "SetEnv VUFIND_LOCAL_MODULES {$this->module}", $config + ); + } + + // In multisite mode, we need to make environment variables conditional: + switch ($this->multisiteMode) { + case self::MULTISITE_DIR_BASED: + $config = preg_replace( + '/SetEnv\s+(\w+)\s+(.*)/', + 'SetEnvIf Request_URI "^' . $this->basePath . '" $1=$2', + $config + ); + break; + case self::MULTISITE_HOST_BASED: + if (($result = $this->validateHost($this->host)) !== true) { + return $result; + } + $config = preg_replace( + '/SetEnv\s+(\w+)\s+(.*)/', + 'SetEnvIfNoCase Host ' . str_replace('.', '\.', $this->host) + . ' $1=$2', + $config + ); + break; + } + + $target = $this->overrideDir . '/httpd-vufind.conf'; + if (file_exists($target)) { + $bak = $target . '.bak.' . time(); + copy($target, $bak); + $output->writeln("Backed up existing Apache configuration to $bak."); + } + return $this->writeFileToDisk($target, $config) + ? true : "Problem writing {$this->overrideDir}/httpd-vufind.conf."; + } + + /** + * Build the Windows-specific startup configuration. Returns true on success, + * error message otherwise. + * + * @return bool|string + */ + protected function buildWindowsConfig() + { + $module = empty($this->module) + ? '' : "@set VUFIND_LOCAL_MODULES={$this->module}\n"; + $batch = "@set VUFIND_HOME={$this->baseDir}\n" + . "@set VUFIND_LOCAL_DIR={$this->overrideDir}\n" . $module; + return $this->writeFileToDisk($this->baseDir . '/env.bat', $batch) + ? true : "Problem writing {$this->baseDir}/env.bat."; + } + + /** + * Configure a SolrMarc properties file. Returns true on success, error message + * otherwise. + * + * @param OutputInterface $output Output object + * @param string $filename The properties file to configure + * + * @return bool|string + */ + protected function buildImportConfig(OutputInterface $output, $filename) + { + $target = $this->overrideDir . '/import/' . $filename; + if (file_exists($target)) { + $output->writeln( + "Warning: $target already exists; skipping file creation." + ); + return true; + } + $import = @file_get_contents($this->baseDir . '/import/' . $filename); + $import = str_replace("/usr/local/vufind", $this->baseDir, $import); + $import = preg_replace( + "/^\s*solrmarc.path\s*=.*$/m", + "solrmarc.path = {$this->overrideDir}/import|{$this->baseDir}/import", + $import + ); + if (!$this->writeFileToDisk($target, $import)) { + return "Problem writing {$this->overrideDir}/import/{$filename}."; + } + return true; + } + + /** + * Build a set of directories. + * + * @param array $dirs Directories to build + * + * @return bool|string True on success, name of problem directory on failure + */ + protected function buildDirs($dirs) + { + foreach ($dirs as $dir) { + if (!is_dir($dir) && !@mkdir($dir)) { + return $dir; + } + } + return true; + } + + /** + * Make sure all modules exist (and create them if they do not). Returns true + * on success, error message otherwise. + * + * @return bool|string + */ + protected function buildModules() + { + if (!empty($this->module)) { + foreach (explode(',', $this->module) as $module) { + $moduleDir = $this->baseDir . '/module/' . $module; + // Is module missing? If so, create it from the template: + if (!file_exists($moduleDir . '/Module.php')) { + if (($result = $this->buildModule($module)) !== true) { + return $result; + } + } + } + } + return true; + } + + /** + * Build the module for storing local code changes. Returns true on success, + * error message otherwise. + * + * @param string $module The name of the new module (assumed valid!) + * + * @return bool|string + */ + protected function buildModule($module) + { + // Create directories: + $moduleDir = $this->baseDir . '/module/' . $module; + $dirStatus = $this->buildDirs( + [ + $moduleDir, + $moduleDir . '/config', + $moduleDir . '/src', + $moduleDir . '/src/' . $module + ] + ); + if ($dirStatus !== true) { + return "Problem creating {$dirStatus}."; + } + + // Copy configuration: + $configFile = $this->baseDir + . '/module/VuFindLocalTemplate/config/module.config.php'; + $config = @file_get_contents($configFile); + if (!$config) { + return "Problem reading {$configFile}."; + } + $success = $this->writeFileToDisk( + $moduleDir . '/config/module.config.php', + str_replace('VuFindLocalTemplate', $module, $config) + ); + if (!$success) { + return "Problem writing {$moduleDir}/config/module.config.php."; + } + + // Copy PHP code: + $moduleFile = $this->baseDir . '/module/VuFindLocalTemplate/Module.php'; + $contents = @file_get_contents($moduleFile); + if (!$contents) { + return "Problem reading {$moduleFile}."; + } + $success = $this->writeFileToDisk( + $moduleDir . '/Module.php', + str_replace('VuFindLocalTemplate', $module, $contents) + ); + return $success ? true : "Problem writing {$moduleDir}/Module.php."; + } + + /** + * Display an error message and return a failure status. + * + * @param OutputInterface $output Output object + * @param string $msg Error message + * @param int $status Error status + * + * @return int + */ + protected function failWithError(OutputInterface $output, string $msg, + int $status = 1 + ): int { + $output->writeln($msg); + return $status; + } + + /** + * Display the final message after successful installation. + * + * @param OutputInterface $output Output object + * + * @return void + */ + protected function displaySuccessMessage(OutputInterface $output) + { + $output->writeln( + "Apache configuration written to {$this->overrideDir}/httpd-vufind.conf." + . "\n\nYou now need to load this configuration into Apache." + ); + $this->getApacheLocation($output); + if (!empty($this->host)) { + $output->writeln( + "Since you are using a host-based multisite configuration, you will " + . "also \nneed to do some virtual host configuration. See\n" + . " http://httpd.apache.org/docs/2.4/vhosts/\n" + ); + } + if ('/' == $this->basePath) { + $output->writeln( + "Since you are installing VuFind at the root of your domain, you " + . "will also\nneed to edit your Apache configuration to change " + . "DocumentRoot to:\n" . $this->baseDir . "/public\n" + ); + } + $output->writeln( + "Once the configuration is linked, restart Apache. You should now be " + . "able\nto access VuFind at http://localhost{$this->basePath}\n\nFor " + . "proper use of command line tools, you should also ensure that your\n" + ); + $finalMsg = empty($this->addOptionmodule) + ? "VUFIND_HOME and VUFIND_LOCAL_DIR environment variables are set to\n" + . "{$this->baseDir} and {$this->overrideDir} respectively." + : "VUFIND_HOME, VUFIND_LOCAL_MODULES and VUFIND_LOCAL_DIR environment\n" + . "variables are set to {$this->baseDir}, {$this->module} and " + . "{$this->overrideDir} respectively."; + $output->writeln($finalMsg); + } + + /** + * Collect input parameters, and return a status (0 = proceed, 1 = fail). + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function collectParameters(InputInterface $input, + OutputInterface $output + ) { + // Are we allowing user interaction? + $interactive = !$input->getOption('non-interactive'); + $userInputNeeded = []; + + // Load user settings if we are not forcing defaults: + if (!$input->getOption('use-defaults')) { + $overrideDir = trim($input->getOption('overridedir')); + if (!empty($overrideDir)) { + $this->overrideDir = $overrideDir; + } elseif ($interactive) { + $userInputNeeded['overrideDir'] = true; + } + $moduleName = trim($input->getOption('module-name')); + if (!empty($moduleName) && $moduleName !== 'disabled') { + if (($result = $this->validateModules($moduleName)) !== true) { + return $this->failWithError($output, $result); + } + $this->module = $moduleName; + } elseif ($interactive) { + $userInputNeeded['module'] = true; + } + + $basePath = trim($input->getOption('basepath')); + if (!empty($basePath)) { + if (($result = $this->validateBasePath($basePath, true)) !== true) { + return $this->failWithError($output, $result); + } + $this->basePath = $basePath; + } elseif ($interactive) { + $userInputNeeded['basePath'] = true; + } + + // We assume "single site" mode unless the --multisite option is set; + // note that $mode will be null if the user provided the option with + // no value specified, and false if the user did not provide the option. + $mode = $input->getOption('multisite'); + if ($mode === 'directory') { + $this->multisiteMode = self::MULTISITE_DIR_BASED; + } elseif ($mode === 'host') { + $this->multisiteMode = self::MULTISITE_HOST_BASED; + } elseif ($mode !== true && $mode !== null && $mode !== false) { + return $this->failWithError( + $output, 'Unexpected multisite mode: ' . $mode + ); + } elseif ($interactive && $mode !== false) { + $userInputNeeded['multisiteMode'] = true; + } + + // Now that we've validated as many parameters as possible, retrieve + // user input where needed. + if (isset($userInputNeeded['overrideDir'])) { + $this->overrideDir = $this->getOverrideDir($input, $output); + } + if (isset($userInputNeeded['module'])) { + $this->module = $this->getModule($input, $output); + } + if (isset($userInputNeeded['basePath'])) { + $this->basePath = $this->getBasePath($input, $output); + } + if (isset($userInputNeeded['multisiteMode'])) { + $this->multisiteMode = $this->getMultisiteMode($input, $output); + } + + // Load supplemental multisite parameters: + if ($this->multisiteMode == self::MULTISITE_HOST_BASED) { + $hostOption = trim($input->getOption('hostname')); + $this->host = (!empty($hostOption) || !$interactive) + ? $hostOption : $this->getHost($input, $output); + } + } + + // Normalize the module setting to remove whitespace: + $this->module = preg_replace('/\s/', '', $this->module); + + return 0; + } + + /** + * Process collected parameters, and return a status (0 = proceed, 1 = fail). + * + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function processParameters(OutputInterface $output) + { + // Make sure the override directory is initialized (using defaults or CLI + // parameters will not have initialized it yet; attempt to reinitialize it + // here is harmless if it was already initialized in interactive mode): + if (!$this->initializeOverrideDir($this->overrideDir)) { + return $this->failWithError( + $output, + "Cannot initialize local override directory: {$this->overrideDir}" + ); + } + + // Build the Windows start file in case we need it: + if (($result = $this->buildWindowsConfig()) !== true) { + return $this->failWithError($output, $result); + } + + // Build the import configuration: + foreach (['import.properties', 'import_auth.properties'] as $file) { + if (($result = $this->buildImportConfig($output, $file)) !== true) { + return $this->failWithError($output, $result); + } + } + + // Build the custom module(s), if necessary: + if (($result = $this->buildModules()) !== true) { + return $this->failWithError($output, $result); + } + + // Build the final configuration: + if (($result = $this->buildApacheConfig($output)) !== true) { + return $this->failWithError($output, $result); + } + return 0; + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln("VuFind has been found in {$this->baseDir}."); + + // Collect and process parameters, and stop if an error is encountered + // along the way.... + if ($this->collectParameters($input, $output) !== 0 + || $this->processParameters($output) !== 0 + ) { + return 1; + } + + // Report success: + $this->displaySuccessMessage($output); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..5c73eea3028aed9de31b143596049bda1cd8b9db --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommand.php @@ -0,0 +1,176 @@ +<?php +/** + * Abstract base class for language commands. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; + +/** + * Abstract base class for language commands. + * + * @category VuFind + * @package Console + * @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 AbstractCommand extends Command +{ + /** + * Normalizer for .ini files + * + * @var ExtendedIniNormalizer + */ + protected $normalizer; + + /** + * Reader for .ini files + * + * @var ExtendedIniReader + */ + protected $reader; + + /** + * Constructor + * + * @param ExtendedIniNormalizer $normalizer Normalizer for .ini files + * @param ExtendedIniReader $reader Reader for .ini files + * @param string $languageDir Base language file directory + * @param string|null $name The name of the command; passing + * null means it must be set in configure() + */ + public function __construct(ExtendedIniNormalizer $normalizer = null, + ExtendedIniReader $reader = null, $languageDir = null, $name = null + ) { + $this->normalizer = $normalizer ?? new ExtendedIniNormalizer(); + $this->reader = $reader ?? new ExtendedIniReader(); + $this->languageDir = $languageDir + ?? realpath(__DIR__ . '/../../../../../../languages'); + parent::__construct($name); + } + + /** + * Add a line to a language file + * + * @param string $filename File to update + * @param string $key Name of language key + * @param string $value Value of translation + * + * @return void + */ + protected function addLineToFile($filename, $key, $value) + { + $fHandle = fopen($filename, "a"); + if (!$fHandle) { + throw new \Exception('Cannot open ' . $filename . ' for writing.'); + } + fputs($fHandle, "\n$key = \"" . $value . "\"\n"); + fclose($fHandle); + } + + /** + * Extract a text domain and key from a raw language key. + * + * @param string $raw Raw language key + * + * @return array [textdomain, key] + */ + protected function extractTextDomain($raw) + { + $parts = explode('::', $raw, 2); + return count($parts) > 1 ? $parts : ['default', $raw]; + } + + /** + * Open the language directory as an object using dir(). Return false on + * failure. + * + * @param OutputInterface $output Output object + * @param string $domain Text domain to retrieve. + * @param bool $createIfMissing Should we create a missing directory? + * + * @return object|bool + */ + protected function getLangDir(OutputInterface $output, $domain = 'default', + $createIfMissing = false + ) { + $subDir = $domain == 'default' ? '' : ('/' . $domain); + $langDir = $this->languageDir . $subDir; + if ($createIfMissing && !is_dir($langDir)) { + mkdir($langDir); + } + $dir = dir(realpath($langDir)); + if (!$dir) { + $output->writeln("Could not open directory $langDir"); + return false; + } + return $dir; + } + + /** + * Create empty files if they do not already exist. + * + * @param string $path Directory path + * @param array $files Filenames to create in directory + * + * @return void + */ + protected function createMissingFiles($path, $files) + { + foreach ($files as $file) { + if (!file_exists($path . '/' . $file)) { + file_put_contents($path . '/' . $file, ''); + } + } + } + + /** + * Process a language directory. + * + * @param object $dir Directory object from dir() to process + * @param Callable $callback Function to run on all .ini files in $dir + * @param bool $statusCallback Callback function to display status messages + * (omit to suppress messages) + * + * @return void + */ + protected function processDirectory($dir, $callback, $statusCallback = false) + { + while ($file = $dir->read()) { + // Only process .ini files, and ignore native.ini special case file: + if (substr($file, -4) == '.ini' && $file !== 'native.ini') { + if (is_callable($statusCallback)) { + $statusCallback("Processing $file..."); + } + $callback($dir->path . '/' . $file); + } + } + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4e3bcfb5d48973eea430310f1bf8ba28f04804d2 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/AbstractCommandFactory.php @@ -0,0 +1,69 @@ +<?php +/** + * Shared factory for language commands. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; + +/** + * Shared factory for language commands. + * + * @category VuFind + * @package Console + * @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 AbstractCommandFactory 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 + ) { + return new $requestedName( + new ExtendedIniNormalizer(), + new ExtendedIniReader(), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/AddUsingTemplateCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/AddUsingTemplateCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8cd2ed8412ef0012b806767599cd0b48744e1235 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/AddUsingTemplateCommand.php @@ -0,0 +1,149 @@ +<?php +/** + * Language command: add string using template. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Language command: add string using template. + * + * @category VuFind + * @package Console + * @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 AddUsingTemplateCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'language/addusingtemplate'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Template-based string builder') + ->setHelp( + 'Builds new language strings from existing ones using a template' + )->addArgument( + 'target', + InputArgument::REQUIRED, + "the target key to add (may include 'textdomain::' prefix)" + )->addArgument( + 'template', + InputArgument::REQUIRED, + 'the template to build the string, using ||string||' + . ' to import existing strings' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $target = $input->getArgument('target'); + $template = $input->getArgument('template'); + + // Make sure a valid target has been specified: + list($targetDomain, $targetKey) = $this->extractTextDomain($target); + if (!($targetDir = $this->getLangDir($output, $targetDomain, true))) { + return 1; + } + + // Extract required source values from template: + preg_match_all('/\|\|[^|]+\|\|/', $template, $matches); + $lookups = []; + foreach ($matches[0] as $current) { + $key = trim($current, '|'); + list($sourceDomain, $sourceKey) = $this->extractTextDomain($key); + $lookups[$sourceDomain][$current] = [ + 'key' => $sourceKey, + 'translations' => [] + ]; + } + + // Look up translations of all references in template: + foreach ($lookups as $domain => & $tokens) { + $sourceDir = $this->getLangDir($output, $domain, false); + if (!$sourceDir) { + return $this->getFailureResponse(); + } + $sourceCallback = function ($full) use ($domain, & $tokens) { + $strings = $this->reader->getTextDomain($full, false); + foreach ($tokens as & $current) { + $sourceKey = $current['key']; + if (isset($strings[$sourceKey])) { + $current['translations'][basename($full)] + = $strings[$sourceKey]; + } + } + }; + $this->processDirectory($sourceDir, $sourceCallback, false); + } + + // Fill in template, write results: + $targetCallback = function ($full) use ( + $output, $template, $targetKey, $lookups + ) { + $lang = basename($full); + $in = $out = []; + foreach ($lookups as $domain => $tokens) { + foreach ($tokens as $token => $details) { + if (!isset($details['translations'][$lang])) { + $output->writeln("Skipping; no match for token: $token"); + return; + } + $in[] = $token; + $out[] = $details['translations'][$lang]; + } + } + $this->addLineToFile( + $full, $targetKey, str_replace($in, $out, $template) + ); + $this->normalizer->normalizeFile($full); + }; + $this->processDirectory($targetDir, $targetCallback, [$output, 'writeln']); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/CopyStringCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/CopyStringCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..1701b370d9149c11ed8e3360136db6d7f52a42de --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/CopyStringCommand.php @@ -0,0 +1,141 @@ +<?php +/** + * Language command: copy string. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Language command: copy string. + * + * @category VuFind + * @package Console + * @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 CopyStringCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'language/copystring'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $note = "(may include 'textdomain::' prefix)"; + $this + ->setDescription('String copier') + ->setHelp('Copies one language string to another.') + ->addArgument( + 'source', + InputArgument::REQUIRED, + 'the source key to read ' . $note + )->addArgument( + 'target', + InputArgument::REQUIRED, + 'the target key to write ' . $note + ); + } + + /** + * Add a line to a language file + * + * @param string $filename File to update + * @param string $key Name of language key + * @param string $value Value of translation + * + * @return void + */ + protected function addLineToFile($filename, $key, $value) + { + $fHandle = fopen($filename, "a"); + if (!$fHandle) { + throw new \Exception('Cannot open ' . $filename . ' for writing.'); + } + fputs($fHandle, "\n$key = \"" . $value . "\"\n"); + fclose($fHandle); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $source = $input->getArgument('source'); + $target = $input->getArgument('target'); + + list($sourceDomain, $sourceKey) = $this->extractTextDomain($source); + list($targetDomain, $targetKey) = $this->extractTextDomain($target); + + if (!($sourceDir = $this->getLangDir($output, $sourceDomain)) + || !($targetDir = $this->getLangDir($output, $targetDomain, true)) + ) { + return 1; + } + + // First, collect the source values from the source text domain: + $sources = []; + $sourceCallback = function ($full) use ($output, $sourceKey, & $sources) { + $strings = $this->reader->getTextDomain($full, false); + if (!isset($strings[$sourceKey])) { + $output->writeln('Source key not found.'); + } else { + $sources[basename($full)] = $strings[$sourceKey]; + } + }; + $this->processDirectory($sourceDir, $sourceCallback, [$output, 'writeln']); + + // Make sure that all target files exist: + $this->createMissingFiles($targetDir->path, array_keys($sources)); + + // Now copy the values to their destination: + $targetCallback = function ($full) use ($output, $targetKey, $sources) { + if (isset($sources[basename($full)])) { + $this->addLineToFile($full, $targetKey, $sources[basename($full)]); + $this->normalizer->normalizeFile($full); + } + }; + $this->processDirectory($targetDir, $targetCallback, [$output, 'writeln']); + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/DeleteCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/DeleteCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..b95851219f3d1ef8d0dac8c2652ba06637049cb2 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/DeleteCommand.php @@ -0,0 +1,123 @@ +<?php +/** + * Language command: add string using template. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Language command: add string using template. + * + * @category VuFind + * @package Console + * @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 DeleteCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'language/delete'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Delete string tool') + ->setHelp( + 'Removes a language string from all files' + )->addArgument( + 'target', + InputArgument::REQUIRED, + "the target key to remove (may include 'textdomain::' prefix)" + ); + } + + /** + * Write file contents to disk. + * + * @param string $filename Filename + * @param string $content Content + * + * @return bool + */ + protected function writeFileToDisk($filename, $content) + { + return file_put_contents($filename, $content); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $target = $input->getArgument('target'); + + list($domain, $key) = $this->extractTextDomain($target); + $target = $key . ' = "'; + + if (!($dir = $this->getLangDir($output, $domain))) { + return 1; + } + $callback = function ($full) use ($output, $target) { + $lines = file($full); + $out = ''; + $found = false; + foreach ($lines as $line) { + if (substr($line, 0, strlen($target)) !== $target) { + $out .= $line; + } else { + $found = true; + } + } + if ($found) { + $this->writeFileToDisk($full, $out); + $this->normalizer->normalizeFile($full); + } else { + $output->writeln('Source key not found.'); + } + }; + $this->processDirectory($dir, $callback, [$output, 'writeln']); + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Language/NormalizeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Language/NormalizeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..218a4e4b4587517bc5838a478d6cb4bf1e99b9ad --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Language/NormalizeCommand.php @@ -0,0 +1,92 @@ +<?php +/** + * Language command: normalize file or directory. + * + * 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 Console + * @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 VuFindConsole\Command\Language; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Language command: normalize file or directory. + * + * @category VuFind + * @package Console + * @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 NormalizeCommand extends AbstractCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'language/normalize'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Language file normalizer') + ->setHelp( + 'Normalizes a file or directory of language strings' + )->addArgument( + 'target', + InputArgument::REQUIRED, + "a file or directory to normalize" + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $target = $input->getArgument('target'); + + if (is_dir($target)) { + $this->normalizer->normalizeDirectory($target); + } elseif (is_file($target)) { + $this->normalizer->normalizeFile($target); + } else { + $output->writeln("{$target} does not exist."); + return 1; + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/PluginManager.php b/module/VuFindConsole/src/VuFindConsole/Command/PluginManager.php new file mode 100644 index 0000000000000000000000000000000000000000..998130e7544c2a6e62a579708d7a3551f0b0da2f --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/PluginManager.php @@ -0,0 +1,187 @@ +<?php +/** + * Console command plugin manager + * + * 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 Console + * @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:ils_drivers Wiki + */ +namespace VuFindConsole\Command; + +use Laminas\ServiceManager\Factory\InvokableFactory; + +/** + * Console command plugin manager + * + * @category VuFind + * @package Console + * @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:ils_drivers Wiki + */ +class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager +{ + /** + * Default plugin aliases. + * + * @var array + */ + protected $aliases = [ + 'compile/theme' => Compile\ThemeCommand::class, + 'generate/dynamicroute' => Generate\DynamicRouteCommand::class, + 'generate/extendclass' => Generate\ExtendClassCommand::class, + 'generate/extendservice' => Generate\ExtendServiceCommand::class, + 'generate/nontabrecordaction' => Generate\NonTabRecordActionCommand::class, + 'generate/plugin' => Generate\PluginCommand::class, + 'generate/recordroute' => Generate\RecordRouteCommand::class, + 'generate/staticroute' => Generate\StaticRouteCommand::class, + 'generate/theme' => Generate\ThemeCommand::class, + 'generate/thememixin' => Generate\ThemeMixinCommand::class, + 'harvest/harvest_oai' => Harvest\HarvestOaiCommand::class, + 'harvest/merge-marc' => Harvest\MergeMarcCommand::class, + 'import/import-xsl' => Import\ImportXslCommand::class, + 'import/webcrawl' => Import\WebCrawlCommand::class, + 'install/install' => Install\InstallCommand::class, + 'language/addusingtemplate' => Language\AddUsingTemplateCommand::class, + 'language/copystring' => Language\CopyStringCommand::class, + 'language/delete' => Language\DeleteCommand::class, + 'language/normalize' => Language\NormalizeCommand::class, + 'scheduledsearch/notify' => ScheduledSearch\NotifyCommand::class, + 'util/cleanuprecordcache' => Util\CleanUpRecordCacheCommand::class, + 'util/cleanup_record_cache' => Util\CleanUpRecordCacheCommand::class, + 'util/commit' => Util\CommitCommand::class, + 'util/createHierarchyTrees' => Util\CreateHierarchyTreesCommand::class, + 'util/cssBuilder' => Util\CssBuilderCommand::class, + 'util/dedupe' => Util\DedupeCommand::class, + 'util/deletes' => Util\DeletesCommand::class, + 'util/expire_auth_hashes' => Util\ExpireAuthHashesCommand::class, + 'util/expire_external_sessions' => Util\ExpireExternalSessionsCommand::class, + 'util/expire_searches' => Util\ExpireSearchesCommand::class, + 'util/expire_sessions' => Util\ExpireSessionsCommand::class, + 'util/index_reserves' => Util\IndexReservesCommand::class, + 'util/lint_marc' => Util\LintMarcCommand::class, + 'util/optimize' => Util\OptimizeCommand::class, + 'util/sitemap' => Util\SitemapCommand::class, + 'util/suppressed' => Util\SuppressedCommand::class, + 'util/switch_db_hash' => Util\SwitchDbHashCommand::class, + ]; + + /** + * Default plugin factories. + * + * @var array + */ + protected $factories = [ + Compile\ThemeCommand::class => Compile\ThemeCommandFactory::class, + Generate\DynamicRouteCommand::class => + Generate\AbstractRouteCommandFactory::class, + Generate\ExtendClassCommand::class => + Generate\AbstractContainerAwareCommandFactory::class, + Generate\ExtendServiceCommand::class => + Generate\AbstractCommandFactory::class, + Generate\NonTabRecordActionCommand::class => + Generate\NonTabRecordActionCommandFactory::class, + Generate\PluginCommand::class => + Generate\AbstractContainerAwareCommandFactory::class, + Generate\RecordRouteCommand::class => + Generate\AbstractRouteCommandFactory::class, + Generate\StaticRouteCommand::class => + Generate\AbstractRouteCommandFactory::class, + Generate\ThemeCommand::class => + Generate\ThemeCommandFactory::class, + Generate\ThemeMixinCommand::class => + Generate\ThemeMixinCommandFactory::class, + Harvest\MergeMarcCommand::class => InvokableFactory::class, + Harvest\HarvestOaiCommand::class => Harvest\HarvestOaiCommandFactory::class, + Import\ImportXslCommand::class => Import\ImportXslCommandFactory::class, + Import\WebCrawlCommand::class => Import\WebCrawlCommandFactory::class, + Install\InstallCommand::class => InvokableFactory::class, + Language\AddUsingTemplateCommand::class => + Language\AbstractCommandFactory::class, + Language\CopyStringCommand::class => Language\AbstractCommandFactory::class, + Language\DeleteCommand::class => Language\AbstractCommandFactory::class, + Language\NormalizeCommand::class => Language\AbstractCommandFactory::class, + ScheduledSearch\NotifyCommand::class => + ScheduledSearch\NotifyCommandFactory::class, + Util\CleanUpRecordCacheCommand::class => + Util\CleanUpRecordCacheCommandFactory::class, + Util\CommitCommand::class => Util\AbstractSolrCommandFactory::class, + Util\CreateHierarchyTreesCommand::class => + Util\CreateHierarchyTreesCommandFactory::class, + Util\CssBuilderCommand::class => Util\CssBuilderCommandFactory::class, + Util\DedupeCommand::class => InvokableFactory::class, + Util\DeletesCommand::class => Util\AbstractSolrCommandFactory::class, + Util\ExpireAuthHashesCommand::class => + Util\ExpireAuthHashesCommandFactory::class, + Util\ExpireExternalSessionsCommand::class => + Util\ExpireExternalSessionsCommandFactory::class, + Util\ExpireSearchesCommand::class => + Util\ExpireSearchesCommandFactory::class, + Util\ExpireSessionsCommand::class => + Util\ExpireSessionsCommandFactory::class, + Util\IndexReservesCommand::class => + Util\AbstractSolrAndIlsCommandFactory::class, + Util\LintMarcCommand::class => InvokableFactory::class, + Util\OptimizeCommand::class => Util\AbstractSolrCommandFactory::class, + Util\SitemapCommand::class => Util\SitemapCommandFactory::class, + Util\SuppressedCommand::class => + Util\AbstractSolrAndIlsCommandFactory::class, + Util\SwitchDbHashCommand::class => Util\SwitchDbHashCommandFactory::class, + ]; + + /** + * 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); + } + + /** + * Get a list of all available commands in the plugin manager. + * + * @return array + */ + public function getCommandList() + { + return array_keys($this->factories); + } + + /** + * Return the name of the base class or interface that plug-ins must conform + * to. + * + * @return string + */ + protected function getExpectedInterface() + { + return \Symfony\Component\Console\Command\Command::class; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/RelativeFileAwareCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/RelativeFileAwareCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..882e0e249c9c0f8dd68bdbed7f9bc1cb20202296 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/RelativeFileAwareCommand.php @@ -0,0 +1,60 @@ +<?php +/** + * Abstract base class for commands that take relative paths as parameters. + * + * 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 Console + * @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 VuFindConsole\Command; + +use Symfony\Component\Console\Command\Command; + +/** + * Abstract base class for commands that take relative paths as parameters. + * + * @category VuFind + * @package Console + * @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 RelativeFileAwareCommand extends Command +{ + /** + * Constructor + * + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct($name = null) + { + // Switch the context back to the original working directory so that + // relative paths work as expected. (This constant is set in + // public/index.php) + if (defined('ORIGINAL_WORKING_DIRECTORY')) { + chdir(ORIGINAL_WORKING_DIRECTORY); + } + + parent::__construct($name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php b/module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommand.php similarity index 75% rename from module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php rename to module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommand.php index dafeb2889573e80f1bec8ba1b3bedba3ea9c408a..827575c20727b976942f98b6ee761fd3d742b999 100644 --- a/module/VuFindConsole/src/VuFindConsole/Controller/ScheduledSearchController.php +++ b/module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommand.php @@ -1,10 +1,10 @@ <?php /** - * CLI Controller Module (scheduled search tools) + * Console command: notify users of scheduled searches. * * PHP version 7 * - * Copyright (C) Villanova University 2019. + * 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, @@ -20,35 +20,53 @@ * 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> + * @package Console * @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 + * @link https://vufind.org/wiki/development Wiki */ -namespace VuFindConsole\Controller; +namespace VuFindConsole\Command\ScheduledSearch; -use Laminas\Console\Console; -use Laminas\ServiceManager\ServiceLocatorInterface; +use Laminas\Config\Config; +use Laminas\View\Renderer\PhpRenderer; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Crypt\HMAC; +use VuFind\Db\Table\Search as SearchTable; +use VuFind\Db\Table\User as UserTable; +use VuFind\I18n\Translator\TranslatorAwareInterface; +use VuFind\Mailer\Mailer; +use VuFind\Search\Results\PluginManager as ResultsManager; /** - * CLI Controller Module (scheduled search tools) + * Console command: notify users of scheduled searches. * * @category VuFind - * @package Controller - * @author Samuli Sillanpää <samuli.sillanpaa@helsinki.fi> - * @author Ere Maijala <ere.maijala@helsinki.fi> + * @package Console * @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 + * @link https://vufind.org/wiki/development Wiki */ -class ScheduledSearchController extends AbstractBase - implements \VuFind\I18n\Translator\TranslatorAwareInterface +class NotifyCommand extends Command implements TranslatorAwareInterface { use \VuFind\I18n\Translator\TranslatorAwareTrait; use \VuFind\I18n\Translator\LanguageInitializerTrait; + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'scheduledsearch/notify'; + + /** + * Output interface + * + * @var OutputInterface + */ + protected $output = null; + /** * Useful date format value * @@ -59,14 +77,14 @@ class ScheduledSearchController extends AbstractBase /** * HMAC generator * - * @var \VuFind\Crypt\HMAC + * @var HMAC */ protected $hmac; /** * View renderer * - * @var \Laminas\View\Renderer\PhpRenderer + * @var PhpRenderer */ protected $renderer; @@ -80,7 +98,7 @@ class ScheduledSearchController extends AbstractBase /** * Search results plugin manager * - * @var \VuFind\Search\Results\PluginManager + * @var ResultsManager */ protected $resultsManager; @@ -94,7 +112,7 @@ class ScheduledSearchController extends AbstractBase /** * Top-level VuFind configuration * - * @var \Laminas\Config\Config + * @var Config */ protected $mainConfig; @@ -108,42 +126,64 @@ class ScheduledSearchController extends AbstractBase /** * Mail service * - * @var \VuFind\Mailer\Mailer + * @var Mailer */ protected $mailer; /** - * Constructor + * Search table * - * @param ServiceLocatorInterface $sm Service locator + * @var SearchTable */ - public function __construct(ServiceLocatorInterface $sm) - { - parent::__construct($sm); + protected $searchTable; - $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); + /** + * User table + * + * @var UserTable + */ + protected $userTable; + + /** + * Constructor + * + * @param HMAC $hmac HMAC generator + * @param PhpRenderer $renderer View renderer + * @param ResultsManager $resultsManager Search results plugin manager + * @param array $scheduleOptions Configured schedule options + * @param Config $mainConfig Top-level VuFind configuration + * @param Mailer $mailer Mail service + * @param SearchTable $searchTable Search table + * @param UserTable $userTable User table + * @param string|null $name The name of the command; passing + * null means it must be set in configure() + */ + public function __construct(HMAC $hmac, PhpRenderer $renderer, + ResultsManager $resultsManager, array $scheduleOptions, Config $mainConfig, + Mailer $mailer, SearchTable $searchTable, UserTable $userTable, $name = null + ) { + $this->hmac = $hmac; + $this->renderer = $renderer; + $this->urlHelper = $renderer->plugin('url'); + $this->resultsManager = $resultsManager; + $this->scheduleOptions = $scheduleOptions; + $this->mainConfig = $mainConfig; + $this->mailer = $mailer; + $this->searchTable = $searchTable; + $this->userTable = $userTable; + parent::__construct($name); } /** - * Send notifications. + * Configure the command. * - * @return \Laminas\Console\Response + * @return void */ - public function notifyAction() + protected function configure() { - $this->processViewAlerts(); - return $this->getSuccessResponse(); + $this + ->setDescription('Scheduled Search Notifier') + ->setHelp('Sends scheduled search email notifications.'); } /** @@ -155,7 +195,9 @@ class ScheduledSearchController extends AbstractBase */ protected function msg($msg) { - Console::writeLine($msg); + if (null !== $this->output) { + $this->output->writeln($msg); + } } /** @@ -167,7 +209,7 @@ class ScheduledSearchController extends AbstractBase */ protected function warn($msg) { - Console::writeLine('WARNING: ' . $msg); + $this->msg('WARNING: ' . $msg); } /** @@ -179,7 +221,22 @@ class ScheduledSearchController extends AbstractBase */ protected function err($msg) { - Console::writeLine('ERROR: ' . $msg); + $this->msg('ERROR: ' . $msg); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + $this->processViewAlerts(); + return 0; } /** @@ -226,7 +283,8 @@ class ScheduledSearchController extends AbstractBase static $user = false; if ($user === false || $s->user_id != $user->id) { - if (!$user = $this->getTable('user')->getById($s->user_id)) { + if (!$user = $this->userTable->getById($s->user_id)) { + $user = false; // make sure static variable is cleared $this->warn( 'Search ' . $s->id . ': user ' . $s->user_id . ' does not exist ' @@ -254,14 +312,12 @@ class ScheduledSearchController extends AbstractBase 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()) - ) - ) { + // preference if set and valid. Default to English if configuration + // is missing. + $language = $this->mainConfig->Site->language ?? 'en'; + $allLanguages = isset($this->mainConfig->Languages) + ? array_keys($this->mainConfig->Languages->toArray()) : ['en']; + if ($userLang != '' && in_array($userLang, $allLanguages)) { $language = $userLang; } $this->translator->setLocale($language); @@ -436,7 +492,7 @@ class ScheduledSearchController extends AbstractBase protected function processViewAlerts() { $todayTime = new \DateTime(); - $scheduled = $this->getTable('search')->getScheduledSearches(); + $scheduled = $this->searchTable->getScheduledSearches(); $this->msg(sprintf('Processing %d searches', count($scheduled))); foreach ($scheduled as $s) { $lastTime = new \DateTime($s->last_notification_sent); @@ -459,7 +515,7 @@ class ScheduledSearchController extends AbstractBase } $searchTime = date('Y-m-d H:i:s'); if ($s->setLastExecuted($searchTime) === 0) { - $this->err("Error updating last_executed date for search $searchId"); + $this->err("Error updating last_executed date for search {$s->id}"); } } $this->msg('Done processing searches'); diff --git a/module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..69dcbf5a17db465ec64c5287f8410c2b3cf63f54 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/ScheduledSearch/NotifyCommandFactory.php @@ -0,0 +1,85 @@ +<?php +/** + * Factory for ScheduledSearch/Notify command. + * + * 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 Console + * @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 VuFindConsole\Command\ScheduledSearch; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for ScheduledSearch/Notify command. + * + * @category VuFind + * @package Console + * @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 NotifyCommandFactory 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 + ) { + $scheduleOptions = $container + ->get(\VuFind\Search\History::class) + ->getScheduleOptions(); + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + $mainConfig = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + + // We need to initialize the theme so that the view renderer works: + $theme = new \VuFindTheme\Initializer($mainConfig->Site, $container); + $theme->init(); + + // Now build the object: + return new $requestedName( + $container->get(\VuFind\Crypt\HMAC::class), + $container->get('ViewRenderer'), + $container->get(\VuFind\Search\Results\PluginManager::class), + $scheduleOptions, + $mainConfig, + $container->get(\VuFind\Mailer\Mailer::class), + $tableManager->get(\VuFind\Db\Table\Search::class), + $tableManager->get(\VuFind\Db\Table\User::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractExpireCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractExpireCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..84331687975742cc8a828c58f47f2f4305c52543 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractExpireCommand.php @@ -0,0 +1,189 @@ +<?php +/** + * Generic base class for expiration commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Db\Table\Gateway; + +/** + * Generic base class for expiration commands. + * + * @category VuFind + * @package Console + * @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 AbstractExpireCommand extends Command +{ + /** + * Help description for the command. + * + * @var string + */ + protected $commandDescription = 'Expiration tool'; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'rows'; + + /** + * Minimum (and default) legal age of rows to delete. + * + * @var int + */ + protected $minAge = 2; + + /** + * Table on which to expire rows + * + * @var Gateway + */ + protected $table; + + /** + * Constructor + * + * @param Gateway $table Table on which to expire rows + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Gateway $table, $name = null) + { + foreach (['getExpiredIdRange', 'deleteExpired'] as $method) { + if (!method_exists($table, $method)) { + $tableName = get_class($table); + throw new \Exception("$tableName does not support $method()"); + } + } + $this->table = $table; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription($this->commandDescription) + ->setHelp("Expires old {$this->rowLabel} in the database.") + ->addOption( + 'batch', + null, + InputOption::VALUE_REQUIRED, + 'number of records to delete in a single batch', + 1000 + )->addOption( + 'sleep', + null, + InputOption::VALUE_REQUIRED, + 'milliseconds to sleep between batches', + 100 + )->addArgument( + 'age', + InputArgument::OPTIONAL, + "the age (in days) of {$this->rowLabel} to expire", + $this->minAge + ); + } + + /** + * Add a time stamp to a message + * + * @param string $msg Message + * + * @return string + */ + protected function getTimestampedMessage($msg) + { + return '[' . date('Y-m-d H:i:s') . '] ' . $msg; + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Collect arguments/options: + $daysOld = floatval($input->getArgument('age')); + $batchSize = $input->getOption('batch'); + $sleepTime = $input->getOption('sleep'); + + // Abort if we have an invalid expiration age. + if ($daysOld < $this->minAge) { + $output->writeln( + str_replace( + '%%age%%', $this->minAge, + 'Expiration age must be at least %%age%% days.' + ) + ); + return 1; + } + + // Delete the expired rows--this cleans up any junk left in the database + // e.g. from old searches or sessions that were not caught by the session + // garbage collector. + $idRange = $this->table->getExpiredIdRange($daysOld); + if (false === $idRange) { + $output->writeln( + $this->getTimestampedMessage("No {$this->rowLabel} to delete.") + ); + return 0; + } + + // Delete records in batches + for ($batch = $idRange[0]; $batch <= $idRange[1]; $batch += $batchSize) { + $count = $this->table->deleteExpired( + $daysOld, $batch, $batch + $batchSize - 1 + ); + $output->writeln( + $this->getTimestampedMessage("{$count} {$this->rowLabel} deleted.") + ); + // Be nice to others and wait between batches + if ($batch + $batchSize <= $idRange[1]) { + usleep($sleepTime * 1000); + } + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..1380538b6c798d794c51fb616b849ba98bad0bd6 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommand.php @@ -0,0 +1,64 @@ +<?php +/** + * Generic base class for Solr + ILS commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use VuFind\ILS\Connection; +use VuFind\Solr\Writer; + +/** + * Generic base class for Solr + ILS commands. + * + * @category VuFind + * @package Console + * @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 AbstractSolrAndIlsCommand extends AbstractSolrCommand +{ + /** + * ILS connection + * + * @var Connection + */ + protected $catalog; + + /** + * Constructor + * + * @param Writer $solr Solr writer + * @param Connection $ils ILS connection object + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Writer $solr, Connection $ils, $name = null) + { + $this->catalog = $ils; + parent::__construct($solr, $name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fe0f10102d10a6064b8e4f7360962d49f43a61bf --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrAndIlsCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Solr + ILS commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Solr + ILS commands. + * + * @category VuFind + * @package Console + * @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 AbstractSolrAndIlsCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFind\Solr\Writer::class), + $container->get(\VuFind\ILS\Connection::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..5438a84d9c655276913cef11f75d6d2dc1a83678 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommand.php @@ -0,0 +1,64 @@ +<?php +/** + * Generic base class for Solr commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use VuFind\Solr\Writer; +use VuFindConsole\Command\RelativeFileAwareCommand; + +/** + * Generic base class for Solr commands. + * + * @category VuFind + * @package Console + * @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 AbstractSolrCommand extends RelativeFileAwareCommand +{ + /** + * Solr writer + * + * @var Writer + */ + protected $solr; + + /** + * Constructor + * + * @param Writer $solr Solr writer + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Writer $solr, $name = null) + { + $this->solr = $solr; + parent::__construct($name); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e1eb134e13a06018cf15a79f05e5a2d673428113 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/AbstractSolrCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Solr commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Solr commands. + * + * @category VuFind + * @package Console + * @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 AbstractSolrCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFind\Solr\Writer::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..74a9bf05dcd2210b44ca26ec6c2c7ea88fe60097 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommand.php @@ -0,0 +1,100 @@ +<?php +/** + * Console command: clean up record cache. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Db\Table\Record; + +/** + * Console command: clean up record cache. + * + * @category VuFind + * @package Console + * @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 CleanUpRecordCacheCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/cleanup_record_cache'; + + /** + * Record table object + * + * @var Record + */ + protected $recordTable; + + /** + * Constructor + * + * @param Record $table Record table object + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Record $table, $name = null) + { + $this->recordTable = $table; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Record cache cleaner') + ->setHelp('Removes unused cached records from the database.') + ->setAliases(['util/cleanuprecordcache']); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $count = $this->recordTable->cleanup(); + $output->writeln("$count records deleted."); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7037524741ff772ea96316123e4737ec4ce5d385 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CleanUpRecordCacheCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Util/CleanUpRecordCache command. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/CleanUpRecordCache command. + * + * @category VuFind + * @package Console + * @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 CleanUpRecordCacheCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFind\Db\Table\PluginManager::class)->get('Record'), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CommitCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CommitCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8601805e5aa07d18ec43968778e53acf43e3f278 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CommitCommand.php @@ -0,0 +1,100 @@ +<?php +/** + * Console command: commit to Solr + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: commit to Solr + * + * @category VuFind + * @package Console + * @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 CommitCommand extends AbstractSolrCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/commit'; + + /** + * The name of the Solr command, for use in help messages. + * + * @var string + */ + protected $solrCommand = 'commit'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Solr ' . $this->solrCommand . ' tool') + ->setHelp('Sends a ' . $this->solrCommand . ' command to a Solr index.') + ->addArgument( + 'core', + InputArgument::OPTIONAL, + 'Name of Solr core to ' . $this->solrCommand, + 'Solr' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Check time limit; increase if necessary: + if (ini_get('max_execution_time') < 3600) { + ini_set('max_execution_time', '3600'); + } + + // Setup Solr Connection -- Allow core to be specified from command line. + $core = $input->getArgument('core'); + + // Commit to the Solr Index + $this->solr->commit($core); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..f8a863f3b82a1f2669d91f993f0e66038edf91c3 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommand.php @@ -0,0 +1,181 @@ +<?php +/** + * Generic base class for Solr commands. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Record\Loader; +use VuFind\Search\Results\PluginManager; + +/** + * Generic base class for Solr commands. + * + * @category VuFind + * @package Console + * @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 CreateHierarchyTreesCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/createHierarchyTrees'; + + /** + * Record loader + * + * @var Loader + */ + protected $recordLoader; + + /** + * Search results manager + * + * @var PluginManager + */ + protected $resultsManager; + + /** + * Constructor + * + * @param Loader $loader Record loader + * @param PluginManager $results Search results manager + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Loader $loader, PluginManager $results, $name = null) + { + $this->recordLoader = $loader; + $this->resultsManager = $results; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Cache populator for hierarchies') + ->setHelp('Populates the hierarchy tree cache.') + ->addArgument( + 'backend', + InputArgument::OPTIONAL, + 'Search backend, e.g. ' . DEFAULT_SEARCH_BACKEND + . ' (default) or Search2', + DEFAULT_SEARCH_BACKEND + )->addOption( + 'skip', + 's', + InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'format(s) to skip caching (x = xml, j = json)' + )->addOption( + 'skip-xml', + null, + InputOption::VALUE_NONE, + 'skip the XML cache (synonymous with -sx)' + )->addOption( + 'skip-json', + null, + InputOption::VALUE_NONE, + 'skip the JSON cache (synonymous with -sj)' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $skips = $input->getOption('skip') ?? []; + $skipJson = $input->getOption('skip-json') || in_array('j', $skips); + $skipXml = $input->getOption('skip-xml') || in_array('x', $skips); + $backendId = $input->getArgument('backend'); + $hierarchies = $this->resultsManager->get($backendId) + ->getFullFieldFacets(['hierarchy_top_id']); + $list = $hierarchies['hierarchy_top_id']['data']['list'] ?? []; + foreach ($list as $hierarchy) { + $recordid = $hierarchy['value']; + $count = $hierarchy['count']; + if (empty($recordid)) { + continue; + } + $output->writeln( + "\tBuilding tree for " . $recordid . '... ' + . number_format($count) . ' records' + ); + try { + $driver = $this->recordLoader->load($recordid, $backendId); + // Only do this if the record is actually a hierarchy type record + if ($driver->getHierarchyType()) { + // JSON + if (!$skipJson) { + $output->writeln("\t\tJSON cache..."); + $driver->getHierarchyDriver()->getTreeSource()->getJSON( + $recordid, ['refresh' => true] + ); + } else { + $output->writeln("\t\tJSON skipped."); + } + // XML + if (!$skipXml) { + $output->writeln("\t\tXML cache..."); + $driver->getHierarchyDriver()->getTreeSource()->getXML( + $recordid, ['refresh' => true] + ); + } else { + $output->writeln("\t\tXML skipped."); + } + } + } catch (\VuFind\Exception\RecordMissing $e) { + $output->writeln( + 'WARNING! - Caught exception: ' . $e->getMessage() . "\n" + ); + } + } + $output->writeln( + count($hierarchies['hierarchy_top_id']['data']['list']) . ' files' + ); + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..91f1c8d03c6a4af7f5c9b52a2d6d6031582e00f6 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CreateHierarchyTreesCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Util/CreateHierarchyTrees command. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/CreateHierarchyTrees command. + * + * @category VuFind + * @package Console + * @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 CreateHierarchyTreesCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFind\Record\Loader::class), + $container->get(\VuFind\Search\Results\PluginManager::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..28b650523bf833cd1e78ce12a67f34f6bc5c877b --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommand.php @@ -0,0 +1,118 @@ +<?php +/** + * Console command: build CSS. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindTheme\LessCompiler; + +/** + * Console command: build CSS. + * + * @category VuFind + * @package Console + * @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 CssBuilderCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/cssBuilder'; + + /** + * Cache directory for compiler + * + * @var string + */ + protected $cacheDir; + + /** + * Constructor + * + * @param string $cacheDir Cache directory for compiler + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct($cacheDir, $name = null) + { + $this->cacheDir = $cacheDir; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('LESS compiler') + ->setHelp('Compiles CSS files from LESS.') + ->addArgument( + 'themes', + InputArgument::IS_ARRAY | InputArgument::OPTIONAL, + 'Name of theme(s) to compile; omit to compile everything' + ); + } + + /** + * Build the LESS compiler. + * + * @param OutputInterface $output Output object + * + * @return LessCompiler + */ + protected function getCompiler(OutputInterface $output) + { + return new LessCompiler($output); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $compiler = $this->getCompiler($output); + $compiler->setTempPath($this->cacheDir); + $compiler->compile(array_unique($input->getArgument('themes'))); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..f8664ed2ca4a7bad8bda1312fd8eeba328bdccdb --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/CssBuilderCommandFactory.php @@ -0,0 +1,65 @@ +<?php +/** + * Factory for Util/CssBuilder command. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/CssBuilder command. + * + * @category VuFind + * @package Console + * @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 CssBuilderCommandFactory 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 + ) { + $cacheManager = $container->get(\VuFind\Cache\Manager::class); + $cacheDir = $cacheManager->getCacheDir() . 'less/'; + return new $requestedName($cacheDir, ...($options ?? [])); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/DedupeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/DedupeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..17b6f40d0b99008f742e25e773ad420a700d2800 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/DedupeCommand.php @@ -0,0 +1,175 @@ +<?php +/** + * Console command: deduplicate lines in a sorted file. + * + * Needed for the Windows version of the alphabetical browse database generator, + * since Windows sort does not support deduplication. Assumes presorted input. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; +use VuFindConsole\Command\RelativeFileAwareCommand; + +/** + * Console command: deduplicate lines in a sorted file. + * + * @category VuFind + * @package Console + * @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 DedupeCommand extends RelativeFileAwareCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/dedupe'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Tool for deduplicating lines in a sorted file') + ->setHelp('Deduplicates lines in a sorted file.') + ->addArgument( + 'input', + InputArgument::OPTIONAL, + 'the file to deduplicate (omit for interactive prompt).' + )->addArgument( + 'output', + InputArgument::OPTIONAL, + 'the output file (omit for interactive prompt).' + ); + } + + /** + * Fetch a single line of input from the user. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * @param string $prompt Prompt to display to the user. + * + * @return string User-entered response. + */ + protected function getInput(InputInterface $input, OutputInterface $output, + string $prompt + ): string { + $question = new Question($prompt, ''); + return $this->getHelper('question')->ask($input, $output, $question); + } + + /** + * Open a file for writing. + * + * @param string $filename File to open + * + * @return resource + */ + protected function openOutputFile($filename) + { + return @fopen($filename, 'w'); + } + + /** + * Write a line to an output file. + * + * @param resource $handle File handle + * @param string $text Text to write + * + * @return void + */ + protected function writeToOutputFile($handle, $text) + { + fputs($handle, $text); + } + + /** + * Close a file handle. + * + * @param resource $handle Handle from openOutputFile() + * + * @return void + */ + protected function closeOutputFile($handle) + { + fclose($handle); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $infile = $input->getArgument('input'); + if (empty($infile)) { + $inprompt = 'Please specify an input file: '; + $infile = $this->getInput($input, $output, $inprompt); + } + $inHandle = @fopen($infile, 'r'); + if (!$inHandle) { + $output->writeln('Could not open input file: ' . $infile); + return 1; + } + $outfile = $input->getArgument('output'); + if (empty($outfile)) { + $outprompt = 'Please specify an output file: '; + $outfile = $this->getInput($input, $output, $outprompt); + } + $outHandle = $this->openOutputFile($outfile); + if (!$outHandle) { + $output->writeln('Could not open output file: ' . $outfile); + return 1; + } + + $last = ''; + while ($tmp = fgets($inHandle)) { + if ($tmp != $last) { + $this->writeToOutputFile($outHandle, $tmp); + } + $last = $tmp; + } + + fclose($inHandle); + $this->closeOutputFile($outHandle); + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/DeletesCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/DeletesCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8a86d8f48d286eb3329a98b43552f7e16aeeabe8 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/DeletesCommand.php @@ -0,0 +1,187 @@ +<?php +/** + * Console command: delete from Solr + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use File_MARC; +use File_MARCXML; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: delete from Solr + * + * @category VuFind + * @package Console + * @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 DeletesCommand extends AbstractSolrCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/deletes'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Tool for deleting Solr records') + ->setHelp('Deletes a set of records from the Solr index.') + ->addArgument( + 'filename', + InputArgument::REQUIRED, + 'the file containing records to delete.' + )->addArgument( + 'format', + InputArgument::OPTIONAL, + "the format of the file -- it may be one of the following:\n" + . "flat - flat text format " + . "(deletes all IDs in newline-delimited file)\n" + . "marc - binary MARC format (delete all record IDs from 001 " + . "fields)\n" + . "marcxml - MARC-XML format (delete all record IDs from 001 " + . "fields)\n", + 'marc' + )->addArgument( + 'index', + InputArgument::OPTIONAL, + 'Name of Solr core/backend to update', + 'Solr' + ); + } + + /** + * Load IDs from a flat file. + * + * @param string $filename Filename to load from + * + * @return array + */ + protected function getIdsFromFlatFile(string $filename): array + { + $ids = []; + foreach (array_map('trim', file($filename)) as $id) { + if (strlen($id)) { + $ids[] = $id; + } + } + return $ids; + } + + /** + * Load IDs from a MARC file + * + * @param string $filename MARC file + * @param string $mode Type of file (marc or marcxml) + * @param OutputInterface $output Output object + * + * @return array + */ + protected function getIdsFromMarcFile(string $filename, string $mode, + OutputInterface $output + ): array { + $ids = []; + // MARC file mode... We need to load the MARC record differently if it's + // XML or binary: + $collection = ($mode == 'marcxml') + ? new File_MARCXML($filename) : new File_MARC($filename); + + // Once the records are loaded, the rest of the logic is always the same: + $missingIdCount = 0; + while ($record = $collection->next()) { + $idField = $record->getField('001'); + if ($idField) { + $ids[] = (string)$idField->getData(); + } else { + $missingIdCount++; + } + } + if ($output->isVerbose() && $missingIdCount) { + $output->writeln( + "Encountered $missingIdCount record(s) without IDs." + ); + } + return $ids; + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $filename = $input->getArgument('filename'); + $mode = $input->getArgument('format'); + $index = $input->getArgument('index'); + + // File doesn't exist? + if (!file_exists($filename)) { + $output->writeln("Cannot find file: {$filename}"); + return 1; + } + + $output->writeln( + "Loading IDs in {$mode} mode.", OutputInterface::VERBOSITY_VERBOSE + ); + + // Build list of records to delete: + $ids = ($mode == 'flat') + ? $this->getIdsFromFlatFile($filename) + : $this->getIdsFromMarcFile($filename, $mode, $output); + + // Delete, Commit and Optimize if necessary: + if (!empty($ids)) { + $output->writeln( + 'Attempting to delete ' . count($ids) . ' record(s): ' + . implode(', ', $ids), OutputInterface::VERBOSITY_VERBOSE + ); + $this->solr->deleteRecords($index, $ids); + $output->writeln( + 'Delete operation completed.', OutputInterface::VERBOSITY_VERBOSE + ); + } elseif ($output->isVerbose()) { + $output->writeln('Nothing to delete.'); + } + + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..637199d09fdc3aac57e63632f5d8db7472080dbe --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommand.php @@ -0,0 +1,61 @@ +<?php +/** + * Console command: expire authentication hashes. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +/** + * Console command: expire authentication hashes. + * + * @category VuFind + * @package Console + * @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 ExpireAuthHashesCommand extends AbstractExpireCommand +{ + /** + * Help description for the command. + * + * @var string + */ + protected $commandDescription = 'Database auth_hash table cleanup'; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'authentication hashes'; + + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/expire_auth_hashes'; +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..70151fe7895c51194bd7f322a602a1b91fbcf6ef --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireAuthHashesCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Util/ExpireAuthHashesCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/ExpireAuthHashesCommand. + * + * @category VuFind + * @package Console + * @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 ExpireAuthHashesCommandFactory 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 + ) { + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + return new $requestedName( + $tableManager->get(\VuFind\Db\Table\AuthHash::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..33086d75cb04a1af0ca823b00ec4c96b33ecbed9 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommand.php @@ -0,0 +1,61 @@ +<?php +/** + * Console command: expire sessions. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +/** + * Console command: expire sessions. + * + * @category VuFind + * @package Console + * @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 ExpireExternalSessionsCommand extends AbstractExpireCommand +{ + /** + * Help description for the command. + * + * @var string + */ + protected $commandDescription = 'Database external_session table cleanup'; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'external sessions'; + + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/expire_external_sessions'; +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..adf0d3c708bd33d1137c86e8d76c605bccde1312 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireExternalSessionsCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Util/ExpireExternalSessionsCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/ExpireExternalSessionsCommand. + * + * @category VuFind + * @package Console + * @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 ExpireExternalSessionsCommandFactory 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 + ) { + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + return new $requestedName( + $tableManager->get(\VuFind\Db\Table\ExternalSession::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..3661c9ddef6618775ebd71b66b7190e9ffbea0c8 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommand.php @@ -0,0 +1,61 @@ +<?php +/** + * Console command: expire searches. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +/** + * Console command: expire searches. + * + * @category VuFind + * @package Console + * @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 ExpireSearchesCommand extends AbstractExpireCommand +{ + /** + * Help description for the command. + * + * @var string + */ + protected $commandDescription = 'Database search table cleanup'; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'searches'; + + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/expire_searches'; +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..732752aa34beee305438f26cf709001706d3ba88 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSearchesCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Util/ExpireSearchesCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/ExpireSearchesCommand. + * + * @category VuFind + * @package Console + * @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 ExpireSearchesCommandFactory 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 + ) { + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + return new $requestedName( + $tableManager->get(\VuFind\Db\Table\Search::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..6cf94a974633797648f2d9f6418a579cdf0e37df --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommand.php @@ -0,0 +1,61 @@ +<?php +/** + * Console command: expire sessions. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +/** + * Console command: expire sessions. + * + * @category VuFind + * @package Console + * @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 ExpireSessionsCommand extends AbstractExpireCommand +{ + /** + * Help description for the command. + * + * @var string + */ + protected $commandDescription = 'Database session table cleanup'; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'sessions'; + + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/expire_sessions'; +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..314e10090452326f188b2ec51e60b510b4969213 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/ExpireSessionsCommandFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Factory for Util/ExpireSessionsCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/ExpireSessionsCommand. + * + * @category VuFind + * @package Console + * @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 ExpireSessionsCommandFactory 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 + ) { + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + return new $requestedName( + $tableManager->get(\VuFind\Db\Table\Session::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/IndexReservesCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/IndexReservesCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..12a474369aec0bbdf0d4ca4ef37f16c54754bcf4 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/IndexReservesCommand.php @@ -0,0 +1,259 @@ +<?php +/** + * Console command: index course reserves into Solr. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Reserves\CsvReader; +use VuFindSearch\Backend\Solr\Document\UpdateDocument; +use VuFindSearch\Backend\Solr\Record\SerializableRecord; + +/** + * Console command: index course reserves into Solr. + * + * @category VuFind + * @package Console + * @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 IndexReservesCommand extends AbstractSolrAndIlsCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/index_reserves'; + + /** + * Default delimiter for reading files + * + * @var string + */ + protected $defaultDelimiter = ','; + + /** + * Default template for reading files + * + * @var string + */ + protected $defaultTemplate = 'BIB_ID,COURSE,INSTRUCTOR,DEPARTMENT'; + + /** + * Keys required in the data to create a valid reserves index. + * + * @var string[] + */ + protected $requiredKeys = ['INSTRUCTOR_ID', 'COURSE_ID', 'DEPARTMENT_ID']; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Course reserves index builder') + ->setHelp( + 'This tool populates your course reserves Solr index. If run with' + . ' no options, it will attempt to load data from your ILS.' + . ' Switches may be used to index from delimited files instead.' + )->addOption( + 'filename', + 'f', + InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, + 'file(s) containing delimited values' + )->addOption( + 'delimiter', + 'd', + InputOption::VALUE_REQUIRED, + 'specifies the delimiter used in file(s)', + $this->defaultDelimiter + )->addOption( + 'template', + 't', + InputOption::VALUE_REQUIRED, + 'provides a template showing where important values can be found ' + . "within the file.\nThe template is a comma-separated list of " + . "values. Choose from:\n" + . "BIB_ID - bibliographic ID\n" + . "COURSE - course name\n" + . "DEPARTMENT - department name\n" + . "INSTRUCTOR - instructor name\n" + . "SKIP - ignore data in this position\n", + $this->defaultTemplate + ); + } + + /** + * Build the reserves index from date returned by the ILS driver, + * specifically: getInstructors, getDepartments, getCourses, findReserves + * + * @param array $instructors Array of instructors $instructor_id => $instructor + * @param array $courses Array of courses $course_id => $course + * @param array $departments Array of department $dept_id => $department + * @param array $reserves Array of reserves records from driver's + * findReserves. + * + * @return UpdateDocument + */ + protected function buildReservesIndex($instructors, $courses, $departments, + $reserves + ) { + foreach ($reserves as $record) { + $requiredKeysFound + = count(array_intersect(array_keys($record), $this->requiredKeys)); + if ($requiredKeysFound < count($this->requiredKeys)) { + throw new \Exception( + implode(' and/or ', $this->requiredKeys) . ' fields ' . + 'not present in reserve records. Please update ILS driver.' + ); + } + $instructorId = $record['INSTRUCTOR_ID']; + $courseId = $record['COURSE_ID']; + $departmentId = $record['DEPARTMENT_ID']; + $id = $courseId . '|' . $instructorId . '|' . $departmentId; + + if (!isset($index[$id])) { + $index[$id] = [ + 'id' => $id, + 'bib_id' => [], + 'instructor_id' => $instructorId, + 'instructor' => $instructors[$instructorId] ?? '', + 'course_id' => $courseId, + 'course' => $courses[$courseId] ?? '', + 'department_id' => $departmentId, + 'department' => $departments[$departmentId] ?? '' + ]; + } + $index[$id]['bib_id'][] = $record['BIB_ID']; + } + + $updates = new UpdateDocument(); + foreach ($index as $id => $data) { + if (!empty($data['bib_id'])) { + $updates->addRecord(new SerializableRecord($data)); + } + } + return $updates; + } + + /** + * Construct a CSV reader. + * + * @param array|string $files Array of files to load (or single filename). + * @param string $delimiter Delimiter used by file(s). + * @param string $template Template showing field positions within + * file(s). Comma-separated list containing BIB_ID, INSTRUCTOR, COURSE, + * DEPARTMENT and/or SKIP. Default = BIB_ID,COURSE,INSTRUCTOR,DEPARTMENT + * + * @return CsvReader + */ + protected function getCsvReader($files, string $delimiter, + string $template + ): CsvReader { + return new CsvReader($files, $delimiter, $template); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Check time limit; increase if necessary: + if (ini_get('max_execution_time') < 3600) { + ini_set('max_execution_time', '3600'); + } + + $delimiter = $input->getOption('delimiter'); + $template = $input->getOption('template'); + + if ($file = $input->getOption('filename')) { + try { + $reader = $this->getCsvReader($file, $delimiter, $template); + $instructors = $reader->getInstructors(); + $courses = $reader->getCourses(); + $departments = $reader->getDepartments(); + $reserves = $reader->getReserves(); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + } elseif ($delimiter !== $this->defaultDelimiter) { + $output->writeln('-d (delimiter) is meaningless without -f (filename)'); + return 1; + } elseif ($template !== $this->defaultTemplate) { + $output->writeln('-t (template) is meaningless without -f (filename)'); + return 1; + } else { + try { + // Connect to ILS and load data: + $instructors = $this->catalog->getInstructors(); + $courses = $this->catalog->getCourses(); + $departments = $this->catalog->getDepartments(); + $reserves = $this->catalog->findReserves('', '', ''); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + } + + // Make sure we have reserves and at least one of: instructors, courses, + // departments: + if ((!empty($instructors) || !empty($courses) || !empty($departments)) + && !empty($reserves) + ) { + // Delete existing records + $this->solr->deleteAll('SolrReserves'); + + // Build and Save the index + $index = $this->buildReservesIndex( + $instructors, $courses, $departments, $reserves + ); + $this->solr->save('SolrReserves', $index); + + // Commit and Optimize the Solr Index + $this->solr->commit('SolrReserves'); + $this->solr->optimize('SolrReserves'); + + $output->writeln('Successfully loaded ' . count($reserves) . ' rows.'); + return 0; + } + $output->writeln('Unable to load data.'); + return 1; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/LintMarcCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/LintMarcCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..8ec5a11f3cdd0c5ce77bb1f87e242b49cba7040f --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/LintMarcCommand.php @@ -0,0 +1,93 @@ +<?php +/** + * Console command: Lint MARC records. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFindConsole\Command\RelativeFileAwareCommand; + +/** + * Console command: Lint MARC records. + * + * @category VuFind + * @package Console + * @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 LintMarcCommand extends RelativeFileAwareCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/lint_marc'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('MARC validator') + ->setHelp('This command lets you validate MARC file contents.') + ->addArgument('filename', InputArgument::REQUIRED, 'MARC filename'); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $filename = $input->getArgument('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'; + $output->writeln("Checking record $i (001 = $field001)..."); + $warnings = $linter->checkRecord($record); + if (count($warnings) > 0) { + $output->writeln('Warnings: ' . implode("\n", $warnings)); + } + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/OptimizeCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/OptimizeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..d246b4ad4bdc832c6c8e426b549bc43c07d9d9ba --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/OptimizeCommand.php @@ -0,0 +1,75 @@ +<?php +/** + * Console command: optimize Solr index + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: optimize Solr index + * + * @category VuFind + * @package Console + * @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 OptimizeCommand extends CommitCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/optimize'; + + /** + * The name of the Solr command, for use in help messages. + * + * @var string + */ + protected $solrCommand = 'optimize'; + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Optimize is the same as commit (parent class) but with an extra step: + $result = parent::execute($input, $output); + $core = $input->getArgument('core'); + $this->solr->optimize($core); + return $result; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..9698204583f97a29cde00538f90fbe2b5e3f8757 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommand.php @@ -0,0 +1,123 @@ +<?php +/** + * Console command: generate sitemaps + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Sitemap\Generator; + +/** + * Console command: generate sitemaps + * + * @category VuFind + * @package Console + * @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 SitemapCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/sitemap'; + + /** + * Sitemap generator + * + * @var Generator + */ + protected $generator; + + /** + * Constructor + * + * @param Generator $generator Sitemap generator + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Generator $generator, $name = null) + { + $this->generator = $generator; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('XML sitemap generator') + ->setHelp('Generates XML sitemap files.') + ->addOption( + 'baseurl', + null, + InputOption::VALUE_REQUIRED, + 'base URL (overrides the url setting in Site section of config.ini)' + )->addOption( + 'basesitemapurl', + null, + InputOption::VALUE_REQUIRED, + 'base sitemap URL (overrides the url setting in Site section of ' + . 'config.ini, or baseSitemapUrl in sitemap.ini)' + ); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->hasOption('verbose') && $input->getOption('verbose')) { + $this->generator->setVerbose([$output, 'writeln']); + } + if ($url = $input->getOption('baseurl')) { + $this->generator->setBaseUrl($url); + } + if ($sitemapUrl = $input->getOption('basesitemapurl')) { + $this->generator->setBaseSitemapUrl($sitemapUrl); + } + $this->generator->generate(); + foreach ($this->generator->getWarnings() as $warning) { + $output->writeln("$warning"); + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..23dece2d9d1942ed9ac8ab6bb7631b2b75aa5ad9 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/SitemapCommandFactory.php @@ -0,0 +1,66 @@ +<?php +/** + * Factory for Util/SitemapCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/SitemapCommand. + * + * @category VuFind + * @package Console + * @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 SitemapCommandFactory 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 + ) { + return new $requestedName( + $container->get(\VuFind\Sitemap\Generator::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/SuppressedCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/SuppressedCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..432d8ff01da4c6afae7b94f5878fbd0ae01dff7b --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/SuppressedCommand.php @@ -0,0 +1,136 @@ +<?php +/** + * Console command: remove suppressed records from index + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console command: remove suppressed records from index + * + * @category VuFind + * @package Console + * @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 SuppressedCommand extends AbstractSolrAndIlsCommand +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/suppressed'; + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Remove ILS-suppressed records from Solr') + ->setHelp( + 'This tool removes ILS-suppressed records from Solr.' + )->addOption( + 'authorities', + null, + InputOption::VALUE_NONE, + 'Delete authority records instead of bibliographic records' + )->addOption( + 'outfile', + null, + InputOption::VALUE_REQUIRED, + 'Write the ID list to the specified file instead of updating Solr' + ); + } + + /** + * Write content to disk. + * + * @param string $filename Target filename + * @param string $content Content to write + * + * @return bool + */ + protected function writeToDisk($filename, $content) + { + return file_put_contents($filename, $content); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Setup Solr Connection + $backend = $input->getOption('authorities') ? 'SolrAuth' : 'Solr'; + + // Make ILS Connection + try { + $result = ($backend == 'SolrAuth') + ? $this->catalog->getSuppressedAuthorityRecords() + : $this->catalog->getSuppressedRecords(); + } catch (\Exception $e) { + $output->writeln('ILS error -- ' . $e->getMessage()); + return 1; + } + + // Validate result: + if (!is_array($result)) { + $output->writeln('Could not obtain suppressed record list from ILS.'); + return 1; + } elseif (empty($result)) { + $output->writeln('No suppressed records to delete.'); + return 0; + } + + // If 'outfile' set, write the list + if ($file = $input->getOption('outfile')) { + if (!$this->writeToDisk($file, implode("\n", $result))) { + $output->writeln("Problem writing to $file"); + return 1; + } + } else { + // Default behavior: Delete from Solr index + $this->solr->deleteRecords($backend, $result); + $this->solr->commit($backend); + $this->solr->optimize($backend); + } + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommand.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..797d63a0711ced4bd5975f0c3d6221c07c97772f --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommand.php @@ -0,0 +1,220 @@ +<?php +/** + * Console command: switch database encryption algorithm. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Laminas\Config\Config; +use Laminas\Crypt\BlockCipher; +use Laminas\Crypt\Symmetric\Openssl; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use VuFind\Config\Locator as ConfigLocator; +use VuFind\Config\Writer as ConfigWriter; +use VuFind\Db\Table\User as UserTable; + +/** + * Console command: switch database encryption algorithm. + * + * @category VuFind + * @package Console + * @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 SwitchDbHashCommand extends Command +{ + /** + * The name of the command (the part after "public/index.php") + * + * @var string + */ + protected static $defaultName = 'util/switch_db_hash'; + + /** + * VuFind configuration. + * + * @var Config + */ + protected $config; + + /** + * User table gateway + * + * @var UserTable + */ + protected $userTable; + + /** + * Constructor + * + * @param Config $config VuFind configuration + * @param UserTable $userTable User table gateway + * @param string|null $name The name of the command; passing null means it + * must be set in configure() + */ + public function __construct(Config $config, UserTable $userTable, $name = null) + { + $this->config = $config; + $this->userTable = $userTable; + parent::__construct($name); + } + + /** + * Configure the command. + * + * @return void + */ + protected function configure() + { + $this + ->setDescription('Encryption algorithm switcher') + ->setHelp( + 'Switches the encryption algorithm in the database ' + . 'and config. Expects new algorithm and (optional) new key as' + . ' parameters.' + )->addArgument('newmethod', InputArgument::REQUIRED, 'Encryption method') + ->addArgument('newkey', InputArgument::OPTIONAL, 'Encryption key'); + } + + /** + * Get a config writer + * + * @param string $path Path of file to write + * + * @return ConfigWriter + */ + protected function getConfigWriter($path) + { + return new ConfigWriter($path); + } + + /** + * Get an OpenSsl object for the specified algorithm (or return null if the + * algorithm is 'none'). + * + * @param string $algorithm Encryption algorithm + * + * @return Openssl + */ + protected function getOpenSsl($algorithm) + { + return ($algorithm == 'none') ? null : new Openssl(compact('algorithm')); + } + + /** + * Run the command. + * + * @param InputInterface $input Input object + * @param OutputInterface $output Output object + * + * @return int 0 for success + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Validate command line arguments: + $newhash = $input->getArgument('newmethod'); + + // Pull existing encryption settings from the configuration: + if (!isset($this->config->Authentication->ils_encryption_key) + || !($this->config->Authentication->encrypt_ils_password ?? false) + ) { + $oldhash = 'none'; + $oldkey = null; + } else { + $oldhash = $this->config->Authentication->ils_encryption_algo + ?? 'blowfish'; + $oldkey = $this->config->Authentication->ils_encryption_key; + } + + // Pull new encryption settings from argument or config: + $newkey = $input->getArgument('newkey') ?? $oldkey; + + // No key specified AND no key on file = fatal error: + if ($newkey === null) { + $output->writeln('Please specify a key as the second parameter.'); + return 1; + } + + // If no changes were requested, abort early: + if ($oldkey == $newkey && $oldhash == $newhash) { + $output->writeln('No changes requested -- no action needed.'); + return 0; + } + + // Initialize Openssl first, so we can catch any illegal algorithms before + // making any changes: + try { + $oldCrypt = $this->getOpenSsl($oldhash); + $newCrypt = $this->getOpenSsl($newhash); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + return 1; + } + + // Next update the config file, so if we are unable to write the file, + // we don't go ahead and make unwanted changes to the database: + $configPath = ConfigLocator::getLocalConfigPath('config.ini', null, true); + $output->writeln("\tUpdating $configPath..."); + $writer = $this->getConfigWriter($configPath); + $writer->set('Authentication', 'encrypt_ils_password', true); + $writer->set('Authentication', 'ils_encryption_algo', $newhash); + $writer->set('Authentication', 'ils_encryption_key', $newkey); + if (!$writer->save()) { + $output->writeln("\tWrite failed!"); + return 1; + } + + // Now do the database rewrite: + $users = $this->userTable->select( + function ($select) { + $select->where->isNotNull('cat_username'); + } + ); + $output->writeln("\tConverting hashes for " . count($users) . ' user(s).'); + foreach ($users as $row) { + $pass = null; + if ($oldhash != 'none' && isset($row['cat_pass_enc'])) { + $oldcipher = new BlockCipher($oldCrypt); + $oldcipher->setKey($oldkey); + $pass = $oldcipher->decrypt($row['cat_pass_enc']); + } else { + $pass = $row['cat_password']; + } + $newcipher = new BlockCipher($newCrypt); + $newcipher->setKey($newkey); + $row['cat_password'] = null; + $row['cat_pass_enc'] = $newcipher->encrypt($pass); + $row->save(); + } + + // If we got this far, all went well! + $output->writeln("\tFinished."); + return 0; + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommandFactory.php b/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommandFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..5d3eca6ee0ffde3ba3e03976d8f309826a9a11ea --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/Command/Util/SwitchDbHashCommandFactory.php @@ -0,0 +1,70 @@ +<?php +/** + * Factory for Util/SwitchDbHashCommand. + * + * 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 Console + * @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 VuFindConsole\Command\Util; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for Util/SwitchDbHashCommand. + * + * @category VuFind + * @package Console + * @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 SwitchDbHashCommandFactory 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 + ) { + $config = $container->get(\VuFind\Config\PluginManager::class) + ->get('config'); + $tableManager = $container->get(\VuFind\Db\Table\PluginManager::class); + return new $requestedName( + $config, + $tableManager->get(\VuFind\Db\Table\User::class), + ...($options ?? []) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/ConsoleOutputTrait.php b/module/VuFindConsole/src/VuFindConsole/ConsoleOutputTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..880458c305a20d41ba66751ad186d06ca783331b --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/ConsoleOutputTrait.php @@ -0,0 +1,75 @@ +<?php +/** + * Console output trait (used to add output support to other classes). + * + * 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 Console + * @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 VuFindConsole; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Console output trait (used to add output support to other classes). + * + * @category VuFind + * @package Console + * @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 + */ +trait ConsoleOutputTrait +{ + /** + * Output interface. + * + * @var OutputInterface + */ + protected $outputInterface = null; + + /** + * Set the output interface. + * + * @param OutputInterface $output Output interface + * + * @return void + */ + public function setOutputInterface(OutputInterface $output): void + { + $this->outputInterface = $output; + } + + /** + * Write a line to the output (if available). + * + * @param string $output Line to output. + * + * @return void + */ + public function writeln(string $output): void + { + if ($this->outputInterface) { + $this->outputInterface->writeln($output); + } + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/ConsoleRunner.php b/module/VuFindConsole/src/VuFindConsole/ConsoleRunner.php new file mode 100644 index 0000000000000000000000000000000000000000..dc20c96c04fe2283ceb789684653179fc7f104c3 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/ConsoleRunner.php @@ -0,0 +1,112 @@ +<?php +/** + * Console runner. + * + * 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 Console + * @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 VuFindConsole; + +use Laminas\ServiceManager\ServiceManager; +use Symfony\Component\Console\Application; + +/** + * Console runner. + * + * @category VuFind + * @package Console + * @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 ConsoleRunner +{ + /** + * List of commands + * + * @var array + */ + protected $commands; + + /** + * Plugin manager (to retrieve commands) + * + * @var ServiceManager + */ + protected $pluginManager; + + /** + * Constructor + * + * @param ServiceManager $pm Plugin manager (to retrieve commands) + */ + public function __construct(ServiceManager $pm) + { + $this->pluginManager = $pm; + } + + /** + * Get the command or list of commands to run. + * + * @return array + */ + protected function getCommandList() + { + // Does the first argument match a command alias? If so, load only that: + if ($this->pluginManager->has($_SERVER['argv'][1] ?? '')) { + return [$_SERVER['argv'][1]]; + } + + // Do the first two arguments match a command alias? If so, manipulate + // the arguments (converting legacy format to Symfony format) and return + // the resulting command: + $command = ($_SERVER['argv'][1] ?? '') . '/' . ($_SERVER['argv'][2] ?? ''); + if ($this->pluginManager->has($command)) { + $_SERVER['argc']--; + array_splice($_SERVER['argv'], 1, 2, [$command]); + return [$command]; + } + + // Default behavior: return all values + return $this->pluginManager->getCommandList(); + } + + /** + * Run the console action + * + * @return mixed + */ + public function run() + { + // Get command list before initializing Application, since we may need + // to manipulate $_SERVER for backward compatibility. + $commands = $this->getCommandList(); + + // Launch Symfony: + $consoleApp = new Application(); + foreach ($commands as $command) { + $consoleApp->add($this->pluginManager->get($command)); + } + return $consoleApp->run(); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/ConsoleRunnerFactory.php b/module/VuFindConsole/src/VuFindConsole/ConsoleRunnerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..3020bf05fdbdb51eb84786a10855a2c7a9389650 --- /dev/null +++ b/module/VuFindConsole/src/VuFindConsole/ConsoleRunnerFactory.php @@ -0,0 +1,68 @@ +<?php +/** + * Console runner factory. + * + * 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 Console + * @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 VuFindConsole; + +use Interop\Container\ContainerInterface; +use Laminas\ServiceManager\Factory\FactoryInterface; + +/** + * Console runner factory. + * + * @category VuFind + * @package Console + * @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 ConsoleRunnerFactory 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(\VuFindConsole\Command\PluginManager::class) + ); + } +} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php b/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php deleted file mode 100644 index f0ae99bb4428b4e76937be1c0f35427e0fa2b9e5..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/AbstractBase.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -/** - * VuFind controller base class (defines some methods that can be shared by other - * controllers). - * - * 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/wiki/development:plugins:controllers Wiki - */ -namespace VuFindConsole\Controller; - -use Laminas\Console\Console; -use Laminas\Mvc\Controller\AbstractActionController; -use Laminas\ServiceManager\ServiceLocatorInterface; - -/** - * VuFind controller base class (defines some methods that can be shared by other - * controllers). - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class AbstractBase extends AbstractActionController -{ - /** - * Constructor - * - * @param ServiceLocatorInterface $sm Service locator - */ - public function __construct(ServiceLocatorInterface $sm) - { - // This controller should only be accessed from the command line! - if (PHP_SAPI != 'cli') { - throw new \Exception('Access denied to command line tools.'); - } - - $this->serviceLocator = $sm; - - // Switch the context back to the original working directory so that - // relative paths work as expected. (This constant is set in - // public/index.php) - if (defined('ORIGINAL_WORKING_DIRECTORY')) { - chdir(ORIGINAL_WORKING_DIRECTORY); - } - } - - /** - * Warn the user if VUFIND_LOCAL_DIR is not set. - * - * @return void - */ - protected function checkLocalSetting() - { - if (!getenv('VUFIND_LOCAL_DIR')) { - Console::writeLine( - "WARNING: The VUFIND_LOCAL_DIR environment variable is not set." - ); - Console::writeLine( - "This should point to your local configuration directory (i.e." - ); - Console::writeLine(realpath(APPLICATION_PATH . '/local') . ")."); - Console::writeLine( - "Without it, inappropriate default settings may be loaded." - ); - Console::writeLine(""); - } - } - - /** - * Indicate failure. - * - * @return \Laminas\Console\Response - */ - protected function getFailureResponse() - { - return $this->getResponse()->setErrorLevel(1); - } - - /** - * Indicate success. - * - * @return \Laminas\Console\Response - */ - protected function getSuccessResponse() - { - return $this->getResponse()->setErrorLevel(0); - } - - /** - * Get a VuFind configuration. - * - * @param string $id Configuration identifier (default = main VuFind config) - * - * @return \Laminas\Config\Config - */ - public function getConfig($id = 'config') - { - return $this->serviceLocator - ->get(\VuFind\Config\PluginManager::class)->get($id); - } - - /** - * Get the ILS connection. - * - * @return \VuFind\ILS\Connection - */ - public function getILS() - { - return $this->serviceLocator->get(\VuFind\ILS\Connection::class); - } - - /** - * Get a database table object. - * - * @param string $table Name of table to retrieve - * - * @return \VuFind\Db\Table\Gateway - */ - public function getTable($table) - { - 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 deleted file mode 100644 index 5834527ed414491cd91ff5df6e569ae17a00fa7f..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/CompileController.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -/** - * Compile Controller Module - * - * PHP version 7 - * - * Copyright (C) Villanova University 2017. - * - * 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/wiki/development:plugins:controllers Wiki - */ -namespace VuFindConsole\Controller; - -use Laminas\Console\Console; - -/** - * This controller handles the command-line tool for compiling themes. - * - * @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/wiki/development:plugins:controllers Wiki - */ -class CompileController extends AbstractBase -{ - /** - * Compile theme action. - * - * @return mixed - */ - public function themeAction() - { - $request = $this->getRequest(); - $source = $request->getParam('source'); - if (empty($source)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() - . ' compile theme [--force] SOURCE [TARGET]' - ); - Console::writeLine("\tSOURCE - the source theme to compile (required)"); - Console::writeLine( - "\tTARGET - the target name for the compiled theme " - . '(optional; defaults to SOURCE_compiled)' - ); - Console::writeLine( - "(If TARGET exists, it will only be overwritten when --force is set)" - ); - return $this->getFailureResponse(); - } - $target = $request->getParam('target'); - if (empty($target)) { - $target = "{$source}_compiled"; - } - $compiler = $this->serviceLocator->get(\VuFindTheme\ThemeCompiler::class); - if (!$compiler->compile($source, $target, $request->getParam('force'))) { - Console::writeLine($compiler->getLastError()); - return $this->getFailureResponse(); - } - Console::writeLine('Success.'); - return $this->getSuccessResponse(); - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php b/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php deleted file mode 100644 index db4b3b97772631705471a277bdae21027fa536b8..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/GenerateController.php +++ /dev/null @@ -1,409 +0,0 @@ -<?php -/** - * CLI Controller Module (language tools) - * - * 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 Chris Hallberg <challber@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 Laminas\Console\Console; - -/** - * This controller handles various command-line tools for dealing with language files - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class GenerateController extends AbstractBase -{ - /** - * Add a new dynamic route definition - * - * @return \Laminas\Console\Response - */ - public function dynamicrouteAction() - { - $request = $this->getRequest(); - $route = $request->getParam('name'); - $controller = $request->getParam('newController'); - $action = $request->getParam('newAction'); - $module = $request->getParam('module'); - if (empty($module)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate dynamicroute' - . ' [route] [controller] [action] [target_module]' - ); - Console::writeLine( - "\troute - the route name (used by router), e.g. customList" - ); - Console::writeLine( - "\tcontroller - the controller name (used in URL), e.g. MyResearch" - ); - Console::writeLine( - "\taction - the action and segment params, e.g. CustomList/[:id]" - ); - Console::writeLine( - "\ttarget_module - the module where the new route will be generated" - ); - return $this->getFailureResponse(); - } - - // Create backup of configuration - $generator = $this->getGeneratorTools(); - $configPath = $generator->getModuleConfigPath($module); - $generator->backUpFile($configPath); - - // Append the route - $config = include $configPath; - $routeGenerator = new \VuFind\Route\RouteGenerator(); - $routeGenerator->addDynamicRoute($config, $route, $controller, $action); - - // Write updated configuration - $generator->writeModuleConfig($configPath, $config); - return $this->getSuccessResponse(); - } - - /** - * Extend an existing class - * - * @return \Laminas\Console\Response - */ - public function extendclassAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $class = $request->getParam('class'); - $target = $request->getParam('target'); - $extendFactory = $request->getParam('extendfactory'); - - if (empty($class) || empty($target)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate extendclass' - . ' [--extendfactory] [class_name] [target_module]' - ); - Console::writeLine( - "\t--extendfactory - optional switch; when set, subclass " - . 'the factory; otherwise, use existing factory' - ); - Console::writeLine( - "\tclass_name - the name of the class you wish to extend" - ); - Console::writeLine( - "\ttarget_module - the module where the new class will be generated" - ); - return $this->getFailureResponse(); - } - - try { - $this->getGeneratorTools()->extendClass( - $this->serviceLocator, $class, $target, $extendFactory - ); - } catch (\Exception $e) { - Console::writeLine($e->getMessage()); - return $this->getFailureResponse(); - } - - return $this->getSuccessResponse(); - } - - /** - * Extend an existing service - * - * @return \Laminas\Console\Response - */ - public function extendserviceAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $source = $request->getParam('source'); - $target = $request->getParam('target'); - if (empty($source) || empty($target)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate extendservice' - . ' [config_path] [target_module]' - ); - Console::writeLine( - "\tconfig_path - the path to the service in the framework config" - ); - Console::writeLine("\t\te.g. controllers/invokables/generate"); - Console::writeLine( - "\ttarget_module - the module where the new class will be generated" - ); - return $this->getFailureResponse(); - } - - try { - $this->getGeneratorTools()->extendService($source, $target); - } catch (\Exception $e) { - Console::writeLine($e->getMessage()); - return $this->getFailureResponse(); - } - - return $this->getSuccessResponse(); - } - - /** - * Add a new non-tab record action to all existing record routes - * - * @return \Laminas\Console\Response - */ - public function nontabrecordactionAction() - { - $request = $this->getRequest(); - $action = $request->getParam('newAction'); - $module = $request->getParam('module'); - if (empty($action) || empty($module)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() - . ' generate nontabrecordaction [action] [target_module]' - ); - Console::writeLine( - "\taction - new action to add" - ); - Console::writeLine( - "\ttarget_module - the module where the new routes will be generated" - ); - return $this->getFailureResponse(); - } - - // Create backup of configuration - $generator = $this->getGeneratorTools(); - $configPath = $generator->getModuleConfigPath($module); - $generator->backUpFile($configPath); - - // Load the route config - $config = include $configPath; - - // Append the route - $mainConfig = $this->serviceLocator->get('Config'); - foreach ($mainConfig['router']['routes'] as $key => $val) { - if (isset($val['options']['route']) - && substr($val['options']['route'], -14) == '[:id[/[:tab]]]' - ) { - $newRoute = $key . '-' . strtolower($action); - if (isset($mainConfig['router']['routes'][$newRoute])) { - Console::writeLine($newRoute . ' already exists; skipping.'); - } else { - $val['options']['route'] = str_replace( - '[:id[/[:tab]]]', "[:id]/$action", $val['options']['route'] - ); - $val['options']['defaults']['action'] = $action; - $config['router']['routes'][$newRoute] = $val; - } - } - } - - // Write updated configuration - $generator->writeModuleConfig($configPath, $config); - return $this->getSuccessResponse(); - } - - /** - * Create a new plugin class - * - * @return \Laminas\Console\Response - */ - public function pluginAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $class = $request->getParam('class'); - $factory = $request->getParam('factory'); - - if (empty($class)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate plugin' - . ' [class_name] [factory]' - ); - Console::writeLine( - "\tclass_name - the name of the class you wish to create" - ); - Console::writeLine( - "\tfactory - an existing factory to use (omit to generate a new one)" - ); - return $this->getFailureResponse(); - } - - try { - $this->getGeneratorTools() - ->createPlugin($this->serviceLocator, $class, $factory); - } catch (\Exception $e) { - Console::writeLine($e->getMessage()); - return $this->getFailureResponse(); - } - - return $this->getSuccessResponse(); - } - - /** - * Add a new record route definition - * - * @return \Laminas\Console\Response - */ - public function recordrouteAction() - { - $request = $this->getRequest(); - $base = $request->getParam('base'); - $controller = $request->getParam('newController'); - $module = $request->getParam('module'); - if (empty($module)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate recordroute' - . ' [base] [controller] [target_module]' - ); - Console::writeLine( - "\tbase - the base route name (used by router), e.g. record" - ); - Console::writeLine( - "\tcontroller - the controller name (used in URL), e.g. Record" - ); - Console::writeLine( - "\ttarget_module - the module where the new route will be generated" - ); - return $this->getFailureResponse(); - } - - // Create backup of configuration - $generator = $this->getGeneratorTools(); - $configPath = $generator->getModuleConfigPath($module); - $generator->backUpFile($configPath); - - // Append the route - $config = include $configPath; - $routeGenerator = new \VuFind\Route\RouteGenerator(); - $routeGenerator->addRecordRoute($config, $base, $controller); - - // Write updated configuration - $generator->writeModuleConfig($configPath, $config); - return $this->getSuccessResponse(); - } - - /** - * Add a new static route definition - * - * @return \Laminas\Console\Response - */ - public function staticrouteAction() - { - $request = $this->getRequest(); - $route = $request->getParam('name'); - $module = $request->getParam('module'); - if (empty($module)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' generate staticroute' - . ' [route_definition] [target_module]' - ); - Console::writeLine( - "\troute_definition - a Controller/Action string, e.g. Search/Home" - ); - Console::writeLine( - "\ttarget_module - the module where the new route will be generated" - ); - return $this->getFailureResponse(); - } - - // Create backup of configuration - $generator = $this->getGeneratorTools(); - $configPath = $generator->getModuleConfigPath($module); - $generator->backUpFile($configPath); - - // Append the route - $config = include $configPath; - $routeGenerator = new \VuFind\Route\RouteGenerator(); - $routeGenerator->addStaticRoute($config, $route); - - // Write updated configuration - $generator->writeModuleConfig($configPath, $config); - return $this->getSuccessResponse(); - } - - /** - * Create a custom theme from the template, configure. - * - * @return \Laminas\Console\Response - */ - public function themeAction() - { - // Validate command line argument: - $request = $this->getRequest(); - $name = $request->getParam('themename'); - if (empty($name)) { - Console::writeLine("\tNo themename found, using \"custom\""); - $name = 'custom'; - } - - // Use the theme generator to create and configure the theme: - $generator = $this->serviceLocator->get(\VuFindTheme\ThemeGenerator::class); - if (!$generator->generate($name) - || !$generator->configure($this->getConfig(), $name) - ) { - Console::writeLine($generator->getLastError()); - return $this->getFailureResponse(); - } - Console::writeLine("\tFinished."); - return $this->getSuccessResponse(); - } - - /** - * Create a custom theme from the template. - * - * @return \Laminas\Console\Response - */ - public function thememixinAction() - { - // Validate command line argument: - $request = $this->getRequest(); - $name = $request->getParam('name'); - if (empty($name)) { - Console::writeLine("\tNo mixin name found, using \"custom\""); - $name = 'custom'; - } - - // Use the theme generator to create and configure the theme: - $generator = $this->serviceLocator->get(\VuFindTheme\MixinGenerator::class); - if (!$generator->generate($name)) { - Console::writeLine($generator->getLastError()); - return $this->getFailureResponse(); - } - Console::writeLine( - "\tFinished. Add to your theme.config.php 'mixins' setting to activate." - ); - return $this->getSuccessResponse(); - } - - /** - * Get generator tools - * - * @return \VuFindConsole\Generator\GeneratorTools - */ - protected function getGeneratorTools() - { - 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 deleted file mode 100644 index ed0f0d657a2779d79c646aad89bbfbc9a369290a..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/HarvestController.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php -/** - * CLI Controller Module - * - * 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 Chris Hallberg <challber@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 Laminas\Console\Console; -use VuFindHarvest\OaiPmh\HarvesterConsoleRunner; - -/** - * This controller handles various command-line tools - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class HarvestController extends AbstractBase -{ - /** - * Get the base directory for harvesting OAI-PMH data. - * - * @return string - */ - protected function getHarvestRoot() - { - // Get the base VuFind path: - if (strlen(LOCAL_OVERRIDE_DIR) > 0) { - $home = LOCAL_OVERRIDE_DIR; - } else { - $home = realpath(APPLICATION_PATH . '/..'); - } - - // Build the full harvest path: - $dir = $home . '/harvest/'; - - // Create the directory if it does not already exist: - if (!is_dir($dir) && !mkdir($dir)) { - throw new \Exception("Problem creating directory {$dir}."); - } - - return $dir; - } - - /** - * Harvest OAI-PMH records. - * - * @return \Laminas\Console\Response - */ - public function harvestoaiAction() - { - $this->checkLocalSetting(); - - // Get default options, add the default --ini setting if missing: - $opts = HarvesterConsoleRunner::getDefaultOptions(); - $opts->setArguments($this->getRequest()->getParam('params')); - if (!$opts->getOption('ini')) { - $ini = \VuFind\Config\Locator::getConfigPath('oai.ini', 'harvest'); - $opts->addArguments(['--ini=' . $ini]); - } - - // Get the default VuFind HTTP client: - $client = $this->serviceLocator->get(\VuFindHttp\HttpService::class) - ->createClient(); - - // Run the job! - $runner = new HarvesterConsoleRunner( - $opts, $client, $this->getHarvestRoot() - ); - return $runner->run() - ? $this->getSuccessResponse() : $this->getFailureResponse(); - } - - /** - * Merge harvested MARC records into a single <collection> - * - * @return \Laminas\Console\Response - * @author Thomas Schwaerzler <thomas.schwaerzler@uibk.ac.at> - */ - public function mergemarcAction() - { - $this->checkLocalSetting(); - - $dir = rtrim($this->getRequest()->getParam('dir', ''), '/'); - if (empty($dir)) { - $scriptName = $this->getRequest()->getScriptName(); - if (substr($scriptName, -9) === 'index.php') { - $scriptName .= ' harvest merge-marc'; - } - Console::writeLine('Merge MARC XML files into a single <collection>;'); - Console::writeLine('writes to stdout.'); - Console::writeLine(''); - Console::writeLine('Usage: ' . $scriptName . ' <path_to_directory>'); - Console::writeLine( - '<path_to_directory>: a directory containing MARC XML files to merge' - ); - return $this->getFailureResponse(); - } - - if (!($handle = opendir($dir))) { - Console::writeLine("Cannot open directory: {$dir}"); - return $this->getFailureResponse(); - } - - Console::writeLine('<collection>'); - while (false !== ($file = readdir($handle))) { - // Only operate on XML files: - if (pathinfo($file, PATHINFO_EXTENSION) === "xml") { - // get file content - $filePath = $dir . '/' . $file; - $fileContent = file_get_contents($filePath); - - // output content: - Console::writeLine("<!-- $filePath -->"); - Console::write($fileContent); - } - } - Console::writeLine('</collection>'); - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php b/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php deleted file mode 100644 index 098279b7c25755dc8a9d58580cbe613f09e17dd9..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/ImportController.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php -/** - * CLI Controller Module - * - * 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 Chris Hallberg <challber@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 Laminas\Console\Console; -use VuFind\XSLT\Importer; - -/** - * This controller handles various command-line tools - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class ImportController extends AbstractBase -{ - /** - * XSLT Import Tool - * - * @return \Laminas\Console\Response - */ - public function importXslAction() - { - $request = $this->getRequest(); - $testMode = $request->getParam('test-only') ? true : false; - $index = $request->getParam('index', 'Solr'); - $xml = $request->getParam('xml'); - $properties = $request->getParam('properties'); - if (empty($properties)) { - $scriptName = $this->getRequest()->getScriptName(); - if (substr($scriptName, -9) === 'index.php') { - $scriptName .= ' import import-xsl'; - } - Console::writeLine( - "Usage: $scriptName [--test-only] [--index <type>] " - . 'XML_file properties_file' - ); - Console::writeLine("\tXML_file - source file to index"); - Console::writeLine("\tproperties_file - import configuration file"); - Console::writeLine( - "If the optional --test-only flag is set, " - . "transformed XML will be displayed" - ); - Console::writeLine( - "on screen for debugging purposes, " - . "but it will not be indexed into VuFind." - ); - Console::writeLine(""); - Console::writeLine( - "If the optional --index parameter is set, " - . "it must be followed by the name of" - ); - Console::writeLine( - "a class for accessing Solr; it defaults to the " - . "standard Solr class, but could" - ); - Console::writeLine( - "be overridden with, for example, SolrAuth to " - . "load authority records." - ); - Console::writeLine(""); - Console::writeLine( - "Note: See ojs.properties for configuration examples." - ); - return $this->getFailureResponse(); - } - - // Try to import the document if successful: - try { - $this->performImport($xml, $properties, $index, $testMode); - } catch (\Exception $e) { - Console::writeLine("Fatal error: " . $e->getMessage()); - if (is_callable([$e, 'getPrevious']) && $e = $e->getPrevious()) { - while ($e) { - Console::writeLine("Previous exception: " . $e->getMessage()); - $e = $e->getPrevious(); - } - } - return $this->getFailureResponse(); - } - if (!$testMode) { - Console::writeLine("Successfully imported $xml..."); - } - return $this->getSuccessResponse(); - } - - /** - * Support method -- perform an XML import. - * - * @param string $xml XML file to load - * @param string $properties Configuration file to load - * @param string $index Name of backend to write to - * @param bool $testMode Use test mode? - * - * @return void - */ - protected function performImport($xml, $properties, $index = 'Solr', - $testMode = false - ) { - $importer = new Importer($this->serviceLocator); - $importer->save($xml, $properties, $index, $testMode); - } - - /** - * Tool to crawl website for special index. - * - * @return \Laminas\Console\Response - */ - public function webcrawlAction() - { - // Get command line parameters: - $request = $this->getRequest(); - $testMode = $request->getParam('test-only') ? true : false; - $index = $request->getParam('index', 'SolrWeb'); - - $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 - // date after everything is finished. Note that we subtract a few seconds - // for safety. - $startTime = date('Y-m-d\TH:i:s\Z', time() - 5); - - // Are we in verbose mode? - $verbose = isset($crawlConfig->General->verbose) - && $crawlConfig->General->verbose; - - // Loop through sitemap URLs in the config file. - foreach ($crawlConfig->Sitemaps->url as $current) { - $this->harvestSitemap($current, $verbose, $index, $testMode); - } - - // Skip Solr operations if we're in test mode. - if (!$testMode) { - $solr = $this->serviceLocator->get(\VuFind\Solr\Writer::class); - if ($verbose) { - Console::writeLine("Deleting old records (prior to $startTime)..."); - } - // Perform the delete of outdated records: - $solr->deleteByQuery($index, 'last_indexed:[* TO ' . $startTime . ']'); - if ($verbose) { - Console::writeLine('Committing...'); - } - $solr->commit($index); - if ($verbose) { - Console::writeLine('Optimizing...'); - } - $solr->optimize($index); - } - } - - /** - * Support method for webcrawlAction(). - * - * Process a sitemap URL, either harvesting its contents directly or recursively - * reading in child sitemaps. - * - * @param string $url URL of sitemap to read. - * @param bool $verbose Are we in verbose mode? - * @param string $index Solr index to update - * @param bool $testMode Are we in test mode? - * - * @return bool True on success, false on error. - */ - protected function harvestSitemap($url, $verbose = false, $index = 'SolrWeb', - $testMode = false - ) { - if ($verbose) { - Console::writeLine("Harvesting $url..."); - } - - $retVal = true; - - $file = tempnam('/tmp', 'sitemap'); - file_put_contents($file, file_get_contents($url)); - $xml = simplexml_load_file($file); - if ($xml) { - // Are there any child sitemaps? If so, pull them in: - $results = isset($xml->sitemap) ? $xml->sitemap : []; - foreach ($results as $current) { - if (isset($current->loc)) { - $success = $this->harvestSitemap( - (string)$current->loc, $verbose, $index, $testMode - ); - if (!$success) { - $retVal = false; - } - } - } - // Only import the current sitemap if it contains URLs! - if (isset($xml->url)) { - try { - $this->performImport( - $file, 'sitemap.properties', $index, $testMode - ); - } catch (\Exception $e) { - if ($verbose) { - Console::writeLine(get_class($e) . ': ' . $e->getMessage()); - } - $retVal = false; - } - } - } - unlink($file); - return $retVal; - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php b/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php deleted file mode 100644 index e44bbaaef67dff9469364b1bf2975e858c4ed455..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/LanguageController.php +++ /dev/null @@ -1,366 +0,0 @@ -<?php -/** - * CLI Controller Module (language tools) - * - * 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 Chris Hallberg <challber@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 Laminas\Console\Console; -use VuFind\I18n\ExtendedIniNormalizer; -use VuFind\I18n\Translator\Loader\ExtendedIniReader; - -/** - * This controller handles various command-line tools for dealing with language files - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class LanguageController extends AbstractBase -{ - /** - * Copy one language string to another - * - * @return \Laminas\Console\Response - */ - public function copystringAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $source = $request->getParam('source'); - $target = $request->getParam('target'); - if (empty($source) || empty($target)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() - . ' language copystring [source] [target]' - ); - Console::writeLine("\tsource - the source key to read"); - Console::writeLine("\ttarget - the target key to write"); - Console::writeLine( - "(source and target may include 'textdomain::' prefix)" - ); - return $this->getFailureResponse(); - } - - $reader = new ExtendedIniReader(); - $normalizer = new ExtendedIniNormalizer(); - list($sourceDomain, $sourceKey) = $this->extractTextDomain($source); - list($targetDomain, $targetKey) = $this->extractTextDomain($target); - - if (!($sourceDir = $this->getLangDir($sourceDomain)) - || !($targetDir = $this->getLangDir($targetDomain, true)) - ) { - return $this->getFailureResponse(); - } - - // First, collect the source values from the source text domain: - $sources = []; - $sourceCallback = function ($full) use ($sourceKey, $reader, & $sources) { - $strings = $reader->getTextDomain($full, false); - if (!isset($strings[$sourceKey])) { - Console::writeLine("Source key not found."); - } else { - $sources[basename($full)] = $strings[$sourceKey]; - } - }; - $this->processDirectory($sourceDir, $sourceCallback); - - // Make sure that all target files exist: - $this->createMissingFiles($targetDir->path, array_keys($sources)); - - // Now copy the values to their destination: - $targetCallback = function ($full) use ($targetKey, $normalizer, $sources) { - if (isset($sources[basename($full)])) { - $fHandle = fopen($full, "a"); - fputs( - $fHandle, - "\n$targetKey = \"" . $sources[basename($full)] . "\"\n" - ); - fclose($fHandle); - $normalizer->normalizeFile($full); - } - }; - $this->processDirectory($targetDir, $targetCallback); - - return $this->getSuccessResponse(); - } - - /** - * Assemble a new language string by combining existing ones using a - * template. - * - * @return \Laminas\Console\Response - */ - public function addusingtemplateAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $target = $request->getParam('target'); - $template = $request->getParam('template'); - if (empty($template)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() - . ' language addusingtemplate [target] [template]' - ); - Console::writeLine( - "\ttarget - the target key to add " - . "(may include 'textdomain::' prefix)\n" - . "\ttemplate - the template to build the string, using ||string||" - . " to import existing strings" - ); - return $this->getFailureResponse(); - } - - // Make sure a valid target has been specified: - list($targetDomain, $targetKey) = $this->extractTextDomain($target); - if (!($targetDir = $this->getLangDir($targetDomain, true))) { - return $this->getFailureResponse(); - } - - // Extract required source values from template: - preg_match_all('/\|\|[^|]+\|\|/', $template, $matches); - $lookups = []; - foreach ($matches[0] as $current) { - $key = trim($current, '|'); - list($sourceDomain, $sourceKey) = $this->extractTextDomain($key); - $lookups[$sourceDomain][$current] = [ - 'key' => $sourceKey, - 'translations' => [] - ]; - } - - // Look up translations of all references in template: - $reader = new ExtendedIniReader(); - foreach ($lookups as $domain => & $tokens) { - $sourceDir = $this->getLangDir($domain, false); - if (!$sourceDir) { - return $this->getFailureResponse(); - } - $sourceCallback = function ($full) use ( - $domain, & $tokens, $reader - ) { - $strings = $reader->getTextDomain($full, false); - foreach ($tokens as & $current) { - $sourceKey = $current['key']; - if (isset($strings[$sourceKey])) { - $current['translations'][basename($full)] - = $strings[$sourceKey]; - } - } - }; - $this->processDirectory($sourceDir, $sourceCallback, false); - } - - // Fill in template, write results: - $normalizer = new ExtendedIniNormalizer(); - $targetCallback = function ($full) use ( - $template, $targetKey, $normalizer, $lookups - ) { - $lang = basename($full); - $in = $out = []; - foreach ($lookups as $domain => $tokens) { - foreach ($tokens as $token => $details) { - if (isset($details['translations'][$lang])) { - $in[] = $token; - $out[] = $details['translations'][$lang]; - } else { - Console::writeLine( - 'Skipping; no match for token: ' . $token - ); - return; - } - } - } - $fHandle = fopen($full, "a"); - fputs( - $fHandle, - "\n$targetKey = \"" . str_replace($in, $out, $template) . "\"\n" - ); - fclose($fHandle); - $normalizer->normalizeFile($full); - }; - $this->processDirectory($targetDir, $targetCallback); - } - - /** - * Delete a language string to another - * - * @return \Laminas\Console\Response - */ - public function deleteAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $target = $request->getParam('target'); - if (empty($target)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() . ' language delete [target]' - ); - Console::writeLine( - "\ttarget - the target key to remove " - . "(may include 'textdomain::' prefix)" - ); - return $this->getFailureResponse(); - } - - $normalizer = new ExtendedIniNormalizer(); - list($domain, $key) = $this->extractTextDomain($target); - $target = $key . ' = "'; - - if (!($dir = $this->getLangDir($domain))) { - return $this->getFailureResponse(); - } - $callback = function ($full) use ($target, $normalizer) { - $lines = file($full); - $out = ''; - $found = false; - foreach ($lines as $line) { - if (substr($line, 0, strlen($target)) !== $target) { - $out .= $line; - } else { - $found = true; - } - } - if ($found) { - file_put_contents($full, $out); - $normalizer->normalizeFile($full); - } else { - Console::writeLine("Source key not found."); - } - }; - $this->processDirectory($dir, $callback); - - return $this->getSuccessResponse(); - } - - /** - * Normalizer - * - * @return \Laminas\Console\Response - */ - public function normalizeAction() - { - // Display help message if parameters missing: - $request = $this->getRequest(); - $target = $request->getParam('target'); - if (empty($target)) { - Console::writeLine( - 'Usage: ' . $request->getScriptName() - . ' language normalize [target]' - ); - Console::writeLine("\ttarget - a file or directory to normalize"); - return $this->getFailureResponse(); - } - - $normalizer = new ExtendedIniNormalizer(); - if (is_dir($target)) { - $normalizer->normalizeDirectory($target); - } elseif (is_file($target)) { - $normalizer->normalizeFile($target); - } else { - Console::writeLine("{$target} does not exist."); - return $this->getFailureResponse(); - } - return $this->getSuccessResponse(); - } - - /** - * Extract a text domain and key from a raw language key. - * - * @param string $raw Raw language key - * - * @return array [textdomain, key] - */ - protected function extractTextDomain($raw) - { - $parts = explode('::', $raw, 2); - return count($parts) > 1 ? $parts : ['default', $raw]; - } - - /** - * Open the language directory as an object using dir(). Return false on - * failure. - * - * @param string $domain Text domain to retrieve. - * @param bool $createIfMissing Should we create a missing directory? - * - * @return object|bool - */ - protected function getLangDir($domain = 'default', $createIfMissing = false) - { - $subDir = $domain == 'default' ? '' : ('/' . $domain); - $langDir = __DIR__ . '/../../../../../languages' . $subDir; - if ($createIfMissing && !is_dir($langDir)) { - mkdir($langDir); - } - $dir = dir(realpath($langDir)); - if (!$dir) { - Console::writeLine("Could not open directory $langDir"); - return false; - } - return $dir; - } - - /** - * Create empty files if they do not already exist. - * - * @param string $path Directory path - * @param array $files Filenames to create in directory - * - * @return void - */ - protected function createMissingFiles($path, $files) - { - foreach ($files as $file) { - if (!file_exists($path . '/' . $file)) { - file_put_contents($path . '/' . $file, ''); - } - } - } - - /** - * Process a language directory. - * - * @param object $dir Directory object from dir() to process - * @param Callable $callback Function to run on all .ini files in $dir - * @param bool $showStatus Should we display status messages? - * - * @return void - */ - protected function processDirectory($dir, $callback, $showStatus = true) - { - while ($file = $dir->read()) { - // Only process .ini files, and ignore native.ini special case file: - if (substr($file, -4) == '.ini' && $file !== 'native.ini') { - if ($showStatus) { - Console::writeLine("Processing $file..."); - } - $callback($dir->path . '/' . $file); - } - } - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php b/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php deleted file mode 100644 index 9e3622f632ec4aa258de70a991369af5a02c59c6..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/src/VuFindConsole/Controller/RedirectController.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * Redirect Controller - * - * 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 Controller - * @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:plugins:controllers Wiki - */ -namespace VuFindConsole\Controller; - -use Laminas\Console\Console; -use Laminas\Mvc\Application; - -/** - * This controller handles various command-line tools - * - * @category VuFind - * @package Controller - * @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:plugins:controllers Wiki - */ -class RedirectController extends AbstractBase -{ - /** - * Get a usage message with the help of the RouteNotFoundStrategy. - * - * @return mixed - */ - protected function getUsage() - { - $strategy = $this->serviceLocator->get('ConsoleRouteNotFoundStrategy'); - $event = $this->getEvent(); - $event->setError(Application::ERROR_ROUTER_NO_MATCH); - $strategy->handleRouteNotFoundError($event); - return $event->getResult(); - } - - /** - * Use the first two command line parameters to redirect the user to an - * appropriate controller. - * - * @return mixed - */ - public function consoledefaultAction() - { - // We need to modify the $_SERVER superglobals so that - // \Laminas\Console\GetOpt will behave correctly after we've manipulated the - // CLI parameters. Let's use references for convenience. - $argv = & $_SERVER['argv']; - $argc = & $_SERVER['argc']; - - // Pull the script name off the front of the argument array: - $script = array_shift($argv); - - // Fail if we don't have at least two arguments (controller/action): - if ($argc < 2) { - return $this->getUsage(); - } - - // Pull off the controller and action. - $controller = array_shift($argv); - $action = array_shift($argv); - - // In case later scripts are displaying $argv[0] for the script name, - // let's push the full invocation into that position when index.php is - // used. We want to eliminate the $controller and $action values as separate - // parts of the array since they'll confuse subsequent parameter processing. - if (substr($script, -9) === 'index.php') { - $script .= " $controller $action"; - } - array_unshift($argv, $script); - $argc -= 2; - - try { - return $this->forward()->dispatch($controller, compact('action')); - } catch (\Exception $e) { - Console::writeLine('ERROR: ' . $e->getMessage()); - return $this->getUsage(); - } - } -} diff --git a/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php index 691c01e74dd108446a8d160ed4aa4d2ea3e56f50..0138401b8836a6ad2be2e5c58cb11482215621ad 100644 --- a/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php +++ b/module/VuFindConsole/src/VuFindConsole/Generator/GeneratorTools.php @@ -32,7 +32,6 @@ use Laminas\Code\Generator\ClassGenerator; use Laminas\Code\Generator\FileGenerator; use Laminas\Code\Generator\MethodGenerator; use Laminas\Code\Reflection\ClassReflection; -use Laminas\Console\Console; /** * Generator tools. @@ -45,6 +44,8 @@ use Laminas\Console\Console; */ class GeneratorTools { + use \VuFindConsole\ConsoleOutputTrait; + /** * Laminas configuration * @@ -441,8 +442,8 @@ class GeneratorTools // __callStatic and ignore the error. Any other exception should be // treated as a fatal error. if (method_exists($factoryClass, '__callStatic')) { - Console::writeLine('Error: ' . $e->getMessage()); - Console::writeLine( + $this->writeln('Error: ' . $e->getMessage()); + $this->writeln( '__callStatic in parent factory; skipping method generation.' ); } else { @@ -575,7 +576,7 @@ class GeneratorTools if (!file_put_contents($fullPath, $code)) { throw new \Exception("Problem writing to $fullPath."); } - Console::writeLine("Saved file: $fullPath"); + $this->writeln("Saved file: $fullPath"); } /** @@ -637,7 +638,7 @@ class GeneratorTools if (!copy($filename, $backup)) { throw new \Exception("Problem generating backup file: $backup"); } - Console::writeLine("Created backup: $backup"); + $this->writeln("Created backup: $backup"); } /** @@ -677,7 +678,7 @@ class GeneratorTools if (!file_put_contents($configPath, $generator->generate())) { throw new \Exception("Cannot write to $configPath"); } - Console::writeLine("Successfully updated $configPath"); + $this->writeln("Successfully updated $configPath"); } /** diff --git a/module/VuFindConsole/tests/fixtures/deletes b/module/VuFindConsole/tests/fixtures/deletes new file mode 100644 index 0000000000000000000000000000000000000000..55eb55adeb87b3c763d707d4dcef1616aa6133fb --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/deletes @@ -0,0 +1,6 @@ +rec1 + + + + rec2 +rec3 diff --git a/module/VuFindConsole/tests/fixtures/empty.config.php b/module/VuFindConsole/tests/fixtures/empty.config.php new file mode 100644 index 0000000000000000000000000000000000000000..881ab67d036d4900e0c6aef94f47961fe0c03b5a --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/empty.config.php @@ -0,0 +1,2 @@ +<?php +return []; diff --git a/module/VuFindConsole/tests/fixtures/fileWithDuplicateLines b/module/VuFindConsole/tests/fixtures/fileWithDuplicateLines new file mode 100644 index 0000000000000000000000000000000000000000..35074e022c2a731fff366ae6706d1835d9b4bd77 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/fileWithDuplicateLines @@ -0,0 +1,6 @@ +foo +foo +foo +bar +baz +baz diff --git a/module/VuFindConsole/tests/fixtures/language/foo/en.ini b/module/VuFindConsole/tests/fixtures/language/foo/en.ini new file mode 100644 index 0000000000000000000000000000000000000000..ca31e16d8853e46acdc477b2a936631bb7447a0d --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/language/foo/en.ini @@ -0,0 +1 @@ +bar = "baz" \ No newline at end of file diff --git a/module/VuFindConsole/tests/fixtures/reserves/fixture1 b/module/VuFindConsole/tests/fixtures/reserves/fixture1 new file mode 100644 index 0000000000000000000000000000000000000000..fc8216d90eff18358fffd857f02475ef38d9e5da --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/reserves/fixture1 @@ -0,0 +1 @@ +1|junk|course1|dept1|inst1 \ No newline at end of file diff --git a/module/VuFindConsole/tests/fixtures/reserves/fixture2 b/module/VuFindConsole/tests/fixtures/reserves/fixture2 new file mode 100644 index 0000000000000000000000000000000000000000..9e825c052d0e1fe11ee964f223db8ea16ab35145 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/reserves/fixture2 @@ -0,0 +1,2 @@ +2|junk|course2|dept2|inst2 +3|junk|course3|dept3|inst3 \ No newline at end of file diff --git a/module/VuFindConsole/tests/fixtures/sitemap/index.xml b/module/VuFindConsole/tests/fixtures/sitemap/index.xml new file mode 100644 index 0000000000000000000000000000000000000000..5f1934b3265a7b0830863f6c75b0e7b6da59e293 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/sitemap/index.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<sitemapindex + xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 + http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> + + <sitemap> + <loc>http://bar</loc> + <lastmod>2020-03-06</lastmod> + </sitemap> + +</sitemapindex> \ No newline at end of file diff --git a/module/VuFindConsole/tests/fixtures/sitemap/map.xml b/module/VuFindConsole/tests/fixtures/sitemap/map.xml new file mode 100644 index 0000000000000000000000000000000000000000..78ea1c540436cf789877bea37c66a8f7f04837a4 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/sitemap/map.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + <url> + <loc>http://xyzzy/</loc> + <lastmod>2020-03-17T16:03:30-04:00</lastmod> + <changefreq>weekly</changefreq> + <priority>0.5</priority> + </url> +</urlset> diff --git a/module/VuFindConsole/tests/fixtures/xml/a.xml b/module/VuFindConsole/tests/fixtures/xml/a.xml new file mode 100644 index 0000000000000000000000000000000000000000..afbdf72cbcf7f08686ac4c1a155483aa09b36cf6 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/xml/a.xml @@ -0,0 +1 @@ +<record id="a" /> diff --git a/module/VuFindConsole/tests/fixtures/xml/b.xml b/module/VuFindConsole/tests/fixtures/xml/b.xml new file mode 100644 index 0000000000000000000000000000000000000000..366a7839d3da7a43c7ed46b3fd6eb52c87a25f14 --- /dev/null +++ b/module/VuFindConsole/tests/fixtures/xml/b.xml @@ -0,0 +1 @@ +<record id="b" /> diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Compile/ThemeCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Compile/ThemeCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..76d2a55efb221f842756e0c6fabc85485bf85adb --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Compile/ThemeCommandTest.php @@ -0,0 +1,162 @@ +<?php +/** + * Compile/Theme command test. + * + * 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\Command\Compile; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Compile\ThemeCommand; +use VuFindTheme\ThemeCompiler; + +/** + * Compile/Theme command 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 ThemeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "source").' + ); + $command = new ThemeCommand($this->getMockCompiler()); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $compiler = $this->getMockCompiler(['compile']); + $compiler->expects($this->once())->method('compile') + ->with( + $this->equalTo('theme'), + $this->equalTo('theme_compiled'), + $this->equalTo(false) + )->will($this->returnValue(true)); + $command = new ThemeCommand($compiler); + $commandTester = new CommandTester($command); + $commandTester->execute(['source' => 'theme']); + $this->assertEquals( + "Success.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Simulate failure caused by existing theme and no '--force' option. + * + * @return void + */ + public function testFailureWithMissingForce() + { + $compiler = $this->getMockCompiler(['compile', 'getLastError']); + $compiler->expects($this->once())->method('compile') + ->with( + $this->equalTo('theme'), + $this->equalTo('compiled_theme'), + $this->equalTo(false) + )->will($this->returnValue(false)); + $compiler->expects($this->once())->method('getLastError') + ->will($this->returnValue('Error!')); + $command = new ThemeCommand($compiler); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'source' => 'theme', + 'target' => 'compiled_theme', + ] + ); + $this->assertEquals( + "Error!\n", + $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Simulate success with '--force' option. + * + * @return void + */ + public function testSuccessWithForceOption() + { + $compiler = $this->getMockCompiler(['compile']); + $compiler->expects($this->once())->method('compile') + ->with( + $this->equalTo('theme'), + $this->equalTo('compiled_theme'), + $this->equalTo(true) + )->will($this->returnValue(true)); + $command = new ThemeCommand($compiler); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'source' => 'theme', + 'target' => 'compiled_theme', + '--force' => true, + ] + ); + $this->assertEquals( + "Success.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock compiler object + * + * @param array $methods Methods to mock + * + * @return ThemeCompiler + */ + protected function getMockCompiler($methods = []) + { + return $this->getMockBuilder(ThemeCompiler::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/DynamicRouteCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/DynamicRouteCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3ebd1bb36d030ec27cfa65a3d6426c6938689c48 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/DynamicRouteCommandTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Generate/DynamicRoute command test. + * + * 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\Command\Generate; + +use Interop\Container\ContainerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Route\RouteGenerator; +use VuFindConsole\Command\Generate\DynamicRouteCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/DynamicRoute command 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 DynamicRouteCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments ' + . '(missing: "route, controller, action, target_module").' + ); + $command = new DynamicRouteCommand( + $this->getMockGeneratorTools(), + $this->getMockRouteGenerator() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $configFixturePath = __DIR__ . '/../../../../../fixtures/empty.config.php'; + $expectedConfig = include $configFixturePath; + $tools = $this->getMockGeneratorTools( + ['getModuleConfigPath', 'backUpFile', 'writeModuleConfig'] + ); + $tools->expects($this->once())->method('getModuleConfigPath') + ->with($this->equalTo('xyzzy')) + ->will($this->returnValue($configFixturePath)); + $tools->expects($this->once())->method('backUpFile') + ->with($this->equalTo($configFixturePath)); + $tools->expects($this->once())->method('writeModuleConfig') + ->with( + $this->equalTo($configFixturePath), + $this->equalTo($expectedConfig) + ); + $generator = $this->getMockRouteGenerator(['addDynamicRoute']); + $generator->expects($this->once())->method('addDynamicRoute') + ->with( + $this->equalTo($expectedConfig), + $this->equalTo('foo'), + $this->equalTo('bar'), + $this->equalTo('baz') + ); + $command = new DynamicRouteCommand($tools, $generator); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'route' => 'foo', + 'controller' => 'bar', + 'action' => 'baz', + 'target_module' => 'xyzzy', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock container object + * + * @param array $methods Methods to mock + * + * @return ContainerInterface + */ + protected function getMockRouteGenerator($methods = []) + { + return $this->getMockBuilder(RouteGenerator::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendClassCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendClassCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..30f9f2963d6ae43bcb8e6267e53c5e2e338507b0 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendClassCommandTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Generate/ExtendClass command test. + * + * 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\Command\Generate; + +use Interop\Container\ContainerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\ExtendClassCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/ExtendClass command 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 ExtendClassCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "class_name, target_module").' + ); + $command = new ExtendClassCommand( + $this->getMockGeneratorTools(), + $this->getMockContainer() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['extendClass', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('extendClass') + ->with( + $this->equalTo($container), + $this->equalTo('Foo'), + $this->equalTo('Bar'), + $this->equalTo(null) + ); + $command = new ExtendClassCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'class_name' => 'Foo', + 'target_module' => 'Bar' + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test the extendfactory option. + * + * @return void + */ + public function testSuccessWithFactoryOption() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['extendClass', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('extendClass') + ->with( + $this->equalTo($container), + $this->equalTo('Foo'), + $this->equalTo('Bar'), + $this->equalTo(true) + ); + $command = new ExtendClassCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'class_name' => 'Foo', + 'target_module' => 'Bar', + '--extendfactory' => true, + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test exception handling. + * + * @return void + */ + public function testError() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['extendClass', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('extendClass') + ->will($this->throwException(new \Exception('Foo!'))); + $command = new ExtendClassCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'class_name' => 'Foo', + 'target_module' => 'Bar' + ] + ); + $this->assertEquals("Foo!\n", $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock container object + * + * @param array $methods Methods to mock + * + * @return ContainerInterface + */ + protected function getMockContainer($methods = []) + { + return $this->getMockBuilder(ContainerInterface::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendServiceCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendServiceCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..09d553b7272a2b296b941b8336bc58633610401b --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ExtendServiceCommandTest.php @@ -0,0 +1,131 @@ +<?php +/** + * Generate/ExtendService command test. + * + * 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\Command\Generate; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\ExtendServiceCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/ExtendService command 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 ExtendServiceCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "config_path, target_module").' + ); + $command = new ExtendServiceCommand( + $this->getMockGeneratorTools() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $tools = $this->getMockGeneratorTools( + ['extendService', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('extendService') + ->with( + $this->equalTo('Foo'), + $this->equalTo('Bar') + ); + $command = new ExtendServiceCommand($tools); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'config_path' => 'Foo', + 'target_module' => 'Bar' + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test exception handling. + * + * @return void + */ + public function testError() + { + $tools = $this->getMockGeneratorTools( + ['extendService', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('extendService') + ->will($this->throwException(new \Exception('Foo!'))); + $command = new ExtendServiceCommand($tools); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'config_path' => 'Foo', + 'target_module' => 'Bar' + ] + ); + $this->assertEquals("Foo!\n", $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/NonTabRecordActionCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/NonTabRecordActionCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8ff33dd71faba76b8e8447f9e477893c3741eb26 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/NonTabRecordActionCommandTest.php @@ -0,0 +1,153 @@ +<?php +/** + * Generate/NonTabRecordAction command test. + * + * 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\Command\Generate; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\NonTabRecordActionCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/NonTabRecordAction command 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 NonTabRecordActionCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments ' + . '(missing: "action, target_module").' + ); + $command = new NonTabRecordActionCommand( + $this->getMockGeneratorTools(), + [] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $configFixturePath = __DIR__ . '/../../../../../fixtures/empty.config.php'; + $expectedConfig = [ + 'router' => [ + 'routes' => [ + 'example-foo' => [ + 'type' => \Laminas\Router\Http\Segment::class, + 'options' => [ + 'route' => '/Example/[:id]/Foo', + 'constraints' => [ + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + ], + 'defaults' => [ + 'controller' => 'Example', + 'action' => 'Foo', + ] + ] + ] + ] + ] + ]; + $tools = $this->getMockGeneratorTools( + ['getModuleConfigPath', 'backUpFile', 'writeModuleConfig'] + ); + $tools->expects($this->once())->method('getModuleConfigPath') + ->with($this->equalTo('xyzzy')) + ->will($this->returnValue($configFixturePath)); + $tools->expects($this->once())->method('backUpFile') + ->with($this->equalTo($configFixturePath)); + $tools->expects($this->once())->method('writeModuleConfig') + ->with( + $this->equalTo($configFixturePath), + $this->equalTo($expectedConfig) + ); + $config = [ + 'router' => [ + 'routes' => [ + 'example' => [ + 'type' => \Laminas\Router\Http\Segment::class, + 'options' => [ + 'route' => '/Example/[:id[/[:tab]]]', + 'constraints' => [ + 'controller' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + ], + 'defaults' => [ + 'controller' => 'Example', + 'action' => 'Home', + ] + ] + ] + ] + ] + ]; + $command = new NonTabRecordActionCommand($tools, $config); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'action' => 'Foo', + 'target_module' => 'xyzzy', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/PluginCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/PluginCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..27f54b4571eba0aa60e09347ab8879c483b6c743 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/PluginCommandTest.php @@ -0,0 +1,169 @@ +<?php +/** + * Generate/Plugin command test. + * + * 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\Command\Generate; + +use Interop\Container\ContainerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\PluginCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/Plugin command 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 PluginCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "class_name").' + ); + $command = new PluginCommand( + $this->getMockGeneratorTools(), + $this->getMockContainer() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['setOutputInterface', 'createPlugin'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('createPlugin') + ->with( + $this->equalTo($container), + $this->equalTo('Foo'), + $this->equalTo(null) + ); + $command = new PluginCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute(['class_name' => 'Foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test the factory parameter. + * + * @return void + */ + public function testSuccessWithFactoryParameter() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['setOutputInterface', 'createPlugin'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('createPlugin') + ->with( + $this->equalTo($container), + $this->equalTo('Foo'), + $this->equalTo('Factory') + ); + $command = new PluginCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['class_name' => 'Foo', 'factory' => 'Factory'] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test exception handling. + * + * @return void + */ + public function testError() + { + $container = $this->getMockContainer(); + $tools = $this->getMockGeneratorTools( + ['createPlugin', 'setOutputInterface'] + ); + $tools->expects($this->once())->method('setOutputInterface'); + $tools->expects($this->once())->method('createPlugin') + ->will($this->throwException(new \Exception('Foo!'))); + $command = new PluginCommand($tools, $container); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['class_name' => 'Foo', 'factory' => 'Factory'] + ); + $this->assertEquals("Foo!\n", $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock container object + * + * @param array $methods Methods to mock + * + * @return ContainerInterface + */ + protected function getMockContainer($methods = []) + { + return $this->getMockBuilder(ContainerInterface::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/RecordRouteCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/RecordRouteCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e58e1fb7cc044c7c8c172596570a98645dd34ab6 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/RecordRouteCommandTest.php @@ -0,0 +1,139 @@ +<?php +/** + * Generate/RecordRoute command test. + * + * 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\Command\Generate; + +use Interop\Container\ContainerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Route\RouteGenerator; +use VuFindConsole\Command\Generate\RecordRouteCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/RecordRoute command 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 RecordRouteCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments ' + . '(missing: "base, controller, target_module").' + ); + $command = new RecordRouteCommand( + $this->getMockGeneratorTools(), + $this->getMockRouteGenerator() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $configFixturePath = __DIR__ . '/../../../../../fixtures/empty.config.php'; + $expectedConfig = include $configFixturePath; + $tools = $this->getMockGeneratorTools( + ['getModuleConfigPath', 'backUpFile', 'writeModuleConfig'] + ); + $tools->expects($this->once())->method('getModuleConfigPath') + ->with($this->equalTo('xyzzy')) + ->will($this->returnValue($configFixturePath)); + $tools->expects($this->once())->method('backUpFile') + ->with($this->equalTo($configFixturePath)); + $tools->expects($this->once())->method('writeModuleConfig') + ->with( + $this->equalTo($configFixturePath), + $this->equalTo($expectedConfig) + ); + $generator = $this->getMockRouteGenerator(['addRecordRoute']); + $generator->expects($this->once())->method('addRecordRoute') + ->with( + $this->equalTo($expectedConfig), + $this->equalTo('foo'), + $this->equalTo('bar') + ); + $command = new RecordRouteCommand($tools, $generator); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'base' => 'foo', + 'controller' => 'bar', + 'target_module' => 'xyzzy', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock container object + * + * @param array $methods Methods to mock + * + * @return ContainerInterface + */ + protected function getMockRouteGenerator($methods = []) + { + return $this->getMockBuilder(RouteGenerator::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/StaticRouteCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/StaticRouteCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e8fa0dfd0de07a7ddd83cd85e356af2081cf1cd4 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/StaticRouteCommandTest.php @@ -0,0 +1,137 @@ +<?php +/** + * Generate/StaticRoute command test. + * + * 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\Command\Generate; + +use Interop\Container\ContainerInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Route\RouteGenerator; +use VuFindConsole\Command\Generate\StaticRouteCommand; +use VuFindConsole\Generator\GeneratorTools; + +/** + * Generate/StaticRoute command 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 StaticRouteCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments ' + . '(missing: "route_definition, target_module").' + ); + $command = new StaticRouteCommand( + $this->getMockGeneratorTools(), + $this->getMockRouteGenerator() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $configFixturePath = __DIR__ . '/../../../../../fixtures/empty.config.php'; + $expectedConfig = include $configFixturePath; + $tools = $this->getMockGeneratorTools( + ['getModuleConfigPath', 'backUpFile', 'writeModuleConfig'] + ); + $tools->expects($this->once())->method('getModuleConfigPath') + ->with($this->equalTo('xyzzy')) + ->will($this->returnValue($configFixturePath)); + $tools->expects($this->once())->method('backUpFile') + ->with($this->equalTo($configFixturePath)); + $tools->expects($this->once())->method('writeModuleConfig') + ->with( + $this->equalTo($configFixturePath), + $this->equalTo($expectedConfig) + ); + $generator = $this->getMockRouteGenerator(['addStaticRoute']); + $generator->expects($this->once())->method('addStaticRoute') + ->with( + $this->equalTo($expectedConfig), + $this->equalTo('foo') + ); + $command = new StaticRouteCommand($tools, $generator); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'route_definition' => 'foo', + 'target_module' => 'xyzzy', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock generator tools object + * + * @param array $methods Methods to mock + * + * @return GeneratorTools + */ + protected function getMockGeneratorTools($methods = []) + { + return $this->getMockBuilder(GeneratorTools::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock container object + * + * @param array $methods Methods to mock + * + * @return ContainerInterface + */ + protected function getMockRouteGenerator($methods = []) + { + return $this->getMockBuilder(RouteGenerator::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ac582d6848c3b148e5a099e42fae8ad6ac7c4e07 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeCommandTest.php @@ -0,0 +1,110 @@ +<?php +/** + * Generate/Theme command test. + * + * 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\Command\Generate; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\ThemeCommand; +use VuFindTheme\ThemeGenerator; + +/** + * Generate/Theme command 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 ThemeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $config = new \Laminas\Config\Config([]); + $generator = $this->getMockGenerator(); + $generator->expects($this->once()) + ->method('generate') + ->with($this->equalTo('custom')) + ->will($this->returnValue(true)); + $generator->expects($this->once()) + ->method('configure') + ->with($this->equalTo($config), $this->equalTo('custom')) + ->will($this->returnValue(true)); + $command = new ThemeCommand($generator, $config); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals( + "\tNo theme name provided, using \"custom\"\n\tFinished.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test a failure scenario. + * + * @return void + */ + public function testFailure() + { + $config = new \Laminas\Config\Config([]); + $generator = $this->getMockGenerator(); + $generator->expects($this->once()) + ->method('generate') + ->with($this->equalTo('foo')) + ->will($this->returnValue(true)); + $generator->expects($this->once()) + ->method('configure') + ->with($this->equalTo($config), $this->equalTo('foo')) + ->will($this->returnValue(false)); + $generator->expects($this->once()) + ->method('getLastError') + ->will($this->returnValue('fake error')); + $command = new ThemeCommand($generator, $config); + $commandTester = new CommandTester($command); + $commandTester->execute(['name' => 'foo']); + $this->assertEquals("fake error\n", $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Create a mock generator object. + * + * @return ThemeGenerator + */ + protected function getMockGenerator() + { + return $this->getMockBuilder(ThemeGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeMixinCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeMixinCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7036c087ecc23caed79d7b1aca8e6d38df2e43dd --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Generate/ThemeMixinCommandTest.php @@ -0,0 +1,102 @@ +<?php +/** + * Generate/ThemeMixin command test. + * + * 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\Command\Generate; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Generate\ThemeMixinCommand; +use VuFindTheme\MixinGenerator; + +/** + * Generate/ThemeMixin command 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 ThemeMixinCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $generator = $this->getMockGenerator(); + $generator->expects($this->once()) + ->method('generate') + ->with($this->equalTo('custom')) + ->will($this->returnValue(true)); + $command = new ThemeMixinCommand($generator); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals( + "\tNo theme mixin name provided, using \"custom\"\n" + . "\tFinished. Add to your theme.config.php 'mixins' setting " + . "to activate.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test a failure scenario. + * + * @return void + */ + public function testFailure() + { + $generator = $this->getMockGenerator(); + $generator->expects($this->once()) + ->method('generate') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + $generator->expects($this->once()) + ->method('getLastError') + ->will($this->returnValue('fake error')); + $command = new ThemeMixinCommand($generator); + $commandTester = new CommandTester($command); + $commandTester->execute(['name' => 'foo']); + $this->assertEquals("fake error\n", $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Create a mock generator object. + * + * @return ThemeGenerator + */ + protected function getMockGenerator() + { + return $this->getMockBuilder(MixinGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/HarvestOaiCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/HarvestOaiCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0592b78a755180c37872677da0947a4dee260d78 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/HarvestOaiCommandTest.php @@ -0,0 +1,61 @@ +<?php +/** + * HarvestOai command test. + * + * 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\Command\Harvest; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Harvest\HarvestOaiCommand; + +/** + * HarvestOai command 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 HarvestOaiCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that the --ini setting is overridden automatically. + * + * @return void + */ + public function testIniOverride() + { + $command = new HarvestOaiCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expectedIni = \VuFind\Config\Locator::getConfigPath('oai.ini', 'harvest'); + $this->assertEquals( + "Please add OAI-PMH settings to $expectedIni.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/MergeMarcCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/MergeMarcCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..284892bedc4fb4b30e35f11df344c2867bc9667a --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Harvest/MergeMarcCommandTest.php @@ -0,0 +1,101 @@ +<?php +/** + * MergeMarc command test. + * + * 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\Command\Harvest; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Harvest\MergeMarcCommand; + +/** + * MergeMarc command 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 MergeMarcCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "directory").' + ); + $command = new MergeMarcCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test that merging a directory yields valid results. + * + * @return void + */ + public function testMergingDirectory() + { + $command = new MergeMarcCommand(); + $commandTester = new CommandTester($command); + $directory = __DIR__ . '/../../../../../fixtures/xml'; + $commandTester->execute(compact('directory')); + $expected = <<<EXPECTED +<collection> +<!-- $directory/a.xml --> +<record id="a" /> +<!-- $directory/b.xml --> +<record id="b" /> +</collection> + +EXPECTED; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test that merging a non-existent directory yields an error message. + * + * @return void + */ + public function testMissingDirectory() + { + $command = new MergeMarcCommand(); + $commandTester = new CommandTester($command); + $directory = __DIR__ . '/../../../../../fixtures/does-not-exist'; + $commandTester->execute(compact('directory')); + $expected = "Cannot open directory: $directory\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(1, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/ImportXslCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/ImportXslCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9a87d57d39d9af64c54da9ffcfe9da787e6d3d2e --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/ImportXslCommandTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Import/ImportXsl command test. + * + * 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\Command\Import; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\XSLT\Importer; +use VuFindConsole\Command\Import\ImportXslCommand; + +/** + * Import/ImportXsl command 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 ImportXslCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "XML_file, properties_file").' + ); + $command = new ImportXslCommand( + $this->getMockImporter() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $importer = $this->getMockImporter(); + $importer->expects($this->once())->method('save') + ->with( + $this->equalTo('foo.xml'), + $this->equalTo('bar.properties'), + $this->equalTo('Solr'), + $this->equalTo(false) + ); + $command = new ImportXslCommand($importer); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'XML_file' => 'foo.xml', + 'properties_file' => 'bar.properties' + ] + ); + $this->assertEquals( + "Successfully imported foo.xml...\n", $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test a failure scenario + * + * @return void + */ + public function testFailure() + { + $e = new \Exception('foo', 0, new \Exception('bar')); + $importer = $this->getMockImporter(); + $importer->expects($this->once())->method('save') + ->with( + $this->equalTo('foo.xml'), + $this->equalTo('bar.properties'), + $this->equalTo('SolrTest'), + $this->equalTo(true) + )->will($this->throwException($e)); + $command = new ImportXslCommand($importer); + $commandTester = new CommandTester($command); + $commandTester->execute( + [ + 'XML_file' => 'foo.xml', + 'properties_file' => 'bar.properties', + '--index' => 'SolrTest', + '--test-only' => true, + ] + ); + $this->assertEquals( + "Fatal error: foo\nPrevious exception: bar\n", + $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock importer object + * + * @param array $methods Methods to mock + * + * @return Importer + */ + protected function getMockImporter($methods = []) + { + return $this->getMockBuilder(Importer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/WebCrawlCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/WebCrawlCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..165cf6a7010908304779f7f9adc5b3277eae70b4 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Import/WebCrawlCommandTest.php @@ -0,0 +1,149 @@ +<?php +/** + * Import/WebCrawl command test. + * + * 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\Command\Import; + +use Laminas\Config\Config; +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Solr\Writer; +use VuFind\XSLT\Importer; +use VuFindConsole\Command\Import\WebCrawlCommand; + +/** + * Import/WebCrawl command 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 WebCrawlCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $fixture1 = __DIR__ . '/../../../../../fixtures/sitemap/index.xml'; + $fixture2 = __DIR__ . '/../../../../../fixtures/sitemap/map.xml'; + $importer = $this->getMockImporter(); + $importer->expects($this->once())->method('save') + ->with( + $this->equalTo($fixture2), + $this->equalTo('sitemap.properties'), + $this->equalTo('SolrWeb'), + $this->equalTo(false) + ); + $solr = $this->getMockSolrWriter(); + $solr->expects($this->once())->method('deleteByQuery') + ->with($this->equalTo('SolrWeb')); + $solr->expects($this->once())->method('commit') + ->with($this->equalTo('SolrWeb')); + $solr->expects($this->once())->method('optimize') + ->with($this->equalTo('SolrWeb')); + $config = new Config( + [ + 'Sitemaps' => ['url' => ['http://foo']] + ] + ); + $command = $this->getMockCommand($importer, $solr, $config); + $command->expects($this->at(0))->method('downloadFile') + ->with($this->equalTo('http://foo')) + ->will($this->returnValue($fixture1)); + $command->expects($this->at(1))->method('downloadFile') + ->with($this->equalTo('http://bar')) + ->will($this->returnValue($fixture2)); + $command->expects($this->at(2))->method('removeTempFile') + ->with($this->equalTo($fixture2)); + $command->expects($this->at(3))->method('removeTempFile') + ->with($this->equalTo($fixture1)); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals( + '', $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param Importer $importer Importer object + * @param Writer $solr Solr writer object + * @param Config $config Configuration + * @param array $methods Methods to mock + * + * @return WebCrawlCommand + */ + protected function getMockCommand(Importer $importer = null, + Writer $solr = null, Config $config = null, + array $methods = ['downloadFile', 'removeTempFile'] + ) { + return $this->getMockBuilder(WebCrawlCommand::class) + ->setConstructorArgs( + [ + $importer ?? $this->getMockImporter(), + $solr ?? $this->getMockSolrWriter(), + $config ?? new Config([]), + ] + )->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock importer object + * + * @param array $methods Methods to mock + * + * @return Importer + */ + protected function getMockImporter($methods = []) + { + return $this->getMockBuilder(Importer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock solr writer object + * + * @param array $methods Methods to mock + * + * @return Writer + */ + protected function getMockSolrWriter($methods = []) + { + return $this->getMockBuilder(Writer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Install/InstallCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Install/InstallCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..82828bbcec1bbc30fe042c71e1592ff4b5579b96 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Install/InstallCommandTest.php @@ -0,0 +1,217 @@ +<?php +/** + * Install command test. + * + * 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\Command\Import; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Install\InstallCommand; + +/** + * Install command 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 InstallCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test the interactive installation process. + * + * @return void + */ + public function testInteractiveInstallation() + { + $expectedBaseDir = realpath(__DIR__ . '/../../../../../../../../'); + $localFixtures = $expectedBaseDir . '/module/VuFindConsole/tests/fixtures'; + $command = $this->getMockCommand( + ['buildDirs', 'getApacheLocation', 'getInput', 'writeFileToDisk'] + ); + $command->expects($this->at(0))->method('getInput') + ->with( + $this->isInstanceOf(InputInterface::class), + $this->isInstanceOf(OutputInterface::class), + $this->equalTo( + 'Where would you like to store your local settings? ' + . "[$expectedBaseDir/local] " + ) + )->will($this->returnValue($localFixtures)); + $expectedDirs = [ + $localFixtures, + $localFixtures . '/cache', + $localFixtures . '/config', + $localFixtures . '/harvest', + $localFixtures . '/import', + ]; + $command->expects($this->at(1))->method('buildDirs') + ->with($this->equalTo($expectedDirs)) + ->will($this->returnValue(true)); + $command->expects($this->at(2))->method('getInput') + ->with( + $this->isInstanceOf(InputInterface::class), + $this->isInstanceOf(OutputInterface::class), + $this->equalTo( + "\nWhat module name would you like to use? [blank for none] " + ) + )->will($this->returnValue('')); + $command->expects($this->at(3))->method('getInput') + ->with( + $this->isInstanceOf(InputInterface::class), + $this->isInstanceOf(OutputInterface::class), + $this->equalTo( + 'What base path should be used in VuFind\'s URL? [/vufind] ' + ) + )->will($this->returnValue('/bar')); + $command->expects($this->at(4))->method('buildDirs') + ->with($this->equalTo($expectedDirs)) + ->will($this->returnValue(true)); + $expectedEnvBat = "@set VUFIND_HOME=$expectedBaseDir\n" + . "@set VUFIND_LOCAL_DIR=$localFixtures\n"; + $command->expects($this->at(5))->method('writeFileToDisk') + ->with( + $this->equalTo("$expectedBaseDir/env.bat"), + $this->equalTo($expectedEnvBat) + )->will($this->returnValue(true)); + $command->expects($this->at(6))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/import/import.properties")) + ->will($this->returnValue(true)); + $command->expects($this->at(7))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/import/import_auth.properties")) + ->will($this->returnValue(true)); + $command->expects($this->at(8))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/httpd-vufind.conf")) + ->will($this->returnValue(true)); + $command->expects($this->at(9))->method('getApacheLocation') + ->with($this->isInstanceOf(OutputInterface::class)); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expectedOutput = <<<TEXT +VuFind has been found in $expectedBaseDir. + +VuFind supports use of a custom module for storing local code changes. +If you do not plan to customize the code, you can skip this step. +If you decide to use a custom module, the name you choose will be used for +the module's directory name and its PHP namespace. +Apache configuration written to $localFixtures/httpd-vufind.conf. + +You now need to load this configuration into Apache. +Once the configuration is linked, restart Apache. You should now be able +to access VuFind at http://localhost/bar + +For proper use of command line tools, you should also ensure that your + +VUFIND_HOME and VUFIND_LOCAL_DIR environment variables are set to +$expectedBaseDir and $localFixtures respectively. +TEXT; + $this->assertEquals( + $expectedOutput, trim($commandTester->getDisplay()) + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test the non-interactive installation process. + * + * @return void + */ + public function testNonInteractiveInstallation() + { + $expectedBaseDir = realpath(__DIR__ . '/../../../../../../../../'); + $localFixtures = $expectedBaseDir . '/module/VuFindConsole/tests/fixtures'; + $command = $this->getMockCommand( + ['buildDirs', 'getApacheLocation', 'getInput', 'writeFileToDisk'] + ); + $expectedDirs = [ + $localFixtures, + $localFixtures . '/cache', + $localFixtures . '/config', + $localFixtures . '/harvest', + $localFixtures . '/import', + ]; + $command->expects($this->at(0))->method('buildDirs') + ->with($this->equalTo($expectedDirs)) + ->will($this->returnValue(true)); + $expectedEnvBat = "@set VUFIND_HOME=$expectedBaseDir\n" + . "@set VUFIND_LOCAL_DIR=$localFixtures\n"; + $command->expects($this->at(1))->method('writeFileToDisk') + ->with( + $this->equalTo("$expectedBaseDir/env.bat"), + $this->equalTo($expectedEnvBat) + )->will($this->returnValue(true)); + $command->expects($this->at(2))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/import/import.properties")) + ->will($this->returnValue(true)); + $command->expects($this->at(3))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/import/import_auth.properties")) + ->will($this->returnValue(true)); + $command->expects($this->at(4))->method('writeFileToDisk') + ->with($this->equalTo("$localFixtures/httpd-vufind.conf")) + ->will($this->returnValue(true)); + $command->expects($this->at(5))->method('getApacheLocation') + ->with($this->isInstanceOf(OutputInterface::class)); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['--non-interactive' => true, '--overridedir' => $localFixtures] + ); + $expectedOutput = <<<EXPECTED +VuFind has been found in $expectedBaseDir. +Apache configuration written to $localFixtures/httpd-vufind.conf. + +You now need to load this configuration into Apache. +Once the configuration is linked, restart Apache. You should now be able +to access VuFind at http://localhost/vufind + +For proper use of command line tools, you should also ensure that your + +VUFIND_HOME and VUFIND_LOCAL_DIR environment variables are set to +$expectedBaseDir and $localFixtures respectively. +EXPECTED; + $this->assertEquals( + $expectedOutput, trim($commandTester->getDisplay()) + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param array $methods Methods to mock + * + * @return InstallCommand + */ + protected function getMockCommand( + array $methods = ['buildDirs', 'getInput', 'writeFileToDisk'] + ) { + return $this->getMockBuilder(InstallCommand::class) + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/AddUsingTemplateCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/AddUsingTemplateCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5a6edb549d450cb7959d03f8005c5de9a671cf6c --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/AddUsingTemplateCommandTest.php @@ -0,0 +1,158 @@ +<?php +/** + * Language/AddUsingTemplate command test. + * + * 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\Command\Language; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; +use VuFindConsole\Command\Language\AddUsingTemplateCommand; + +/** + * Language/AddUsingTemplate command 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 AddUsingTemplateCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Language fixture directory + * + * @var string + */ + protected $languageFixtureDir = __DIR__ . '/../../../../../fixtures/language'; + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "target, template").' + ); + $command = new AddUsingTemplateCommand( + $this->getMockNormalizer(), + $this->getMockReader() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $expectedPath = realpath($this->languageFixtureDir) . '/foo/en.ini'; + $normalizer = $this->getMockNormalizer(); + $normalizer->expects($this->once())->method('normalizeFile') + ->with($this->equalTo($expectedPath)); + $reader = $this->getMockReader(); + $reader->expects($this->once())->method('getTextDomain') + ->with($this->equalTo($expectedPath), $this->equalTo(false)) + ->will($this->returnValue(['bar' => 'baz'])); + $command = $this->getMockCommand($normalizer, $reader); + $command->expects($this->once())->method('addLineToFile') + ->with( + $this->equalTo($expectedPath), + $this->equalTo('xyzzy'), + $this->equalTo('baz-baz') + ); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['template' => '||foo::bar||-||foo::bar||', 'target' => 'foo::xyzzy'] + ); + $this->assertEquals("Processing en.ini...\n", $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param ExtendedIniNormalizer $normalizer Normalizer for .ini files + * @param ExtendedIniReader $reader Reader for .ini files + * @param string $languageDir Base language file directory + * @param array $methods Methods to mock + * + * @return AddUsingTemplateCommand + */ + protected function getMockCommand(ExtendedIniNormalizer $normalizer = null, + ExtendedIniReader $reader = null, $languageDir = null, + array $methods = ['addLineToFile'] + ) { + return $this->getMockBuilder(AddUsingTemplateCommand::class) + ->setConstructorArgs( + [ + $normalizer ?? $this->getMockNormalizer(), + $reader ?? $this->getMockReader(), + $languageDir ?? $this->languageFixtureDir, + ] + )->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock normalizer object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniNormalizer + */ + protected function getMockNormalizer($methods = []) + { + return $this->getMockBuilder(ExtendedIniNormalizer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock reader object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniReader + */ + protected function getMockReader($methods = []) + { + return $this->getMockBuilder(ExtendedIniReader::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/CopyStringCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/CopyStringCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7abec14fb09e186d8216428f0130da0c5c80a3d6 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/CopyStringCommandTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Language/CopyString command test. + * + * 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\Command\Language; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; +use VuFindConsole\Command\Language\CopyStringCommand; + +/** + * Language/CopyString command 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 CopyStringCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Language fixture directory + * + * @var string + */ + protected $languageFixtureDir = __DIR__ . '/../../../../../fixtures/language'; + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "source, target").' + ); + $command = new CopyStringCommand( + $this->getMockNormalizer(), + $this->getMockReader() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $expectedPath = realpath($this->languageFixtureDir) . '/foo/en.ini'; + $normalizer = $this->getMockNormalizer(); + $normalizer->expects($this->once())->method('normalizeFile') + ->with($this->equalTo($expectedPath)); + $reader = $this->getMockReader(); + $reader->expects($this->once())->method('getTextDomain') + ->with($this->equalTo($expectedPath), $this->equalTo(false)) + ->will($this->returnValue(['bar' => 'baz'])); + $command = $this->getMockCommand($normalizer, $reader); + $command->expects($this->once())->method('addLineToFile') + ->with( + $this->equalTo($expectedPath), + $this->equalTo('xyzzy'), + $this->equalTo('baz') + ); + $commandTester = new CommandTester($command); + $commandTester->execute(['source' => 'foo::bar', 'target' => 'foo::xyzzy']); + $this->assertEquals( + "Processing en.ini...\nProcessing en.ini...\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test failure due to missing text domain. + * + * @return void + */ + public function testFailureWithMissingTextDomain() + { + $command = $this->getMockCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['source' => 'doesnotexist::bar', 'target' => 'foo::xyzzy'] + ); + $this->assertEquals( + "Could not open directory {$this->languageFixtureDir}/doesnotexist\n", + $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param ExtendedIniNormalizer $normalizer Normalizer for .ini files + * @param ExtendedIniReader $reader Reader for .ini files + * @param string $languageDir Base language file directory + * @param array $methods Methods to mock + * + * @return CopyStringCommand + */ + protected function getMockCommand(ExtendedIniNormalizer $normalizer = null, + ExtendedIniReader $reader = null, $languageDir = null, + array $methods = ['addLineToFile'] + ) { + return $this->getMockBuilder(CopyStringCommand::class) + ->setConstructorArgs( + [ + $normalizer ?? $this->getMockNormalizer(), + $reader ?? $this->getMockReader(), + $languageDir ?? $this->languageFixtureDir, + ] + )->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock normalizer object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniNormalizer + */ + protected function getMockNormalizer($methods = []) + { + return $this->getMockBuilder(ExtendedIniNormalizer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock reader object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniReader + */ + protected function getMockReader($methods = []) + { + return $this->getMockBuilder(ExtendedIniReader::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/DeleteCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/DeleteCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..24594ad32aa9dc7688b2c962e776656670e05f49 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/DeleteCommandTest.php @@ -0,0 +1,169 @@ +<?php +/** + * Language/Delete command test. + * + * 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\Command\Language; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; +use VuFindConsole\Command\Language\DeleteCommand; + +/** + * Language/Delete command 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 DeleteCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Language fixture directory + * + * @var string + */ + protected $languageFixtureDir = __DIR__ . '/../../../../../fixtures/language'; + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "target").' + ); + $command = new DeleteCommand( + $this->getMockNormalizer(), + $this->getMockReader() + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test the simplest possible success case. + * + * @return void + */ + public function testSuccessWithMinimalParameters() + { + $expectedPath = realpath($this->languageFixtureDir) . '/foo/en.ini'; + $normalizer = $this->getMockNormalizer(); + $normalizer->expects($this->once())->method('normalizeFile') + ->with($this->equalTo($expectedPath)); + $command = $this->getMockCommand($normalizer); + $command->expects($this->once())->method('writeFileToDisk') + ->with( + $this->equalTo($expectedPath), + $this->equalTo('') + ); + $commandTester = new CommandTester($command); + $commandTester->execute(['target' => 'foo::bar']); + $this->assertEquals("Processing en.ini...\n", $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test an attempt to delete a string that does not exist. + * + * @return void + */ + public function testDeletingNonExistentString() + { + $expectedPath = realpath($this->languageFixtureDir) . '/foo/en.ini'; + $command = $this->getMockCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute(['target' => 'foo::barzap']); + $this->assertEquals( + "Processing en.ini...\nSource key not found.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param ExtendedIniNormalizer $normalizer Normalizer for .ini files + * @param ExtendedIniReader $reader Reader for .ini files + * @param string $languageDir Base language file directory + * @param array $methods Methods to mock + * + * @return AddUsingTemplateCommand + */ + protected function getMockCommand(ExtendedIniNormalizer $normalizer = null, + ExtendedIniReader $reader = null, $languageDir = null, + array $methods = ['writeFileToDisk'] + ) { + return $this->getMockBuilder(DeleteCommand::class) + ->setConstructorArgs( + [ + $normalizer ?? $this->getMockNormalizer(), + $reader ?? $this->getMockReader(), + $languageDir ?? $this->languageFixtureDir, + ] + )->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock normalizer object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniNormalizer + */ + protected function getMockNormalizer($methods = []) + { + return $this->getMockBuilder(ExtendedIniNormalizer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock reader object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniReader + */ + protected function getMockReader($methods = []) + { + return $this->getMockBuilder(ExtendedIniReader::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/NormalizeCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/NormalizeCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c7350f0082b73b2aadffc29936613b0524aebfe4 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Language/NormalizeCommandTest.php @@ -0,0 +1,178 @@ +<?php +/** + * Language/Normalize command test. + * + * 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\Command\Language; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\I18n\ExtendedIniNormalizer; +use VuFind\I18n\Translator\Loader\ExtendedIniReader; +use VuFindConsole\Command\Language\NormalizeCommand; + +/** + * Language/Normalize command 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 NormalizeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Language fixture directory + * + * @var string + */ + protected $languageFixtureDir = __DIR__ . '/../../../../../fixtures/language'; + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "target").' + ); + $command = new NormalizeCommand($this->getMockNormalizer()); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test normalizing a directory. + * + * @return void + */ + public function testNormalizingDirectory() + { + $target = realpath($this->languageFixtureDir) . '/foo/'; + $normalizer = $this->getMockNormalizer(); + $normalizer->expects($this->once())->method('normalizeDirectory') + ->with($this->equalTo($target)); + $command = new NormalizeCommand($normalizer); + $commandTester = new CommandTester($command); + $commandTester->execute(compact('target')); + $this->assertEquals("", $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test normalizing a file. + * + * @return void + */ + public function testNormalizingFile() + { + $target = realpath($this->languageFixtureDir) . '/foo/en.ini'; + $normalizer = $this->getMockNormalizer(); + $normalizer->expects($this->once())->method('normalizeFile') + ->with($this->equalTo($target)); + $command = new NormalizeCommand($normalizer); + $commandTester = new CommandTester($command); + $commandTester->execute(compact('target')); + $this->assertEquals("", $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test an attempt to normalize a file that does not exist. + * + * @return void + */ + public function testNormalizingNonExistentFile() + { + $target = realpath($this->languageFixtureDir) . '/foo/noexist.ini'; + $command = new NormalizeCommand($this->getMockNormalizer()); + $commandTester = new CommandTester($command); + $commandTester->execute(compact('target')); + $this->assertEquals( + "{$target} does not exist.\n", $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Get a mock command object + * + * @param ExtendedIniNormalizer $normalizer Normalizer for .ini files + * @param ExtendedIniReader $reader Reader for .ini files + * @param string $languageDir Base language file directory + * @param array $methods Methods to mock + * + * @return AddUsingTemplateCommand + */ + protected function getMockCommand(ExtendedIniNormalizer $normalizer = null, + ExtendedIniReader $reader = null, $languageDir = null, + array $methods = ['writeFileToDisk'] + ) { + return $this->getMockBuilder(DeleteCommand::class) + ->setConstructorArgs( + [ + $normalizer ?? $this->getMockNormalizer(), + $reader ?? $this->getMockReader(), + $languageDir ?? $this->languageFixtureDir, + ] + )->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock normalizer object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniNormalizer + */ + protected function getMockNormalizer($methods = []) + { + return $this->getMockBuilder(ExtendedIniNormalizer::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + /** + * Get a mock reader object + * + * @param array $methods Methods to mock + * + * @return ExtendedIniReader + */ + protected function getMockReader($methods = []) + { + return $this->getMockBuilder(ExtendedIniReader::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/ScheduledSearch/NotifyCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/ScheduledSearch/NotifyCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..befa6bb4b9999c4ab2a8c5e6b6247fa39f3ead87 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/ScheduledSearch/NotifyCommandTest.php @@ -0,0 +1,572 @@ +<?php +/** + * ScheduledSearch/Notify command test. + * + * 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\Command\ScheduledSearch; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\ScheduledSearch\NotifyCommand; + +/** + * ScheduledSearch/Notify command 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 NotifyCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test behavior when no notifications are waiting to be sent. + * + * @return void + */ + public function testNoNotifications() + { + $searchTable = $this->prepareMock(\VuFind\Db\Table\Search::class); + $searchTable->expects($this->once())->method('getScheduledSearches') + ->will($this->returnValue([])); + $command = $this->getCommand( + [ + 'searchTable' => $searchTable, + 'scheduleOptions' => [] + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 0 searches\nDone processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent but there is no + * matching frequency configuration. + * + * @return void + */ + public function testNotificationWithIllegalFrequency() + { + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable( + [ + 'search_object' => null, + ] + ), + 'scheduleOptions' => [1 => 'Daily'] + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . "ERROR: Search 1: unknown schedule: 7\nDone processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications have already been sent recently. + * + * @return void + */ + public function testNotificationWithRecentExecution() + { + $lastDate = date('Y-m-d h:i:s'); + $overrides = [ + 'last_notification_sent' => $lastDate, + 'search_object' => null, + ]; + $lastDate = str_replace(' ', 'T', $lastDate) . 'Z'; + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable($overrides), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . " Bypassing search 1: previous execution too recent (Weekly, $lastDate)\n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent but user does not + * exist. + * + * @return void + */ + public function testNotificationsWithMissingUser() + { + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable( + [ + 'search_object' => null, + ] + ), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . "WARNING: Search 1: user 2 does not exist \n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent but an illegal backend + * is involved. + * + * @return void + */ + public function testNotificationsWithUnsupportedBackend() + { + $resultsCallback = function ($results) { + $results->expects($this->any())->method('getBackendId') + ->will($this->returnValue('unsupported')); + $results->expects($this->any())->method('getSearchId') + ->will($this->returnValue(1)); + }; + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable( + [], null, null, $resultsCallback + ), + 'userTable' => $this->getMockUserTable(), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . "ERROR: Unsupported search backend unsupported for search 1\n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent but no search + * results exist. + * + * @return void + */ + public function testNotificationsWithNoSearchResults() + { + $optionsCallback = function ($options) { + $options->expects($this->any())->method('supportsScheduledSearch') + ->will($this->returnValue(true)); + }; + $resultsCallback = function ($results) { + $results->expects($this->any())->method('getSearchId') + ->will($this->returnValue(1)); + }; + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable( + [], $optionsCallback, null, $resultsCallback + ), + 'userTable' => $this->getMockUserTable(), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . " No results found for search 1\n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent but no search + * results exist. + * + * @return void + */ + public function testNotificationsWithNoNewSearchResults() + { + $optionsCallback = function ($options) { + $options->expects($this->any())->method('supportsScheduledSearch') + ->will($this->returnValue(true)); + }; + $resultsCallback = function ($results) { + $results->expects($this->any())->method('getSearchId') + ->will($this->returnValue(1)); + $results->expects($this->any())->method('getResults') + ->will($this->returnValue($this->getMockSearchResultsSet())); + }; + $command = $this->getCommand( + [ + 'searchTable' => $this->getMockSearchTable( + [], $optionsCallback, null, $resultsCallback + ), + 'userTable' => $this->getMockUserTable(), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . " No new results for search (1): 1970-01-01T00:00:00Z < 2000-01-01T00:00:00Z\n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test behavior when notifications are waiting to be sent and new search + * results exist. + * + * @return void + */ + public function testNotificationsWithNewSearchResults() + { + $optionsCallback = function ($options) { + $options->expects($this->any())->method('supportsScheduledSearch') + ->will($this->returnValue(true)); + }; + $paramsCallback = function ($params) { + $params->expects($this->any())->method('getCheckboxFacets') + ->will($this->returnValue([])); + }; + $now = str_replace(' ', 'T', date('Y-m-d h:i:s')) . 'Z'; + $record = new \VuFindTest\RecordDriver\TestHarness(); + $record->setRawData( + [ + 'FirstIndexed' => $now, + ] + ); + $resultsCallback = function ($results) use ($now, $record) { + $results->expects($this->any())->method('getSearchId') + ->will($this->returnValue(1)); + $results->expects($this->any())->method('getResults') + ->will($this->returnValue($this->getMockSearchResultsSet($record))); + }; + $message = 'sample message'; + $expectedViewParams = [ + 'records' => [$record], + 'info' => [ + 'baseUrl' => 'http://foo', + 'description' => null, + 'recordCount' => 1, + 'url' => 'http://foo', + 'unsubscribeUrl' => 'http://foo?id=1&key=', + 'checkboxFilters' => [], + 'filters' => null, + 'userInstitution' => 'My Institution', + ], + ]; + $renderer = $this->prepareMock(\Laminas\View\Renderer\PhpRenderer::class); + $renderer->expects($this->once())->method('render') + ->with( + $this->equalTo('Email/scheduled-alert.phtml'), + $this->equalTo($expectedViewParams) + )->will($this->returnValue($message)); + $mailer = $this->prepareMock(\VuFind\Mailer\Mailer::class); + $mailer->expects($this->once())->method('send') + ->with( + $this->equalTo('fake@myuniversity.edu'), + $this->equalTo('admin@myuniversity.edu'), + $this->equalTo('My Site: translated text'), + $this->equalTo($message) + ); + $translator = $this->prepareMock(\Laminas\I18n\Translator\Translator::class); + $translator->expects($this->once())->method('translate') + ->with($this->equalTo('Scheduled Alert Results')) + ->will($this->returnValue('translated text')); + $command = $this->getCommand( + [ + 'mailer' => $mailer, + 'renderer' => $renderer, + 'translator' => $translator, + 'searchTable' => $this->getMockSearchTable( + [], $optionsCallback, $paramsCallback, $resultsCallback + ), + 'userTable' => $this->getMockUserTable(), + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "Processing 1 searches\n" + . " New results for search (1): $now >= 2000-01-01T00:00:00Z\n" + . "Done processing searches\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Get mock search results. + * + * @param \VuFind\RecordDriver\AbstractBase $record Record to return + * + * @return array + */ + protected function getMockSearchResultsSet($record = null) + { + return [ + $record ?? $this->prepareMock(\VuFind\RecordDriver\SolrDefault::class) + ]; + } + + /** + * Create a list of fake notification objects. + * + * @param array $overrides Fields to override in the notification row. + * @param \Callable $optionsCallback Callback to set expectations on options object + * @param \Callable $paramsCallback Callback to set expectations on params object + * @param \Callable $resultsCallback Callback to set expectations on results object + * + * @return array + */ + protected function getMockNotifications($overrides = [], $optionsCallback = null, + $paramsCallback = null, $resultsCallback = null + ) { + $defaults = [ + 'id' => 1, + 'user_id' => 2, + 'session_id' => null, + 'folder_id' => null, + 'created' => '2000-01-01 00:00:00', + 'title' => null, + 'saved' => 1, + 'checksum' => null, + 'notification_frequency' => 7, + 'last_notification_sent' => '2000-01-01 00:00:00', + 'notification_base_url' => 'http://foo', + ]; + // Don't create the mock search (and thus set up assertions) unless + // we actually need to. We use array_key_exists() instead of isset() + // because the key may be explicitly set to a value of null. + if (!array_key_exists('search_object', $overrides)) { + $defaults['search_object'] = serialize( + $this->getMockSearch( + $optionsCallback, $paramsCallback, $resultsCallback + ) + ); + } + $adapter = $this->prepareMock(\Laminas\Db\Adapter\Adapter::class); + $row1 = $this->getMockBuilder(\VuFind\Db\Row\Search::class) + ->setConstructorArgs([$adapter]) + ->setMethods(['save']) + ->getMock(); + $row1->populate($overrides + $defaults, true); + return [$row1]; + } + + /** + * Get mock search results. + * + * @param \Callable $optionsCallback Callback to set expectations on options object + * @param \Callable $paramsCallback Callback to set expectations on params object + * @param \Callable $resultsCallback Callback to set expectations on results object + * + * @return \VuFind\Search\Solr\Results + */ + protected function getMockSearchResults($optionsCallback = null, + $paramsCallback = null, $resultsCallback = null + ) { + $options = $this->prepareMock(\VuFind\Search\Solr\Options::class); + if ($optionsCallback) { + $optionsCallback($options); + } + $urlQuery = $this->prepareMock(\VuFind\Search\UrlQueryHelper::class); + $params = $this->prepareMock(\VuFind\Search\Solr\Params::class); + if ($paramsCallback) { + $paramsCallback($params); + } + $results = $this->prepareMock(\VuFind\Search\Solr\Results::class); + $results->expects($this->any())->method('getOptions') + ->will($this->returnValue($options)); + $results->expects($this->any())->method('getUrlQuery') + ->will($this->returnValue($urlQuery)); + $results->expects($this->any())->method('getParams') + ->will($this->returnValue($params)); + if ($resultsCallback) { + $resultsCallback($results); + } + return $results; + } + + /** + * Get a minified search object + * + * @param \Callable $optionsCallback Callback to set expectations on options object + * @param \Callable $paramsCallback Callback to set expectations on params object + * @param \Callable $resultsCallback Callback to set expectations on results object + * + * @return \VuFind\Search\Minified + */ + protected function getMockSearch($optionsCallback = null, $paramsCallback = null, + $resultsCallback = null + ) { + $search = $this->prepareMock(\VuFind\Search\Minified::class); + $search->expects($this->any())->method('deminify') + ->with($this->equalTo($this->getMockResultsManager())) + ->will( + $this->returnValue( + $this->getMockSearchResults( + $optionsCallback, $paramsCallback, $resultsCallback + ) + ) + ); + return $search; + } + + /** + * Get a mock row representing a user. + * + * @return \VuFind\Db\Row\Search + */ + protected function getMockUserObject() + { + $data = [ + 'id' => 2, + 'username' => 'foo', + 'email' => 'fake@myuniversity.edu', + 'created' => '2000-01-01 00:00:00', + 'last_language' => 'en', + ]; + $adapter = $this->prepareMock(\Laminas\Db\Adapter\Adapter::class); + $user = new \VuFind\Db\Row\User($adapter); + $user->populate($data, true); + return $user; + } + + /** + * Get a notify command for testing. + * + * @param array $options Options to override + * + * @return NotifyCommand + */ + protected function getCommand($options = []) + { + $renderer = $options['renderer'] + ?? $this->prepareMock(\Laminas\View\Renderer\PhpRenderer::class); + $renderer->expects($this->any())->method('plugin') + ->with($this->equalTo('url')) + ->will($this->returnValue($this->prepareMock(\Laminas\View\Helper\Url::class))); + $command = new NotifyCommand( + $this->prepareMock(\VuFind\Crypt\HMAC::class), + $renderer, + $this->getMockResultsManager(), + $options['scheduleOptions'] ?? [1 => 'Daily', 7 => 'Weekly'], + new \Laminas\Config\Config( + $options['configArray'] ?? [ + 'Site' => [ + 'institution' => 'My Institution', + 'title' => 'My Site', + 'email' => 'admin@myuniversity.edu', + ] + ] + ), + $options['mailer'] ?? $this->prepareMock(\VuFind\Mailer\Mailer::class), + $options['searchTable'] ?? $this->prepareMock(\VuFind\Db\Table\Search::class), + $options['userTable'] ?? $this->prepareMock(\VuFind\Db\Table\User::class) + ); + $command->setTranslator( + $options['translator'] ?? $this->prepareMock(\Laminas\I18n\Translator\Translator::class) + ); + return $command; + } + + /** + * Create a mock results manager. + * + * @return \VuFind\Search\Results\PluginManager + */ + protected function getMockResultsManager() + { + // Use a static variable to ensure we only create a single shared instance + // of the results manager. + static $manager = false; + if (!$manager) { + $manager = $this + ->prepareMock(\VuFind\Search\Results\PluginManager::class); + } + return $manager; + } + + /** + * Create a mock search table that returns a list of fake notification objects. + * + * @param array $overrides Fields to override in the notification row. + * @param \Callable $optionsCallback Callback to set expectations on options object + * @param \Callable $paramsCallback Callback to set expectations on params object + * @param \Callable $resultsCallback Callback to set expectations on results object + * + * @return array + */ + protected function getMockSearchTable($overrides = [], $optionsCallback = null, + $paramsCallback = null, $resultsCallback = null) + { + $searchTable = $this->prepareMock(\VuFind\Db\Table\Search::class); + $searchTable->expects($this->once())->method('getScheduledSearches') + ->will( + $this->returnValue( + $this->getMockNotifications( + $overrides, $optionsCallback, $paramsCallback, + $resultsCallback + ) + ) + ); + return $searchTable; + } + + /** + * Create a mock user table that returns a fake user object. + * + * @return array + */ + protected function getMockUserTable() + { + $user = $this->getMockUserObject(); + $userTable = $this->prepareMock(\VuFind\Db\Table\User::class); + $userTable->expects($this->any())->method('getById') + ->with($this->equalTo(2))->will($this->returnValue($user)); + return $userTable; + } + + /** + * Prepare a mock object + * + * @param string $class Class to mock + * + * @return mixed + */ + protected function prepareMock($class) + { + return $this->getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/AbstractExpireCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/AbstractExpireCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..576d3c04382b3687e6af49637e1e941e669a8576 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/AbstractExpireCommandTest.php @@ -0,0 +1,166 @@ +<?php +/** + * AbstractExpireCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\AbstractExpireCommand; + +/** + * AbstractExpireCommand 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 AbstractExpireCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Name of class being tested + * + * @var string + */ + protected $targetClass = AbstractExpireCommand::class; + + /** + * Name of a valid table class to test with + * + * @var string + */ + protected $validTableClass = \VuFind\Db\Table\AuthHash::class; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'rows'; + + /** + * Test an unsupported table class. + * + * @return void + */ + public function testUnsupportedTableClass() + { + $table = $this->getMockBuilder(\VuFind\Db\Table\User::class) + ->disableOriginalConstructor() + ->getMock(); + $this->expectException(\Exception::class); + $this->expectExceptionMessage( + get_class($table) . ' does not support getExpiredIdRange()' + ); + $command = new $this->targetClass($table, 'foo'); + } + + /** + * Test an illegal age parameter. + * + * @return void + */ + public function testIllegalAgeInput() + { + $table = $this->getMockBuilder($this->validTableClass) + ->disableOriginalConstructor() + ->getMock(); + $command = new $this->targetClass($table, 'foo'); + $commandTester = new CommandTester($command); + $commandTester->execute(['age' => 1]); + $this->assertEquals( + "Expiration age must be at least 2 days.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(1, $commandTester->getStatusCode()); + } + + /** + * Test that the command expires rows correctly. + * + * @return void + */ + public function testSuccessfulExpiration() + { + $table = $this->getMockBuilder($this->validTableClass) + ->disableOriginalConstructor() + ->getMock(); + $table->expects($this->at(0))->method('getExpiredIdRange') + ->with($this->equalTo(2)) + ->will($this->returnValue([0, 1500])); + $table->expects($this->at(1))->method('deleteExpired') + ->with($this->equalTo(2), $this->equalTo(0), $this->equalTo(999)) + ->will($this->returnValue(50)); + $table->expects($this->at(2))->method('deleteExpired') + ->with($this->equalTo(2), $this->equalTo(1000), $this->equalTo(1999)) + ->will($this->returnValue(7)); + $command = new $this->targetClass($table, 'foo'); + $commandTester = new CommandTester($command); + $commandTester->execute(['--sleep' => 1]); + $response = $commandTester->getDisplay(); + // The response contains date stamps that will vary every time the test + // runs, so let's split things apart to work around that... + $parts = explode("\n", trim($response)); + $this->assertEquals(2, count($parts)); + $this->assertEquals( + "50 {$this->rowLabel} deleted.", + explode('] ', $parts[0])[1] + ); + $this->assertEquals( + "7 {$this->rowLabel} deleted.", + explode('] ', $parts[1])[1] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } + + /** + * Test correct behavior when no rows need to be expired. + * + * @return void + */ + public function testSuccessfulNonExpiration() + { + $table = $this->getMockBuilder($this->validTableClass) + ->disableOriginalConstructor() + ->getMock(); + $table->expects($this->once())->method('getExpiredIdRange') + ->with($this->equalTo(2)) + ->will($this->returnValue(false)); + $command = new $this->targetClass($table, 'foo'); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $response = $commandTester->getDisplay(); + // The response contains date stamps that will vary every time the test + // runs, so let's split things apart to work around that... + $parts = explode("\n", trim($response)); + $this->assertEquals(1, count($parts)); + $this->assertEquals( + "No {$this->rowLabel} to delete.", explode('] ', $parts[0])[1] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CleanUpRecordCacheCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CleanUpRecordCacheCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c77f04130e5edff9c65a6fbaea8d3b2851c07da4 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CleanUpRecordCacheCommandTest.php @@ -0,0 +1,62 @@ +<?php +/** + * CleanUpRecordCacheCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\CleanUpRecordCacheCommand; + +/** + * CleanUpRecordCacheCommand 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 CleanUpRecordCacheCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that the cache clear action is delegated properly. + * + * @return void + */ + public function testBasicOperation() + { + $table = $this->getMockBuilder(\VuFind\Db\Table\Record::class) + ->disableOriginalConstructor()->getMock(); + $table->expects($this->once())->method('cleanup') + ->will($this->returnValue(5)); + $command = new CleanUpRecordCacheCommand($table); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $expected = "5 records deleted.\n"; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CommitCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CommitCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1d0519be3119dac558f7c573fce9babed65526f --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CommitCommandTest.php @@ -0,0 +1,62 @@ +<?php +/** + * CommitCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\CommitCommand; + +/** + * CommitCommand 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 CommitCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test success with all options set. + * + * @return void + */ + public function testSuccessWithOptions() + { + $writer = $this->getMockBuilder(\VuFind\Solr\Writer::class) + ->disableOriginalConstructor() + ->getMock(); + $writer->expects($this->once())->method('commit') + ->with($this->equalTo('foo')); + $command = new CommitCommand($writer); + $commandTester = new CommandTester($command); + $commandTester->execute(['core' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CreateHierarchyTreesCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CreateHierarchyTreesCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..af357e7e1fb1ac09db2e4f24e4db95e6fede3da6 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CreateHierarchyTreesCommandTest.php @@ -0,0 +1,215 @@ +<?php +/** + * CreateHierarchyTreesCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Hierarchy\Driver\ConfigurationBased as HierarchyDriver; +use VuFind\Hierarchy\TreeDataSource\Solr as TreeSource; +use VuFind\Record\Loader; +use VuFind\Search\Results\PluginManager; +use VuFind\Search\Solr\Results; +use VuFindConsole\Command\Util\CreateHierarchyTreesCommand; + +/** + * CreateHierarchyTreesCommand 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 CreateHierarchyTreesCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get mock hierarchy driver + * + * @return HierarchyDriver + */ + protected function getMockHierarchyDriver() + { + return $this->getMockBuilder(HierarchyDriver::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get mock tree source + * + * @return TreeSource + */ + protected function getMockTreeSource() + { + return $this->getMockBuilder(TreeSource::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get mock record. + * + * @param HierarchyDriver $driver Hierarchy driver + * + * @return \VuFind\RecordDriver\AbstractBase + */ + protected function getMockRecord($driver = null) + { + $record = new \VuFindTest\RecordDriver\TestHarness(); + $record->setRawData( + [ + 'HierarchyType' => 'foo', + 'HierarchyDriver' => $driver ?? $this->getMockHierarchyDriver() + ] + ); + return $record; + } + + /** + * Get mock record loader. + * + * @param \VuFind\RecordDriver\AbstractBase $record Record driver + * + * @return Loader + */ + protected function getMockRecordLoader($record = null) + { + $loader = $this->getMockBuilder(Loader::class) + ->disableOriginalConstructor() + ->getMock(); + $loader->expects($this->once())->method('load') + ->with($this->equalTo('recordid'), $this->equalTo('foo')) + ->will($this->returnValue($record ?? $this->getMockRecord())); + return $loader; + } + + /** + * Get mock results. + * + * @return Results + */ + protected function getMockResults() + { + $results = $this->getMockBuilder(Results::class) + ->disableOriginalConstructor() + ->getMock(); + $output = [ + 'hierarchy_top_id' => [ + 'data' => [ + 'list' => [ + [ + 'value' => 'recordid', + 'count' => 5, + ] + ] + ] + ] + ]; + $results->expects($this->once())->method('getFullFieldFacets') + ->with($this->equalTo(['hierarchy_top_id'])) + ->will($this->returnValue($output)); + return $results; + } + + /** + * Get mock results manager. + * + * @param Results $results Results object + * + * @return PluginManager + */ + protected function getMockResultsManager($results = null) + { + $manager = $this->getMockBuilder(PluginManager::class) + ->disableOriginalConstructor() + ->getMock(); + $manager->expects($this->once())->method('get') + ->with($this->equalTo('foo')) + ->will($this->returnValue($results ?? $this->getMockResults())); + return $manager; + } + + /** + * Get command to test. + * + * @param Loader $loader Record loader + * @param PluginManager $results Search results plugin manager + * + * @return SuppressedCommand + */ + protected function getCommand(Loader $loader = null, + PluginManager $results = null + ) { + return new CreateHierarchyTreesCommand( + $loader ?? $this->getMockRecordLoader(), + $results ?? $this->getMockResultsManager() + ); + } + + /** + * Test skipping everything. + * + * @return void + */ + public function testSkippingEverything() + { + $command = $this->getCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['--skip-xml' => true, '--skip-json' => true, 'backend' => 'foo'] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $expectedText = "\tBuilding tree for recordid... 5 records\n" + . "\t\tJSON skipped.\n\t\tXML skipped.\n1 files\n"; + $this->assertEquals($expectedText, $commandTester->getDisplay()); + } + + /** + * Test populating everything. + * + * @return void + */ + public function testPopulatingEverything() + { + $tree = $this->getMockTreeSource(); + $tree->expects($this->once())->method('getJSON') + ->with($this->equalTo('recordid'), $this->equalTo(['refresh' => true])); + $tree->expects($this->once())->method('getXML') + ->with($this->equalTo('recordid'), $this->equalTo(['refresh' => true])); + $driver = $this->getMockHierarchyDriver(); + $driver->expects($this->any())->method('getTreeSource') + ->will($this->returnValue($tree)); + $loader = $this->getMockRecordLoader($this->getMockRecord($driver)); + $command = $this->getCommand($loader); + $commandTester = new CommandTester($command); + $commandTester->execute(['backend' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $expectedText = "\tBuilding tree for recordid... 5 records\n" + . "\t\tJSON cache...\n\t\tXML cache...\n1 files\n"; + $this->assertEquals($expectedText, $commandTester->getDisplay()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CssBuilderCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CssBuilderCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..123c78b426dbface4ce48b679a34114418810e60 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/CssBuilderCommandTest.php @@ -0,0 +1,69 @@ +<?php +/** + * CssBuilderCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\CssBuilderCommand; + +/** + * CssBuilderCommand 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 CssBuilderCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that the command delegates proper behavior. + * + * @return void + */ + public function testBasicOperation() + { + $cacheDir = '/foo'; + $compiler = $this->getMockBuilder(\VuFindTheme\LessCompiler::class) + ->disableOriginalConstructor()->getMock(); + $compiler->expects($this->once())->method('setTempPath') + ->with($this->equalTo($cacheDir)); + $compiler->expects($this->once())->method('compile') + ->with($this->equalTo(['foo', 'bar'])); + $command = $this->getMockBuilder(CssBuilderCommand::class) + ->setMethods(['getCompiler']) + ->setConstructorArgs([$cacheDir]) + ->getMock(); + $command->expects($this->once())->method('getCompiler') + ->will($this->returnValue($compiler)); + $commandTester = new CommandTester($command); + $commandTester->execute(['themes' => ['foo', 'bar', 'foo']]); + $this->assertEquals('', $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DedupeCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DedupeCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f5f6aa4c0c72627b775b64099f297cd9fdd6002b --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DedupeCommandTest.php @@ -0,0 +1,160 @@ +<?php +/** + * DedupeCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\DedupeCommand; + +/** + * DedupeCommand 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 DedupeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get a mocked-out command object. + * + * @return DedupeCommand + */ + protected function getMockCommand() + { + $mockMethods = [ + 'getInput', + 'openOutputFile', + 'writeToOutputFile', + 'closeOutputFile', + ]; + return $this->getMockBuilder(DedupeCommand::class) + ->setMethods($mockMethods) + ->getMock(); + } + + /** + * Set up basic expectations on a command. + * + * @param DedupeCommand $command Mock command + * @param string $output Output filename + * @param int $sequence Expectation sequence number + * + * @return void + */ + protected function setSuccessfulExpectations($command, $output, $sequence = 0) + { + $fakeHandle = 7; // arbitrary number for test purposes + $command->expects($this->at($sequence++))->method('openOutputFile') + ->with($this->equalTo($output)) + ->will($this->returnValue($fakeHandle)); + $command->expects($this->at($sequence++))->method('writeToOutputFile') + ->with($this->equalTo($fakeHandle), $this->equalTo("foo\n")); + $command->expects($this->at($sequence++))->method('writeToOutputFile') + ->with($this->equalTo($fakeHandle), $this->equalTo("bar\n")); + $command->expects($this->at($sequence++))->method('writeToOutputFile') + ->with($this->equalTo($fakeHandle), $this->equalTo("baz\n")); + $command->expects($this->at($sequence++))->method('closeOutputFile') + ->with($this->equalTo($fakeHandle)); + } + + /** + * Test that missing file yields an error message. + * + * @return void + */ + public function testWithMissingFile() + { + $command = new DedupeCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute(['input' => '/does/not/exist']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "Could not open input file: /does/not/exist\n", + $commandTester->getDisplay() + ); + } + + /** + * Test success with command line arguments. + * + * @return void + */ + public function testSuccessWithArguments() + { + $outputFilename = '/fake/outfile'; + $command = $this->getMockCommand(); + $this->setSuccessfulExpectations($command, $outputFilename); + $commandTester = new CommandTester($command); + $fixture = __DIR__ . '/../../../../../fixtures/fileWithDuplicateLines'; + $commandTester->execute( + [ + 'input' => $fixture, + 'output' => $outputFilename, + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } + + /** + * Test success with interactive input. + * + * @return void + */ + public function testSuccessWithoutArguments() + { + $fixture = __DIR__ . '/../../../../../fixtures/fileWithDuplicateLines'; + $outputFilename = '/fake/outfile'; + $command = $this->getMockCommand(); + $command->expects($this->at(0))->method('getInput') + ->with( + $this->isInstanceOf(InputInterface::class), + $this->isInstanceOf(OutputInterface::class), + $this->equalTo( + 'Please specify an input file: ' + ) + )->will($this->returnValue($fixture)); + $command->expects($this->at(1))->method('getInput') + ->with( + $this->isInstanceOf(InputInterface::class), + $this->isInstanceOf(OutputInterface::class), + $this->equalTo( + 'Please specify an output file: ' + ) + )->will($this->returnValue($outputFilename)); + $this->setSuccessfulExpectations($command, $outputFilename, 2); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DeletesCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DeletesCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6044aec354b54d87203759fb655c4d937fcda1ba --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/DeletesCommandTest.php @@ -0,0 +1,137 @@ +<?php +/** + * DeletesCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\DeletesCommand; + +/** + * DeletesCommand 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 DeletesCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get mock Solr writer. + * + * @return \VuFind\Solr\Writer + */ + protected function getMockWriter() + { + return $this->getMockBuilder(\VuFind\Solr\Writer::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "filename").' + ); + $writer = $this->getMockWriter(); + $command = new DeletesCommand($writer); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test that missing file yields an error message. + * + * @return void + */ + public function testWithMissingFile() + { + $writer = $this->getMockWriter(); + $command = new DeletesCommand($writer); + $commandTester = new CommandTester($command); + $commandTester->execute(['filename' => '/does/not/exist']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "Cannot find file: /does/not/exist\n", $commandTester->getDisplay() + ); + } + + /** + * Test success with a flat file and default index. + * + * @return void + */ + public function testSuccessWithFlatFileAndDefaultIndex() + { + $writer = $this->getMockWriter(); + $writer->expects($this->once())->method('deleteRecords') + ->with($this->equalTo('Solr'), $this->equalTo(['rec1', 'rec2', 'rec3'])); + $command = new DeletesCommand($writer); + $commandTester = new CommandTester($command); + $fixture = __DIR__ . '/../../../../../fixtures/deletes'; + $commandTester->execute( + [ + 'filename' => $fixture, + 'format' => 'flat', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } + + /** + * Test success with a MARC file and non-default index. + * + * @return void + */ + public function testSuccessWithMarcFileAndNonDefaultIndex() + { + $writer = $this->getMockWriter(); + $writer->expects($this->once())->method('deleteRecords') + ->with($this->equalTo('foo'), $this->equalTo(['testbug2'])); + $command = new DeletesCommand($writer); + $commandTester = new CommandTester($command); + $fixture = __DIR__ . '/../../../../../../../../tests/data/testbug2.mrc'; + $commandTester->execute( + [ + 'filename' => $fixture, + 'index' => 'foo', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireAuthHashesCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireAuthHashesCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..0c3758c77ddb37c26197d6ee29de1aff96e01b7b --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireAuthHashesCommandTest.php @@ -0,0 +1,63 @@ +<?php +/** + * ExpireAuthHashesCommand test. + * + * 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\Command\Util; + +use VuFindConsole\Command\Util\ExpireAuthHashesCommand; + +/** + * ExpireAuthHashesCommand 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 ExpireAuthHashesCommandTest extends AbstractExpireCommandTest +{ + /** + * Name of class being tested + * + * @var string + */ + protected $targetClass = ExpireAuthHashesCommand::class; + + /** + * Name of a valid table class to test with + * + * @var string + */ + protected $validTableClass = \VuFind\Db\Table\AuthHash::class; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'authentication hashes'; +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireExternalSessionsCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireExternalSessionsCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b2b5601cf4fb54f58c66400773d9fbc23854f109 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireExternalSessionsCommandTest.php @@ -0,0 +1,63 @@ +<?php +/** + * ExpireExternalSessionsCommand test. + * + * 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\Command\Util; + +use VuFindConsole\Command\Util\ExpireExternalSessionsCommand; + +/** + * ExpireExternalSessionsCommand 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 ExpireExternalSessionsCommandTest extends AbstractExpireCommandTest +{ + /** + * Name of class being tested + * + * @var string + */ + protected $targetClass = ExpireExternalSessionsCommand::class; + + /** + * Name of a valid table class to test with + * + * @var string + */ + protected $validTableClass = \VuFind\Db\Table\ExternalSession::class; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'external sessions'; +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSearchesCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSearchesCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..331128f640f237b3ae9fecd460eb7498003949f9 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSearchesCommandTest.php @@ -0,0 +1,63 @@ +<?php +/** + * ExpireSearchesCommand test. + * + * 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\Command\Util; + +use VuFindConsole\Command\Util\ExpireSearchesCommand; + +/** + * ExpireSearchesCommand 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 ExpireSearchesCommandTest extends AbstractExpireCommandTest +{ + /** + * Name of class being tested + * + * @var string + */ + protected $targetClass = ExpireSearchesCommand::class; + + /** + * Name of a valid table class to test with + * + * @var string + */ + protected $validTableClass = \VuFind\Db\Table\Search::class; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'searches'; +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSessionsCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSessionsCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..314daf365369dd55a807d0fc595af5a968551a68 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/ExpireSessionsCommandTest.php @@ -0,0 +1,63 @@ +<?php +/** + * ExpireSessionsCommand test. + * + * 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\Command\Util; + +use VuFindConsole\Command\Util\ExpireSessionsCommand; + +/** + * ExpireSessionsCommand 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 ExpireSessionsCommandTest extends AbstractExpireCommandTest +{ + /** + * Name of class being tested + * + * @var string + */ + protected $targetClass = ExpireSessionsCommand::class; + + /** + * Name of a valid table class to test with + * + * @var string + */ + protected $validTableClass = \VuFind\Db\Table\Session::class; + + /** + * Label to use for rows in help messages. + * + * @var string + */ + protected $rowLabel = 'sessions'; +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/IndexReservesCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/IndexReservesCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..26bcff0c3082a67d002c58def563b063fb121ae3 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/IndexReservesCommandTest.php @@ -0,0 +1,299 @@ +<?php +/** + * IndexReservesCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\ILS\Connection; +use VuFind\Solr\Writer; +use VuFindConsole\Command\Util\IndexReservesCommand; + +/** + * IndexReservesCommand 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 IndexReservesCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get mock ILS connection. + * + * @return Connection + */ + protected function getMockIlsConnection() + { + return $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get mock Solr writer. + * + * @return Writer + */ + protected function getMockSolrWriter() + { + return $this->getMockBuilder(Writer::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get command to test. + * + * @param Writer $solr Solr writer + * @param Connection $ils ILS connection + * + * @return IndexReservesCommand + */ + protected function getCommand(Writer $solr = null, Connection $ils = null) + { + return new IndexReservesCommand( + $solr ?? $this->getMockSolrWriter(), + $ils ?? $this->getMockIlsConnection() + ); + } + + /** + * Test bad parameter combination. + * + * @return void + */ + public function testBadParameterCombination() + { + $command = $this->getCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute(['--delimiter' => '|']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "-d (delimiter) is meaningless without -f (filename)\n", + $commandTester->getDisplay() + ); + } + + /** + * Test missing file. + * + * @return void + */ + public function testBadFilename() + { + $command = $this->getCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute(['--filename' => '/does/not/exist']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "Could not open /does/not/exist!\n", + $commandTester->getDisplay() + ); + } + + /** + * Test successful file loading. + * + * @return void + */ + public function testSuccessWithMultipleFiles() + { + $writer = $this->getMockSolrWriter(); + $writer->expects($this->once())->method('deleteAll') + ->with($this->equalTo('SolrReserves')); + $that = $this; + $updateValidator = function ($update) use ($that) { + $expectedXml = "<?xml version=\"1.0\"?>\n" + . '<add>' + . '<doc>' + . '<field name="id">course1|inst1|dept1</field>' + . '<field name="bib_id">1</field>' + . '<field name="instructor_id">inst1</field>' + . '<field name="instructor">inst1</field>' + . '<field name="course_id">course1</field>' + . '<field name="course">course1</field>' + . '<field name="department_id">dept1</field>' + . '<field name="department">dept1</field>' + . '</doc>' + . '<doc>' + . '<field name="id">course2|inst2|dept2</field>' + . '<field name="bib_id">2</field>' + . '<field name="instructor_id">inst2</field>' + . '<field name="instructor">inst2</field>' + . '<field name="course_id">course2</field>' + . '<field name="course">course2</field>' + . '<field name="department_id">dept2</field>' + . '<field name="department">dept2</field>' + . '</doc>' + . '<doc>' + . '<field name="id">course3|inst3|dept3</field>' + . '<field name="bib_id">3</field>' + . '<field name="instructor_id">inst3</field>' + . '<field name="instructor">inst3</field>' + . '<field name="course_id">course3</field>' + . '<field name="course">course3</field>' + . '<field name="department_id">dept3</field>' + . '<field name="department">dept3</field>' + . '</doc>' + . '</add>'; + $that->assertEquals($expectedXml, trim($update->asXml())); + return true; + }; + $writer->expects($this->once())->method('save') + ->with( + $this->equalTo('SolrReserves'), + $this->callback($updateValidator) + ); + $writer->expects($this->once())->method('commit') + ->with($this->equalTo('SolrReserves')); + $writer->expects($this->once())->method('optimize') + ->with($this->equalTo('SolrReserves')); + $command = $this->getCommand($writer); + $commandTester = new CommandTester($command); + $fixture1 = __DIR__ . '/../../../../../fixtures/reserves/fixture1'; + $fixture2 = __DIR__ . '/../../../../../fixtures/reserves/fixture2'; + $commandTester->execute( + [ + '--filename' => [$fixture1, $fixture2], + '--delimiter' => '|', + '--template' => 'BIB_ID,SKIP,COURSE,DEPARTMENT,INSTRUCTOR', + ] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "Successfully loaded 3 rows.\n", + $commandTester->getDisplay() + ); + } + + /** + * Test successful ILS loading. + * + * @return void + */ + public function testSuccessWithILS() + { + $ils = $this->getMockIlsConnection(); + $instructors = ['inst1' => 'inst1', 'inst2' => 'inst2', 'inst3' => 'inst3']; + $ils->expects($this->at(0))->method('__call') + ->with($this->equalTo('getInstructors')) + ->will($this->returnValue($instructors)); + $courses = [ + 'course1' => 'course1', 'course2' => 'course2', 'course3' => 'course3' + ]; + $ils->expects($this->at(1))->method('__call') + ->with($this->equalTo('getCourses')) + ->will($this->returnValue($courses)); + $departments = ['dept1' => 'dept1', 'dept2' => 'dept2', 'dept3' => 'dept3']; + $ils->expects($this->at(2))->method('__call') + ->with($this->equalTo('getDepartments')) + ->will($this->returnValue($departments)); + $reserves = [ + [ + 'BIB_ID' => 1, + 'COURSE_ID' => 'course1', + 'DEPARTMENT_ID' => 'dept1', + 'INSTRUCTOR_ID' => 'inst1', + ], + [ + 'BIB_ID' => 2, + 'COURSE_ID' => 'course2', + 'DEPARTMENT_ID' => 'dept2', + 'INSTRUCTOR_ID' => 'inst2', + ], + [ + 'BIB_ID' => 3, + 'COURSE_ID' => 'course3', + 'DEPARTMENT_ID' => 'dept3', + 'INSTRUCTOR_ID' => 'inst3', + ], + ]; + $ils->expects($this->at(3))->method('__call') + ->with($this->equalTo('findReserves'), $this->equalTo(['', '', ''])) + ->will($this->returnValue($reserves)); + $writer = $this->getMockSolrWriter(); + $writer->expects($this->once())->method('deleteAll') + ->with($this->equalTo('SolrReserves')); + $that = $this; + $updateValidator = function ($update) use ($that) { + $expectedXml = "<?xml version=\"1.0\"?>\n" + . '<add>' + . '<doc>' + . '<field name="id">course1|inst1|dept1</field>' + . '<field name="bib_id">1</field>' + . '<field name="instructor_id">inst1</field>' + . '<field name="instructor">inst1</field>' + . '<field name="course_id">course1</field>' + . '<field name="course">course1</field>' + . '<field name="department_id">dept1</field>' + . '<field name="department">dept1</field>' + . '</doc>' + . '<doc>' + . '<field name="id">course2|inst2|dept2</field>' + . '<field name="bib_id">2</field>' + . '<field name="instructor_id">inst2</field>' + . '<field name="instructor">inst2</field>' + . '<field name="course_id">course2</field>' + . '<field name="course">course2</field>' + . '<field name="department_id">dept2</field>' + . '<field name="department">dept2</field>' + . '</doc>' + . '<doc>' + . '<field name="id">course3|inst3|dept3</field>' + . '<field name="bib_id">3</field>' + . '<field name="instructor_id">inst3</field>' + . '<field name="instructor">inst3</field>' + . '<field name="course_id">course3</field>' + . '<field name="course">course3</field>' + . '<field name="department_id">dept3</field>' + . '<field name="department">dept3</field>' + . '</doc>' + . '</add>'; + $that->assertEquals($expectedXml, trim($update->asXml())); + return true; + }; + $writer->expects($this->once())->method('save') + ->with( + $this->equalTo('SolrReserves'), + $this->callback($updateValidator) + ); + $writer->expects($this->once())->method('commit') + ->with($this->equalTo('SolrReserves')); + $writer->expects($this->once())->method('optimize') + ->with($this->equalTo('SolrReserves')); + $command = $this->getCommand($writer, $ils); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "Successfully loaded 3 rows.\n", + $commandTester->getDisplay() + ); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/LintMarcCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/LintMarcCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a3552bd041c2b155fd5ea54adea7c0f235e02832 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/LintMarcCommandTest.php @@ -0,0 +1,82 @@ +<?php +/** + * LintMarc command test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\LintMarcCommand; + +/** + * LintMarc command 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 LintMarcCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "filename").' + ); + $command = new LintMarcCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test that linting a file yields useful messages. + * + * @return void + */ + public function testLintingFile() + { + $command = new LintMarcCommand(); + $commandTester = new CommandTester($command); + $filename = __DIR__ . '/../../../../../../../../tests/data/heb.mrc'; + $commandTester->execute(compact('filename')); + $expected = <<<EXPECTED +Checking record 1 (001 = testbug1)... +Warnings: 245: Must end with . (period). +245: Subfield _b should be preceded by space-colon, space-semicolon, or space-equals sign. + +EXPECTED; + $this->assertEquals($expected, $commandTester->getDisplay()); + $this->assertEquals(0, $commandTester->getStatusCode()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/OptimizeCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/OptimizeCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..42ecb3975d039169495b9249ca661f5a19635eab --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/OptimizeCommandTest.php @@ -0,0 +1,64 @@ +<?php +/** + * OptimizeCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\OptimizeCommand; + +/** + * OptimizeCommand 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 OptimizeCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test success with all options set. + * + * @return void + */ + public function testSuccessWithOptions() + { + $writer = $this->getMockBuilder(\VuFind\Solr\Writer::class) + ->disableOriginalConstructor() + ->getMock(); + $writer->expects($this->once())->method('commit') + ->with($this->equalTo('foo')); + $writer->expects($this->once())->method('optimize') + ->with($this->equalTo('foo')); + $command = new OptimizeCommand($writer); + $commandTester = new CommandTester($command); + $commandTester->execute(['core' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SitemapCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SitemapCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e0e0102efca7d3f145e5c5e8ad0726944deec205 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SitemapCommandTest.php @@ -0,0 +1,72 @@ +<?php +/** + * SitemapCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFindConsole\Command\Util\SitemapCommand; + +/** + * SitemapCommand 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 SitemapCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test success with all options set. + * + * @return void + */ + public function testSuccessWithOptions() + { + $generator = $this->getMockBuilder(\VuFind\Sitemap\Generator::class) + ->disableOriginalConstructor() + ->getMock(); + $generator->expects($this->once())->method('setBaseUrl') + ->with($this->equalTo('http://foo')); + $generator->expects($this->once())->method('setBaseSitemapUrl') + ->with($this->equalTo('http://bar')); + $generator->expects($this->once())->method('generate'); + $generator->expects($this->once())->method('getWarnings') + ->will($this->returnValue(['Sample warning'])); + $command = new SitemapCommand($generator); + $commandTester = new CommandTester($command); + $commandTester->execute( + ['--baseurl' => 'http://foo', '--basesitemapurl' => 'http://bar'] + ); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "Sample warning\n", + $commandTester->getDisplay() + ); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SuppressedCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SuppressedCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fa88d33574068978eb2cedced7a34b1f7b665f63 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SuppressedCommandTest.php @@ -0,0 +1,200 @@ +<?php +/** + * SuppressedCommand test. + * + * 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\Command\Util; + +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\ILS\Connection; +use VuFind\Solr\Writer; +use VuFindConsole\Command\Util\SuppressedCommand; + +/** + * SuppressedCommand 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 SuppressedCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Get mock ILS connection. + * + * @return Connection + */ + protected function getMockIlsConnection() + { + return $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get mock Solr writer. + * + * @return Writer + */ + protected function getMockSolrWriter() + { + return $this->getMockBuilder(Writer::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get command to test. + * + * @param Writer $solr Solr writer + * @param Connection $ils ILS connection + * + * @return SuppressedCommand + */ + protected function getCommand(Writer $solr = null, Connection $ils = null) + { + $args = [ + $solr ?? $this->getMockSolrWriter(), + $ils ?? $this->getMockIlsConnection() + ]; + return $this->getMockBuilder(SuppressedCommand::class) + ->setConstructorArgs($args) + ->setMethods(['writeToDisk']) + ->getMock(); + } + + /** + * Test no results coming back from ILS + * + * @return void + */ + public function testNoRecordsToDelete() + { + $ils = $this->getMockIlsConnection(); + $ils->expects($this->once())->method('__call') + ->with($this->equalTo('getSuppressedRecords')) + ->will($this->returnValue([])); + $command = $this->getCommand(null, $ils); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "No suppressed records to delete.\n", + $commandTester->getDisplay() + ); + } + + /** + * Test successful Solr update. + * + * @return void + */ + public function testRecordsToDelete() + { + $ils = $this->getMockIlsConnection(); + $ils->expects($this->once())->method('__call') + ->with($this->equalTo('getSuppressedRecords')) + ->will($this->returnValue([1, 2])); + $solr = $this->getMockSolrWriter(); + $solr->expects($this->once())->method('deleteRecords') + ->with($this->equalTo('Solr'), $this->equalTo([1, 2])); + $solr->expects($this->once())->method('commit') + ->with($this->equalTo('Solr')); + $solr->expects($this->once())->method('optimize') + ->with($this->equalTo('Solr')); + $command = $this->getCommand($solr, $ils); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals("", $commandTester->getDisplay()); + } + + /** + * Test no results coming back from ILS + * + * @return void + */ + public function testNoAuthorityRecordsToDelete() + { + $ils = $this->getMockIlsConnection(); + $ils->expects($this->once())->method('__call') + ->with($this->equalTo('getSuppressedAuthorityRecords')) + ->will($this->returnValue([])); + $command = $this->getCommand(null, $ils); + $commandTester = new CommandTester($command); + $commandTester->execute(['--authorities' => true]); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "No suppressed records to delete.\n", + $commandTester->getDisplay() + ); + } + + /** + * Test write to file. + * + * @return void + */ + public function testWriteToFile() + { + $ils = $this->getMockIlsConnection(); + $ils->expects($this->once())->method('__call') + ->with($this->equalTo('getSuppressedRecords')) + ->will($this->returnValue([1, 2])); + $command = $this->getCommand(null, $ils); + $command->expects($this->once())->method('writeToDisk') + ->with($this->equalTo('foo'), $this->equalTo("1\n2")) + ->will($this->returnValue(true)); + $commandTester = new CommandTester($command); + $commandTester->execute(['--outfile' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals('', $commandTester->getDisplay()); + } + + /** + * Test failed write to file. + * + * @return void + */ + public function testFailedWriteToFile() + { + $ils = $this->getMockIlsConnection(); + $ils->expects($this->once())->method('__call') + ->with($this->equalTo('getSuppressedRecords')) + ->will($this->returnValue([1, 2])); + $command = $this->getCommand(null, $ils); + $command->expects($this->once())->method('writeToDisk') + ->with($this->equalTo('foo'), $this->equalTo("1\n2")) + ->will($this->returnValue(false)); + $commandTester = new CommandTester($command); + $commandTester->execute(['--outfile' => 'foo']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "Problem writing to foo\n", $commandTester->getDisplay() + ); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SwitchDbHashCommandTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SwitchDbHashCommandTest.php new file mode 100644 index 0000000000000000000000000000000000000000..c66c1cbc8f9399a11c48649a556cd7a42297f560 --- /dev/null +++ b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Command/Util/SwitchDbHashCommandTest.php @@ -0,0 +1,312 @@ +<?php +/** + * SwitchDbHashCommand test. + * + * 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\Command\Util; + +use Laminas\Config\Config; +use Laminas\Crypt\BlockCipher; +use Laminas\Crypt\Symmetric\Openssl; +use Symfony\Component\Console\Tester\CommandTester; +use VuFind\Config\Locator; +use VuFind\Config\Writer; +use VuFind\Db\Table\User; +use VuFindConsole\Command\Util\SwitchDbHashCommand; + +/** + * SwitchDbHashCommand 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 SwitchDbHashCommandTest extends \PHPUnit\Framework\TestCase +{ + /** + * Prepare a mock object + * + * @param string $class Class to mock + * + * @return mixed + */ + protected function prepareMock($class) + { + return $this->getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Get mock table object + * + * @return User + */ + protected function getMockTable() + { + return $this->prepareMock(User::class); + } + + /** + * Get mock command object + * + * @param array $config Config settings + * @param User $table User table gateway + */ + protected function getMockCommand(array $config = [], $table = null) + { + return $this->getMockBuilder(SwitchDbHashCommand::class) + ->setConstructorArgs( + [ + new Config($config), + $table ?? $this->getMockTable(), + ] + )->setMethods(['getConfigWriter']) + ->getMock(); + } + + /** + * Get a mock config writer + * + * @return Writer + */ + protected function getMockConfigWriter() + { + return $this->prepareMock(Writer::class); + } + + /** + * Test that missing parameters yield an error message. + * + * @return void + */ + public function testWithoutParameters() + { + $this->expectException( + \Symfony\Component\Console\Exception\RuntimeException::class + ); + $this->expectExceptionMessage( + 'Not enough arguments (missing: "newmethod").' + ); + $command = $this->getMockCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + /** + * Test missing key parameter (not in config or on command line). + * + * @return void + */ + public function testWithoutKeyParameter() + { + $command = $this->getMockCommand(); + $commandTester = new CommandTester($command); + $commandTester->execute(['newmethod' => 'blowfish']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $this->assertEquals( + "Please specify a key as the second parameter.\n", + $commandTester->getDisplay() + ); + } + + /** + * Test no action needed because no changes requested. + * + * @return void + */ + public function testNoActionNeeded() + { + $command = $this->getMockCommand( + [ + 'Authentication' => [ + 'encrypt_ils_password' => true, + 'ils_encryption_algo' => 'blowfish', + 'ils_encryption_key' => 'bar', + ] + ] + ); + $commandTester = new CommandTester($command); + $commandTester->execute(['newmethod' => 'blowfish', 'newkey' => 'bar']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $this->assertEquals( + "No changes requested -- no action needed.\n", + $commandTester->getDisplay() + ); + } + + /** + * Test failed configurate write. + * + * @return void + */ + public function testFailedConfigWrite() + { + $writer = $this->getMockConfigWriter(); + $writer->expects($this->once())->method('save') + ->will($this->returnValue(false)); + $command = $this->getMockCommand(); + $command->expects($this->once())->method('getConfigWriter') + ->will($this->returnValue($writer)); + $commandTester = new CommandTester($command); + $commandTester->execute(['newmethod' => 'blowfish', 'newkey' => 'foo']); + $this->assertEquals(1, $commandTester->getStatusCode()); + $expectedConfig = Locator::getLocalConfigPath('config.ini', null, true); + $this->assertEquals( + "\tUpdating $expectedConfig...\n\tWrite failed!\n", + $commandTester->getDisplay() + ); + } + + /** + * Test success with no users to update. + * + * @return void + */ + public function testSuccessNoUsers() + { + $writer = $this->getMockConfigWriter(); + $writer->expects($this->at(0))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('encrypt_ils_password'), + $this->equalTo(true) + ); + $writer->expects($this->at(1))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('ils_encryption_algo'), + $this->equalTo('blowfish') + ); + $writer->expects($this->at(2))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('ils_encryption_key'), + $this->equalTo('foo') + ); + $writer->expects($this->once())->method('save') + ->will($this->returnValue(true)); + $table = $this->getMockTable(); + $table->expects($this->once())->method('select') + ->will($this->returnValue([])); + $command = $this->getMockCommand([], $table); + $command->expects($this->once())->method('getConfigWriter') + ->will($this->returnValue($writer)); + $commandTester = new CommandTester($command); + $commandTester->execute(['newmethod' => 'blowfish', 'newkey' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $expectedConfig = Locator::getLocalConfigPath('config.ini', null, true); + $this->assertEquals( + "\tUpdating $expectedConfig...\n\tConverting hashes for 0 user(s).\n" + . "\tFinished.\n", + $commandTester->getDisplay() + ); + } + + /** + * Get a mock row representing a user. + * + * @return \VuFind\Db\Row\Search + */ + protected function getMockUserObject() + { + $data = [ + 'id' => 2, + 'username' => 'foo', + 'email' => 'fake@myuniversity.edu', + 'created' => '2000-01-01 00:00:00', + 'cat_password' => 'mypassword', + 'last_language' => 'en', + ]; + $adapter = $this->prepareMock(\Laminas\Db\Adapter\Adapter::class); + $user = $this->getMockBuilder(\VuFind\Db\Row\User::class) + ->setConstructorArgs([$adapter]) + ->setMethods(['save']) + ->getMock(); + $user->populate($data, true); + return $user; + } + + /** + * Decode a hash to confirm that it was encoded correctly. + */ + protected function decode($hash) + { + $cipher = new BlockCipher(new Openssl(['algorithm' => 'blowfish'])); + $cipher->setKey('foo'); + return $cipher->decrypt($hash); + } + + /** + * Test success with a user to update. + * + * @return void + */ + public function testSuccessWithUser() + { + $writer = $this->getMockConfigWriter(); + $writer->expects($this->at(0))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('encrypt_ils_password'), + $this->equalTo(true) + ); + $writer->expects($this->at(1))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('ils_encryption_algo'), + $this->equalTo('blowfish') + ); + $writer->expects($this->at(2))->method('set') + ->with( + $this->equalTo('Authentication'), + $this->equalTo('ils_encryption_key'), + $this->equalTo('foo') + ); + $writer->expects($this->once())->method('save') + ->will($this->returnValue(true)); + $user = $this->getMockUserObject(); + $user->expects($this->once())->method('save'); + $table = $this->getMockTable(); + $table->expects($this->once())->method('select') + ->will($this->returnValue([$user])); + $command = $this->getMockCommand([], $table); + $command->expects($this->once())->method('getConfigWriter') + ->will($this->returnValue($writer)); + $commandTester = new CommandTester($command); + $commandTester->execute(['newmethod' => 'blowfish', 'newkey' => 'foo']); + $this->assertEquals(0, $commandTester->getStatusCode()); + $expectedConfig = Locator::getLocalConfigPath('config.ini', null, true); + $this->assertEquals( + "\tUpdating $expectedConfig...\n\tConverting hashes for 1 user(s).\n" + . "\tFinished.\n", + $commandTester->getDisplay() + ); + $this->assertEquals(null, $user['cat_password']); + $this->assertEquals('mypassword', $this->decode($user['cat_pass_enc'])); + } +} diff --git a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php b/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php deleted file mode 100644 index 171e28bb07980df7bdb9f0fa2fe3d62c6b3926c4..0000000000000000000000000000000000000000 --- a/module/VuFindConsole/tests/unit-tests/src/VuFindTest/Route/RouteGeneratorTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** - * Route generator tests. - * - * 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 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 VuFindConsoleTest\Route; - -use VuFindConsole\Route\RouteGenerator; - -/** - * Route generator 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 CacheTest extends \PHPUnit\Framework\TestCase -{ - /** - * Test route generation - * - * @return void - */ - public function testGeneration() - { - $config = []; - $routes = [ - 'controller1/action1' => 'controller1 action1', - 'controller2/action2' => 'controller2 action2', - ]; - $generator = new RouteGenerator(); - $generator->addRoutes($config, $routes); - $expected = [ - 'console' => [ - 'router' => [ - 'routes' => [ - 'controller1-action1' => [ - 'options' => [ - 'route' => 'controller1 action1', - 'defaults' => [ - 'controller' => 'controller1', - 'action' => 'action1', - ], - ], - ], - 'controller2-action2' => [ - 'options' => [ - 'route' => 'controller2 action2', - 'defaults' => [ - 'controller' => 'controller2', - 'action' => 'action2', - ], - ], - ], - ], - ], - ], - ]; - $this->assertEquals($expected, $config); - } -} diff --git a/module/VuFindTheme/src/VuFindTheme/GeneratorInterface.php b/module/VuFindTheme/src/VuFindTheme/GeneratorInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..488a188f29aef36c0e6d0722cfd8400fb87b5763 --- /dev/null +++ b/module/VuFindTheme/src/VuFindTheme/GeneratorInterface.php @@ -0,0 +1,67 @@ +<?php +/** + * Interface shared by theme and mixin generator classes. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2017. + * + * 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 Symfony\Component\Console\Output\OutputInterface; + +/** + * Interface shared by theme and mixin generator classes. + * + * @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 + */ +interface GeneratorInterface +{ + /** + * Generate a new resource. + * + * @param string $name Name of resource to generate. + * + * @return bool + */ + public function generate($name); + + /** + * Get last error message. + * + * @return string + */ + public function getLastError(); + + /** + * Set the output interface. + * + * @param OutputInterface $output Output interface + * + * @return void + */ + public function setOutputInterface(OutputInterface $output): void; +} diff --git a/module/VuFindTheme/src/VuFindTheme/Initializer.php b/module/VuFindTheme/src/VuFindTheme/Initializer.php index 6e8d97eb5778429f1f6e08f47ceaa79984a77710..fa5f4f2c727796132b007ff54339f723f0653436 100644 --- a/module/VuFindTheme/src/VuFindTheme/Initializer.php +++ b/module/VuFindTheme/src/VuFindTheme/Initializer.php @@ -29,7 +29,6 @@ namespace VuFindTheme; use Interop\Container\ContainerInterface; use Laminas\Config\Config; -use Laminas\Console\Console; use Laminas\Mvc\MvcEvent; use Laminas\Stdlib\RequestInterface as Request; use Laminas\View\Resolver\TemplatePathStack; @@ -201,7 +200,7 @@ class Initializer { // Load standard configuration options: $standardTheme = $this->config->theme; - if (Console::isConsole()) { + if (PHP_SAPI == 'cli') { return $standardTheme; } $mobileTheme = $this->mobile->enabled() @@ -259,7 +258,7 @@ class Initializer protected function sendThemeOptionsToView() { // Get access to the view model: - if (!Console::isConsole()) { + if (PHP_SAPI !== 'cli') { $viewModel = $this->serviceManager->get('ViewManager')->getViewModel(); // Send down the view options: diff --git a/module/VuFindTheme/src/VuFindTheme/LessCompiler.php b/module/VuFindTheme/src/VuFindTheme/LessCompiler.php index b931262354bfbc1f86d62e840b3096aaaed25a68..d6bccff42af748de6326ed029c058d8d0ef5b1f0 100644 --- a/module/VuFindTheme/src/VuFindTheme/LessCompiler.php +++ b/module/VuFindTheme/src/VuFindTheme/LessCompiler.php @@ -27,7 +27,7 @@ */ namespace VuFindTheme; -use Laminas\Console\Console; +use Symfony\Component\Console\Output\OutputInterface; /** * Class to compile LESS into CSS within a theme. @@ -62,22 +62,22 @@ class LessCompiler protected $fakePath = '/zzzz_basepath_zzzz/'; /** - * Console log? + * Output object (set for logging) * - * @var bool + * @var OutputInterface */ - protected $verbose; + protected $output; /** * Constructor * - * @param bool $verbose Display messages while compiling? + * @param OutputInterface $output Output interface for logging (optional) */ - public function __construct($verbose = false) + public function __construct(OutputInterface $output = null) { $this->basePath = realpath(__DIR__ . '/../../../../'); $this->tempPath = sys_get_temp_dir(); - $this->verbose = $verbose; + $this->output = $output; } /** @@ -270,8 +270,8 @@ class LessCompiler */ protected function logMessage($str) { - if ($this->verbose) { - Console::writeLine($str); + if ($this->output) { + $this->output->writeln($str); } } } diff --git a/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php b/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php index 2d2eb89ef492815459d5dd357f806ece4ea643e1..9a278a596ed8e8b57b4e8af8a56f56b5e64571a2 100644 --- a/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php +++ b/module/VuFindTheme/src/VuFindTheme/MixinGenerator.php @@ -28,8 +28,6 @@ */ namespace VuFindTheme; -use Laminas\Console\Console; - /** * Class to generate a new mixin from a template. * @@ -40,8 +38,10 @@ use Laminas\Console\Console; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ -class MixinGenerator extends AbstractThemeUtility +class MixinGenerator extends AbstractThemeUtility implements GeneratorInterface { + use \VuFindConsole\ConsoleOutputTrait; + /** * Generate a new mixin from a template. * @@ -57,12 +57,12 @@ class MixinGenerator extends AbstractThemeUtility if (realpath($baseDir . $name)) { return $this->setLastError('Mixin "' . $name . '" already exists'); } - Console::writeLine('Creating new mixin: "' . $name . '"'); + $this->writeln('Creating new mixin: "' . $name . '"'); $source = $baseDir . $template; $dest = $baseDir . $name; - Console::writeLine("\tCopying $template"); - Console::writeLine("\t\tFrom: " . $source); - Console::writeLine("\t\tTo: " . $dest); + $this->writeln("\tCopying $template"); + $this->writeln("\t\tFrom: " . $source); + $this->writeln("\t\tTo: " . $dest); return $this->copyDir($source, $dest); } } diff --git a/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php b/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php index 20d744ab6d10c6e8ac1ce05c23213d6be1454f7e..68ae464ea7c7015801ab487e67c43b5fc5974e09 100644 --- a/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php +++ b/module/VuFindTheme/src/VuFindTheme/ThemeGenerator.php @@ -29,7 +29,6 @@ namespace VuFindTheme; use Laminas\Config\Config; -use Laminas\Console\Console; use VuFind\Config\Locator as ConfigLocator; use VuFind\Config\Writer as ConfigWriter; @@ -43,8 +42,10 @@ use VuFind\Config\Writer as ConfigWriter; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Site */ -class ThemeGenerator extends AbstractThemeUtility +class ThemeGenerator extends AbstractThemeUtility implements GeneratorInterface { + use \VuFindConsole\ConsoleOutputTrait; + /** * Generate a new theme from a template. * @@ -60,12 +61,12 @@ class ThemeGenerator extends AbstractThemeUtility if (realpath($baseDir . $name)) { return $this->setLastError('Theme "' . $name . '" already exists'); } - Console::writeLine('Creating new theme: "' . $name . '"'); + $this->writeln('Creating new theme: "' . $name . '"'); $source = $baseDir . $themeTemplate; $dest = $baseDir . $name; - Console::writeLine("\tCopying $themeTemplate"); - Console::writeLine("\t\tFrom: " . $source); - Console::writeLine("\t\tTo: " . $dest); + $this->writeln("\tCopying $themeTemplate"); + $this->writeln("\t\tFrom: " . $source); + $this->writeln("\t\tTo: " . $dest); return $this->copyDir($source, $dest); } @@ -86,8 +87,8 @@ class ThemeGenerator extends AbstractThemeUtility return $this ->setLastError("Expected configuration file missing: $configPath"); } - Console::writeLine("\tUpdating $configPath..."); - Console::writeLine("\t\t[Site] > theme = $name"); + $this->writeln("\tUpdating $configPath..."); + $this->writeln("\t\t[Site] > theme = $name"); $writer = new ConfigWriter($configPath); $writer->set('Site', 'theme', $name); // Enable dropdown @@ -96,7 +97,7 @@ class ThemeGenerator extends AbstractThemeUtility 'custom' => strtolower(str_replace(' ', '', $name)) ]; // - Set alternate_themes - Console::writeLine("\t\t[Site] > alternate_themes"); + $this->writeln("\t\t[Site] > alternate_themes"); $altSetting = []; if (isset($config->Site->alternate_themes)) { $alts = explode(',', $config->Site->alternate_themes); @@ -115,7 +116,7 @@ class ThemeGenerator extends AbstractThemeUtility $altSetting[] = $settingPrefixes['custom'] . ':' . $name; $writer->set('Site', 'alternate_themes', implode(',', $altSetting)); // - Set selectable_themes - Console::writeLine("\t\t[Site] > selectable_themes"); + $this->writeln("\t\t[Site] > selectable_themes"); $dropSetting = [ $settingPrefixes['bootstrap'] . ':Bootstrap', $settingPrefixes['custom'] . ':' . ucwords($name) diff --git a/public/index.php b/public/index.php index 125fe7220b5dc1ae77f5aee3cab0ccb847ac433f..ddae01413d8b955b390047d548cfa3a5ef9eabbd 100644 --- a/public/index.php +++ b/public/index.php @@ -80,4 +80,10 @@ if (!class_exists('Laminas\Loader\AutoloaderFactory')) { } // Run the application! -Laminas\Mvc\Application::init(require 'config/application.config.php')->run(); +$app = Laminas\Mvc\Application::init(require 'config/application.config.php'); +if (PHP_SAPI === 'cli') { + return $app->getServiceManager() + ->get(\VuFindConsole\ConsoleRunner::class)->run(); +} else { + $app->run(); +} diff --git a/util/dedupe.php b/util/dedupe.php index d5cc104cca3d7e4fe3281188eb1bbd68ba6de88d..a899aa19863dfaec5497d0c26637878bc242463d 100644 --- a/util/dedupe.php +++ b/util/dedupe.php @@ -2,7 +2,7 @@ /** * Remove duplicate lines from a file -- needed for the Windows version of * the alphabetical browse database generator, since Windows sort does not - * support deduplication. Assumed presorted + * support deduplication. Assumed presorted. * * PHP version 7 * @@ -27,31 +27,8 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/indexing:alphabetical_heading_browse Wiki */ -if(count($argv) < 2 || $argv[1] == "") { - echo "\nPlease specify an input file: "; - $argv[1] = chop(fgets(STDIN)); // Read the input -} -$in = fopen($argv[1], 'r'); -if (!$in) { - die('Could not open input file: '.$argv[1]."\n"); -} -if(count($argv) < 3 || $argv[2] == "") { - echo "\nPlease specify an output file: "; - $argv[2] = chop(fgets(STDIN)); // Read the input -} -$out = fopen($argv[2], 'w'); -if (!$out) { - die('Could not open output file: '.$argv[2]."\n"); -} - -$last = ''; -while ($tmp = fgets($in)) { - if ($tmp != $last) { - fputs($out, $tmp); - } - $last = $tmp; -} - -fclose($in); -fclose($out); \ No newline at end of file +// Manipulate command line to load correct route, then run the main index page: +array_unshift($_SERVER['argv'], array_shift($_SERVER['argv']), 'util', 'dedupe'); +$_SERVER['argc'] += 2; +require_once __DIR__ . '/../public/index.php';