diff --git a/circle.yml b/circle.yml index 8d48c3e9fa..213f3d8cc2 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,7 @@ general: machine: php: - version: 5.5.21 + version: 5.5.31 node: version: stable services: @@ -22,12 +22,17 @@ dependencies: - node_modules - ~/.composer pre: - - sudo apt-get install librabbitmq-dev - - pecl install amqp-1.4.0 + - git clone https://github.com/alanxz/rabbitmq-c + - cd rabbitmq-c && git submodule init && git submodule update && autoreconf -i && ./configure && make && sudo make install + - pecl install amqp-1.6.0 - yes '' | pecl install imagick - pecl install json + - sudo apt-get install libzmq-dev - yes '' | pecl install zmq-beta - - sed -i 's/^\(session.cache_limiter = \).*/\1""/' ~/.phpenv/versions/$(phpenv global)/etc/php.ini + - echo "extension = amqp.so" > /opt/circleci/php/$(phpenv global)/etc/conf.d/amqp.ini + - echo "extension = zmq.so" > /opt/circleci/php/$(phpenv global)/etc/conf.d/zmq.ini + - echo "date.timezone = UTC" > /opt/circleci/php/$(phpenv global)/etc/conf.d/timezone.ini + - sed -i 's/^\(session.cache_limiter = \).*/\1""/' /opt/circleci/php/$(phpenv global)/etc/php.ini - npm rebuild node-sass override: - composer install --no-progress --no-interaction --optimize-autoloader diff --git a/composer.json b/composer.json index 7e2f9af70a..acefb375c0 100644 --- a/composer.json +++ b/composer.json @@ -96,7 +96,7 @@ "simple-bus/serialization": "^2.0", "sorien/silex-dbal-profiler": "^1.1", "sorien/silex-pimple-dumper": "^1.0", - "swiftmailer/swiftmailer": "~5.3.0", + "swiftmailer/swiftmailer": "~5.4.5", "symfony/symfony": "~2.7.10|~2.8.3", "themattharris/tmhoauth": "~0.7", "twig/extensions": "^1.2.0", diff --git a/composer.lock b/composer.lock index 66ce34e2a9..a3b3262915 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "9a1955fa0102a16b25ee47148a6537a0", - "content-hash": "b19b6544330d38b28cc31554738bded3", + "hash": "bac58111d3666a1239ce66f4bfda46ff", + "content-hash": "03bc42658d3cfef4ce8fd202719e0713", "packages": [ { "name": "alchemy-fr/tcpdf-clone", @@ -163,7 +163,11 @@ "Alchemy\\EmbedProvider\\": "src/Provider" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "Alchemy\\EmbedBundle\\Tests\\": "tests/unit/Bundle" + } + }, "license": [ "MIT" ], @@ -174,6 +178,10 @@ } ], "description": "Embed resources bundle", + "support": { + "source": "https://github.com/alchemy-fr/embed-bundle/tree/v0.4.4", + "issues": "https://github.com/alchemy-fr/embed-bundle/issues" + }, "time": "2016-07-07 10:02:43" }, { @@ -963,16 +971,16 @@ }, { "name": "beberlei/assert", - "version": "v2.6.8", + "version": "v2.7.3", "source": { "type": "git", "url": "https://github.com/beberlei/assert.git", - "reference": "848c8f0bde97b48d1e159075e20a6667583f3978" + "reference": "5972776d6a9eedfd3c55216341434e19cb50418f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/beberlei/assert/zipball/848c8f0bde97b48d1e159075e20a6667583f3978", - "reference": "848c8f0bde97b48d1e159075e20a6667583f3978", + "url": "https://api.github.com/repos/beberlei/assert/zipball/5972776d6a9eedfd3c55216341434e19cb50418f", + "reference": "5972776d6a9eedfd3c55216341434e19cb50418f", "shasum": "" }, "require": { @@ -1014,7 +1022,7 @@ "assertion", "validation" ], - "time": "2016-12-05 11:33:17" + "time": "2017-01-24 15:14:39" }, { "name": "behat/transliterator", @@ -1539,20 +1547,20 @@ }, { "name": "doctrine/dbal", - "version": "v2.5.5", + "version": "v2.5.10", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9" + "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", - "reference": "9f8c05cd5225a320d56d4bfdb4772f10d045a0c9", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/fc376f7a61498e18520cd6fa083752a4ca08072b", + "reference": "fc376f7a61498e18520cd6fa083752a4ca08072b", "shasum": "" }, "require": { - "doctrine/common": ">=2.4,<2.7-dev", + "doctrine/common": ">=2.4,<2.8-dev", "php": ">=5.3.2" }, "require-dev": { @@ -1606,7 +1614,7 @@ "persistence", "queryobject" ], - "time": "2016-09-09 19:13:33" + "time": "2017-01-23 23:17:10" }, { "name": "doctrine/inflector", @@ -1785,16 +1793,16 @@ }, { "name": "doctrine/migrations", - "version": "1.4.1", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05" + "reference": "c81147c0f2938a6566594455367e095150547f72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/0d0ff5da10c5d30846da32060bd9e357abf70a05", - "reference": "0d0ff5da10c5d30846da32060bd9e357abf70a05", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/c81147c0f2938a6566594455367e095150547f72", + "reference": "c81147c0f2938a6566594455367e095150547f72", "shasum": "" }, "require": { @@ -1809,9 +1817,10 @@ "doctrine/orm": "2.*", "jdorn/sql-formatter": "~1.1", "johnkary/phpunit-speedtrap": "~1.0@dev", + "mikey179/vfsstream": "^1.6", "mockery/mockery": "^0.9.4", "phpunit/phpunit": "~4.7", - "satooshi/php-coveralls": "0.6.*" + "satooshi/php-coveralls": "^1.0" }, "suggest": { "jdorn/sql-formatter": "Allows to generate formatted SQL with the diff command." @@ -1822,7 +1831,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "v1.5.x-dev" + "dev-master": "v1.6.x-dev" } }, "autoload": { @@ -1854,26 +1863,26 @@ "database", "migrations" ], - "time": "2016-03-14 12:29:11" + "time": "2016-12-25 22:54:00" }, { "name": "doctrine/orm", - "version": "v2.5.5", + "version": "v2.5.6", "source": { "type": "git", "url": "https://github.com/doctrine/doctrine2.git", - "reference": "73e4be7c7b3ba26f96b781a40b33feba4dfa6d45" + "reference": "e6c434196c8ef058239aaa0724b4aadb0107940b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/73e4be7c7b3ba26f96b781a40b33feba4dfa6d45", - "reference": "73e4be7c7b3ba26f96b781a40b33feba4dfa6d45", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/e6c434196c8ef058239aaa0724b4aadb0107940b", + "reference": "e6c434196c8ef058239aaa0724b4aadb0107940b", "shasum": "" }, "require": { "doctrine/cache": "~1.4", "doctrine/collections": "~1.2", - "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/common": ">=2.5-dev,<2.8-dev", "doctrine/dbal": ">=2.5-dev,<2.6-dev", "doctrine/instantiator": "~1.0.1", "ext-pdo": "*", @@ -1930,7 +1939,7 @@ "database", "orm" ], - "time": "2016-09-10 18:51:13" + "time": "2016-12-18 15:42:34" }, { "name": "elasticsearch/elasticsearch", @@ -2070,7 +2079,7 @@ "facebook", "sdk" ], - "abandoned": "facebook/php-sdk-v4", + "abandoned": "facebook/graph-sdk", "time": "2013-11-19 23:11:14" }, { @@ -3727,16 +3736,16 @@ }, { "name": "league/flysystem", - "version": "1.0.32", + "version": "1.0.34", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab" + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/1b5c4a0031697f46e779a9d1b309c2e1b24daeab", - "reference": "1b5c4a0031697f46e779a9d1b309c2e1b24daeab", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299", + "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299", "shasum": "" }, "require": { @@ -3806,7 +3815,7 @@ "sftp", "storage" ], - "time": "2016-10-19 20:38:46" + "time": "2017-01-30 17:41:17" }, { "name": "league/flysystem-aws-s3-v2", @@ -4588,6 +4597,76 @@ ], "time": "2016-11-07 23:38:38" }, + { + "name": "php-amqplib/php-amqplib", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/php-amqplib/php-amqplib.git", + "reference": "fa2f0d4410a11008cb36b379177291be7ee9e4f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/fa2f0d4410a11008cb36b379177291be7ee9e4f6", + "reference": "fa2f0d4410a11008cb36b379177291be7ee9e4f6", + "shasum": "" + }, + "require": { + "ext-bcmath": "*", + "ext-mbstring": "*", + "php": ">=5.3.0" + }, + "replace": { + "videlalvaro/php-amqplib": "self.version" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "scrutinizer/ocular": "^1.1", + "squizlabs/php_codesniffer": "^2.5" + }, + "suggest": { + "ext-sockets": "Use AMQPSocketConnection" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "PhpAmqpLib\\": "PhpAmqpLib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Alvaro Videla", + "role": "Original Maintainer" + }, + { + "name": "John Kelly", + "email": "johnmkelly86@gmail.com", + "role": "Maintainer" + }, + { + "name": "Raúl Araya", + "email": "nubeiro@gmail.com", + "role": "Maintainer" + } + ], + "description": "Formerly videlalvaro/php-amqplib. This library is a pure PHP implementation of the AMQP protocol. It's been tested against RabbitMQ.", + "homepage": "https://github.com/php-amqplib/php-amqplib/", + "keywords": [ + "message", + "queue", + "rabbitmq" + ], + "time": "2016-04-11 14:30:01" + }, { "name": "php-ffmpeg/php-ffmpeg", "version": "0.5.1", @@ -5118,27 +5197,22 @@ }, { "name": "react/promise", - "version": "v2.4.1", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "8025426794f1944de806618671d4fa476dc7626f" + "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8025426794f1944de806618671d4fa476dc7626f", - "reference": "8025426794f1944de806618671d4fa476dc7626f", + "url": "https://api.github.com/repos/reactphp/promise/zipball/2760f3898b7e931aa71153852dcd48a75c9b95db", + "reference": "2760f3898b7e931aa71153852dcd48a75c9b95db", "shasum": "" }, "require": { "php": ">=5.4.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, "autoload": { "psr-4": { "React\\Promise\\": "src/" @@ -5158,7 +5232,11 @@ } ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2016-05-03 17:50:52" + "keywords": [ + "promise", + "promises" + ], + "time": "2016-12-22 14:09:01" }, { "name": "roave/security-advisories", @@ -5166,12 +5244,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "3c25700b65ba5e34a5ec8fd05b5cdc7a68d9385a" + "reference": "3db4b0df21d1f527304650e717c66af48981f1c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/3c25700b65ba5e34a5ec8fd05b5cdc7a68d9385a", - "reference": "3c25700b65ba5e34a5ec8fd05b5cdc7a68d9385a", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/3db4b0df21d1f527304650e717c66af48981f1c4", + "reference": "3db4b0df21d1f527304650e717c66af48981f1c4", "shasum": "" }, "conflict": { @@ -5212,18 +5290,19 @@ "namshi/jose": "<2.2", "oro/crm": ">=1.7,<1.7.4", "oro/platform": ">=1.7,<1.7.4", - "phpmailer/phpmailer": ">=5,<5.2.14", + "phpmailer/phpmailer": ">=5,<5.2.22", "pusher/pusher-php-server": "<2.2.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", - "shopware/shopware": "<4.3.7|>=5,<5.1.5", + "shopware/shopware": "<4.4|>=5,<5.2.15", "silverstripe/cms": ">=3.1,<3.1.11|>=3,<=3.0.11", "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3", "silverstripe/framework": ">=3,<3.3", "silverstripe/userforms": "<3", - "simplesamlphp/saml2": ">=1.9,<1.9.1|>=1.10,<1.10.3|>=2.3,<2.3.3", - "simplesamlphp/simplesamlphp": "<1.14.4", + "simplesamlphp/saml2": "<1.8.1|>=1.9,<1.9.1|>=1.10,<1.10.3|>=2,<2.3.3", + "simplesamlphp/simplesamlphp": "<1.14.11", + "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1", "socalnick/scn-social-auth": "<1.15.2", - "swiftmailer/swiftmailer": ">=4,<4.99.99|>=5,<5.2.1", + "swiftmailer/swiftmailer": ">=4,<5.4.5", "symfony/dependency-injection": ">=2,<2.0.17", "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.7", "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2", @@ -5242,7 +5321,7 @@ "thelia/backoffice-default-template": ">=2.1,<2.1.2", "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2", "twig/twig": "<1.20", - "typo3/cms": ">=6.2,<6.2.29|>=8,<8.4.1|>=7,<7.6.13", + "typo3/cms": ">=6.2,<6.2.30|>=8,<8.4.1|>=7,<7.6.13", "typo3/flow": ">=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5|>=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1", "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4", "willdurand/js-translation-bundle": "<2.1.1", @@ -5261,13 +5340,13 @@ "zendframework/zend-http": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6", "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3", - "zendframework/zend-mail": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.3,<2.3.8|>=2.4,<2.4.1", + "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2", "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1", "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4", "zendframework/zend-validator": ">=2.3,<2.3.6", "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1", "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6", - "zendframework/zendframework": ">=2,<2.4.9|>=2.5,<2.5.1", + "zendframework/zendframework": ">=2,<2.4.11|>=2.5,<2.5.1", "zendframework/zendframework1": "<1.12.20", "zendframework/zendopenid": ">=2,<2.0.2", "zendframework/zendxml": ">=1,<1.0.1", @@ -5288,7 +5367,7 @@ } ], "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it", - "time": "2016-12-04 13:54:33" + "time": "2017-01-24 18:32:04" }, { "name": "seld/jsonlint", @@ -5805,28 +5884,29 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.3.1", + "version": "v5.4.5", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "c5f963e7f9d6f6438fda4f22d5cc2db296ec621a" + "reference": "cd142238a339459b10da3d8234220963f392540c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/c5f963e7f9d6f6438fda4f22d5cc2db296ec621a", - "reference": "c5f963e7f9d6f6438fda4f22d5cc2db296ec621a", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", + "reference": "cd142238a339459b10da3d8234220963f392540c", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "~0.9.1" + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.3-dev" + "dev-master": "5.4-dev" } }, "autoload": { @@ -5850,10 +5930,11 @@ "description": "Swiftmailer, free feature-rich PHP mailer", "homepage": "http://swiftmailer.org", "keywords": [ + "email", "mail", "mailer" ], - "time": "2014-12-05 14:17:14" + "time": "2016-12-29 10:02:40" }, { "name": "symfony/polyfill-apcu", @@ -6369,16 +6450,16 @@ }, { "name": "symfony/symfony", - "version": "v2.8.14", + "version": "v2.8.16", "source": { "type": "git", "url": "https://github.com/symfony/symfony.git", - "reference": "6ceca5b4154c80839270c38aa65373de76127df7" + "reference": "9fef72a3ab561c4bfa703a70369db028dec387d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/6ceca5b4154c80839270c38aa65373de76127df7", - "reference": "6ceca5b4154c80839270c38aa65373de76127df7", + "url": "https://api.github.com/repos/symfony/symfony/zipball/9fef72a3ab561c4bfa703a70369db028dec387d2", + "reference": "9fef72a3ab561c4bfa703a70369db028dec387d2", "shasum": "" }, "require": { @@ -6500,7 +6581,7 @@ "keywords": [ "framework" ], - "time": "2016-11-21 02:24:51" + "time": "2017-01-12 20:27:46" }, { "name": "themattharris/tmhoauth", @@ -6598,16 +6679,16 @@ }, { "name": "twig/twig", - "version": "v1.28.2", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "b22ce0eb070e41f7cba65d78fe216de29726459c" + "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/b22ce0eb070e41f7cba65d78fe216de29726459c", - "reference": "b22ce0eb070e41f7cba65d78fe216de29726459c", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ddc9e3e20ee9c0b6908f401ac8353635b750eca7", + "reference": "ddc9e3e20ee9c0b6908f401ac8353635b750eca7", "shasum": "" }, "require": { @@ -6615,12 +6696,12 @@ }, "require-dev": { "symfony/debug": "~2.7", - "symfony/phpunit-bridge": "~3.2@dev" + "symfony/phpunit-bridge": "~3.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.28-dev" + "dev-master": "1.31-dev" } }, "autoload": { @@ -6655,7 +6736,7 @@ "keywords": [ "templating" ], - "time": "2016-11-23 18:41:40" + "time": "2017-01-11 19:36:15" }, { "name": "vierbergenlars/php-semver", @@ -7551,16 +7632,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.8.30", + "version": "4.8.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a534e04d0bd39c557c2881c341efd06fa6f1292a" + "reference": "7eb45205d27edd94bd2b3614085ea158bd1e2bca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a534e04d0bd39c557c2881c341efd06fa6f1292a", - "reference": "a534e04d0bd39c557c2881c341efd06fa6f1292a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7eb45205d27edd94bd2b3614085ea158bd1e2bca", + "reference": "7eb45205d27edd94bd2b3614085ea158bd1e2bca", "shasum": "" }, "require": { @@ -7619,7 +7700,7 @@ "testing", "xunit" ], - "time": "2016-12-01 17:05:48" + "time": "2017-01-26 16:15:36" }, { "name": "phpunit/phpunit-mock-objects", @@ -7679,16 +7760,16 @@ }, { "name": "sebastian/comparator", - "version": "1.2.2", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f" + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a1ed12e8b2409076ab22e3897126211ff8b1f7f", - "reference": "6a1ed12e8b2409076ab22e3897126211ff8b1f7f", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", "shasum": "" }, "require": { @@ -7739,7 +7820,7 @@ "compare", "equality" ], - "time": "2016-11-19 09:18:40" + "time": "2017-01-29 09:50:25" }, { "name": "sebastian/diff", diff --git a/lib/Alchemy/Phrasea/Controller/Api/SearchController.php b/lib/Alchemy/Phrasea/Controller/Api/SearchController.php index 922b910959..37f698ca7d 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/SearchController.php +++ b/lib/Alchemy/Phrasea/Controller/Api/SearchController.php @@ -10,6 +10,7 @@ namespace Alchemy\Phrasea\Controller\Api; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Fractal\ArraySerializer; use Alchemy\Phrasea\Model\Manipulator\UserManipulator; @@ -60,15 +61,12 @@ class SearchController extends Controller $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $result->getUserQuery()); - foreach ($options->getDataboxes() as $databox) { - $colls = array_map(function (\collection $collection) { - return $collection->get_coll_id(); - }, array_filter($options->getCollections(), function (\collection $collection) use ($databox) { - return $collection->get_databox()->get_sbas_id() == $databox->get_sbas_id(); - })); - - $this->getSearchEngineLogger() - ->log($databox, $result->getUserQuery(), $result->getTotal(), $colls); + // log array of collectionIds (from $options) for each databox + $collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox(); + foreach ($collectionsReferencesByDatabox as $sbid => $references) { + $databox = $this->findDataboxById($sbid); + $collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references); + $this->getSearchEngineLogger()->log($databox, $result->getUserQuery(), $result->getTotal(), $collectionsIds); } $this->getSearchEngine()->clearCache(); diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php index 5cbbc25dd4..7358539cbe 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php @@ -26,6 +26,7 @@ use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Border\Manager; use Alchemy\Phrasea\Border\Visa; use Alchemy\Phrasea\Cache\Cache; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Core\Event\RecordEdit; use Alchemy\Phrasea\Core\PhraseaEvents; @@ -85,7 +86,6 @@ use Alchemy\Phrasea\Status\StatusStructure; use Alchemy\Phrasea\TaskManager\LiveInformation; use Alchemy\Phrasea\Utilities\NullableDateTime; use Doctrine\ORM\EntityManager; - use JMS\TranslationBundle\Annotation\Ignore; use League\Fractal\Resource\Item; use Symfony\Component\Form\Form; @@ -1500,15 +1500,12 @@ class V1Controller extends Controller $this->getUserManipulator()->logQuery($this->getAuthenticatedUser(), $search_result->getUserQuery()); - foreach ($options->getDataboxes() as $databox) { - $colls = array_map(function (\collection $collection) { - return $collection->get_coll_id(); - }, array_filter($options->getCollections(), function (\collection $collection) use ($databox) { - return $collection->get_databox()->get_sbas_id() == $databox->get_sbas_id(); - })); - - $this->getSearchEngineLogger() - ->log($databox, $search_result->getUserQuery(), $search_result->getTotal(), $colls); + // log array of collectionIds (from $options) for each databox + $collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox(); + foreach ($collectionsReferencesByDatabox as $sbid => $references) { + $databox = $this->findDataboxById($sbid); + $collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references); + $this->getSearchEngineLogger()->log($databox, $search_result->getUserQuery(), $search_result->getTotal(), $collectionsIds); } $this->getSearchEngine()->clearCache(); diff --git a/lib/Alchemy/Phrasea/Controller/LazyLocator.php b/lib/Alchemy/Phrasea/Controller/LazyLocator.php deleted file mode 100644 index 92a96f9cb7..0000000000 --- a/lib/Alchemy/Phrasea/Controller/LazyLocator.php +++ /dev/null @@ -1,20 +0,0 @@ -request->get('fake_qry'); + $selStart = (int) $request->request->get('_selectionStart'); + $selEnd = (int) $request->request->get('_selectionEnd'); + + // move the selection back to find the begining of the "word" + for(;;) { + $c = ''; + if($selStart>0) { + $c = mb_substr($query, $selStart-1, 1); + } + if(in_array($c, ['', ' ', '"'])) { + break; + } + $selStart--; + } + + // move the selection up to find the end of the "word" + for(;;) { + $c = mb_substr($query, $selEnd, 1); + if(in_array($c, ['', ' ', '"'])) { + break; + } + $selEnd++; + } + $before = mb_substr($query, 0, $selStart); + $word = mb_substr($query, $selStart, $selEnd-$selStart); + $after = mb_substr($query, $selEnd); + + // since the query comes from a submited form, normalize crlf,cr,lf ... + $word = StringHelper::crlfNormalize($word); + $options = SearchEngineOptions::fromRequest($this->app, $request); + + $search_engine_structure = GlobalStructure::createFromDataboxes( + $this->app->getDataboxes(), + Structure::WITH_EVERYTHING & ~(Structure::STRUCTURE_WITH_FLAGS | Structure::FIELD_WITH_FACETS | Structure::FIELD_WITH_THESAURUS) + ); + + $query_context_factory = new QueryContextFactory( + $search_engine_structure, + array_keys($this->app['locales.available']), + $this->app['locale'] + ); + + $engine = new ElasticSearchEngine( + $this->app, + $search_engine_structure, + $this->app['elasticsearch.client'], + $query_context_factory, + $this->app['elasticsearch.facets_response.factory'], + $this->app['elasticsearch.options'] + ); + + $autocomplete = $engine->autocomplete($word, $options); + + $completions = []; + foreach($autocomplete['text'] as $text) { + $completions[] = [ + 'label' => $text, + 'value' => [ + 'before' => $before, + 'word' => $word, + 'after' => $after, + 'completion' => $text, + 'completed' => $before . $text . $after + ] + ]; + } + foreach($autocomplete['byField'] as $fieldName=>$values) { + foreach($values as $value) { + $completions[] = [ + 'label' => $value['query'], + 'value' => [ + 'before' => $before, + 'word' => $word, + 'after' => $after, + 'completion' => $value['query'], + 'completed' => $before . $value['query'] . $after + ] + ]; + } + } + + return $this->app->json($completions); + } + /** * Query Phraseanet to fetch records * @@ -68,14 +162,12 @@ class QueryController extends Controller $userManipulator->setUserSetting($user, 'start_page_query', $query); } - foreach ($options->getDataboxes() as $databox) { - $collections = array_map(function (\collection $collection) { - return $collection->get_coll_id(); - }, array_filter($options->getCollections(), function (\collection $collection) use ($databox) { - return $collection->get_databox()->get_sbas_id() == $databox->get_sbas_id(); - })); - - $this->getSearchEngineLogger()->log($databox, $result->getUserQuery(), $result->getTotal(), $collections); + // log array of collectionIds (from $options) for each databox + $collectionsReferencesByDatabox = $options->getCollectionsReferencesByDatabox(); + foreach ($collectionsReferencesByDatabox as $sbid => $references) { + $databox = $this->findDataboxById($sbid); + $collectionsIds = array_map(function(CollectionReference $ref){return $ref->getCollectionId();}, $references); + $this->getSearchEngineLogger()->log($databox, $result->getUserQuery(), $result->getTotal(), $collectionsIds); } $proposals = $firstPage ? $result->getProposals() : false; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Collection.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Collection.php index c92a16c4e8..2daaf273f4 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Collection.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Collection.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Admin\CollectionController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; use Silex\ControllerProviderInterface; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Dashboard.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Dashboard.php index 6199de60c8..d21e8f8262 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Dashboard.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Dashboard.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Admin\DashboardController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; use Silex\ControllerCollection; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php index 6f7c716422..ff0ad4ecc0 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Databox.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Admin\DataboxController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\Security\Firewall; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Users.php b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Users.php index 39a5afa0aa..4dbccae847 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Admin/Users.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Admin/Users.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Admin; use Alchemy\Phrasea\Controller\Admin\UserController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; use Silex\ControllerProviderInterface; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php index 67eedda1c5..622c429122 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Api; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Api\V1Controller; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\Configuration\PropertyAccess; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php index 5e2c07022e..7ff511332a 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V2.php @@ -13,7 +13,7 @@ use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\Api\BasketController; use Alchemy\Phrasea\Controller\Api\LazaretController; use Alchemy\Phrasea\Controller\Api\SearchController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\Core\Configuration\PropertyAccess; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Datafiles.php b/lib/Alchemy/Phrasea/ControllerProvider/Datafiles.php index 844cdedef7..f90a89692a 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Datafiles.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Datafiles.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\ControllerProvider; use Alchemy\Phrasea\Application as PhraseaApplication; use Alchemy\Phrasea\Controller\DatafileController; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Silex\Application; use Silex\ControllerProviderInterface; use Silex\ServiceProviderInterface; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php index b6339e786d..270e61e6d9 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Permalink.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\PermalinkController; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/DoDownload.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/DoDownload.php index bc71cbfb8a..e776e53152 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/DoDownload.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/DoDownload.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\DoDownloadController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Edit.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Edit.php index 8fb6861088..2051dfa93e 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Edit.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Edit.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\EditController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Export.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Export.php index 6a7d693ca5..d830c12daf 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Export.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Export.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\ExportController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Alchemy\Phrasea\Core\Event\Listener\OAuthListener; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php index 96d454439b..16081d7f14 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Lazaret.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\LazaretController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php index a3a99718bd..48352e1e39 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Push.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\PushController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Query.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Query.php index b632b9abdc..37a9ad181b 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Query.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Query.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\QueryController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; @@ -45,6 +45,8 @@ class Query implements ControllerProviderInterface, ServiceProviderInterface $controllers->post('/', 'controller.prod.query:query') ->bind('prod_query'); + $controllers->post('/completion/', 'controller.prod.query:completion'); + $controllers->post('/answer-train/', 'controller.prod.query:queryAnswerTrain') ->bind('preview_answer_train'); diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Record.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Record.php index e5909eb9ad..9bccb396bb 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Record.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Record.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\RecordController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Story.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Story.php index 9e5648c357..f9a5a9b222 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Story.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Story.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\StoryController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php index de63ae1375..b49e8dda86 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tools.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\ToolsController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tooltip.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tooltip.php index a908214dcb..91f87d88d3 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tooltip.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Tooltip.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\TooltipController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php index 985944957d..a32133050f 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Upload.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\UploadController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/UsrLists.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/UsrLists.php index 982adbe71e..27cfd0c7d0 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/UsrLists.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/UsrLists.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\UsrListController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/WorkZone.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/WorkZone.php index f504db0e48..74a4904931 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/WorkZone.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/WorkZone.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Prod; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Prod\WorkzoneController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Root/Account.php b/lib/Alchemy/Phrasea/ControllerProvider/Root/Account.php index f2cd84aa32..f9ca82127d 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Root/Account.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Root/Account.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Root; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Root\AccountController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Root/Login.php b/lib/Alchemy/Phrasea/ControllerProvider/Root/Login.php index 9bb7bd09ae..14a398f7a1 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Root/Login.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Root/Login.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Root; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Root\LoginController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Root/RSSFeeds.php b/lib/Alchemy/Phrasea/ControllerProvider/Root/RSSFeeds.php index 6a04227e91..d4c4ff79c2 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Root/RSSFeeds.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Root/RSSFeeds.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Root; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Root\RSSFeedController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php b/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php index 7b27538718..b549a2bc03 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Root/Session.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Root; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Controller\Root\SessionController; use Alchemy\Phrasea\ControllerProvider\ControllerProviderTrait; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/AbstractNotificationSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/AbstractNotificationSubscriber.php index 10f6ee5748..fb2c213688 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/AbstractNotificationSubscriber.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/AbstractNotificationSubscriber.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Core\Event\Subscriber; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application\Helper\NotifierAware; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Model\Entities\User; use Symfony\Component\EventDispatcher\EventSubscriberInterface; diff --git a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php index fdeb34849e..c2a03263a3 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/ManipulatorServiceProvider.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Core\Provider; use Alchemy\Phrasea\Application; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Model\Manager\UserManager; use Alchemy\Phrasea\Model\Manipulator\ACLManipulator; use Alchemy\Phrasea\Model\Manipulator\ApiAccountManipulator; diff --git a/lib/Alchemy/Phrasea/Core/Provider/OrderServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/OrderServiceProvider.php index 2a022002f4..281e21658c 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/OrderServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/OrderServiceProvider.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\Event\Subscriber\OrderSubscriber; use Alchemy\Phrasea\Model\Entities\Order; use Alchemy\Phrasea\Order\ValidationNotifier\MailNotifier; diff --git a/lib/Alchemy/Phrasea/Core/Provider/PhraseaEventServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/PhraseaEventServiceProvider.php index 0023940628..76d34c72b7 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/PhraseaEventServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/PhraseaEventServiceProvider.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\Event\Subscriber\ContentNegotiationSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\CookiesDisablerSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber; diff --git a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php index d1d4f10d39..9ecdc0c587 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/SearchEngineServiceProvider.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\SearchEngine\Elastic\DataboxFetcherFactory; use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions; use Alchemy\Phrasea\SearchEngine\Elastic\Index; @@ -76,6 +76,7 @@ class SearchEngineServiceProvider implements ServiceProviderInterface if ($type !== SearchEngineInterface::TYPE_ELASTICSEARCH) { throw new InvalidArgumentException(sprintf('Invalid search engine type "%s".', $type)); } + /** @var ElasticsearchOptions $options */ $options = $app['elasticsearch.options']; @@ -83,7 +84,6 @@ class SearchEngineServiceProvider implements ServiceProviderInterface $app, $app['search_engine.structure'], $app['elasticsearch.client'], - $options->getIndexName(), $app['query_context.factory'], $app['elasticsearch.facets_response.factory'], $options diff --git a/lib/Alchemy/Phrasea/Core/Provider/SerializerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/SerializerServiceProvider.php index 4627175f81..24de903a8b 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/SerializerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/SerializerServiceProvider.php @@ -10,7 +10,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Model\Serializer\CaptionSerializer; use Alchemy\Phrasea\Model\Serializer\ESRecordSerializer; use Silex\Application; diff --git a/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php index da0e5f09a6..b183e88c0d 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/TasksServiceProvider.php @@ -11,7 +11,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\TaskManager\Job\FtpJob; use Alchemy\Phrasea\TaskManager\Job\ArchiveJob; use Alchemy\Phrasea\TaskManager\Job\BridgeJob; diff --git a/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php index 633dc4cc37..ddec79e8a4 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/WebProfilerServiceProvider.php @@ -2,7 +2,7 @@ namespace Alchemy\Phrasea\Core\Provider; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Core\Event\Subscriber\CacheStatisticsSubscriber; use Alchemy\Phrasea\Core\Profiler\CacheDataCollector; use Alchemy\Phrasea\Core\Profiler\TraceableCache; diff --git a/lib/Alchemy/Phrasea/Databox/Caption/CaptionServiceProvider.php b/lib/Alchemy/Phrasea/Databox/Caption/CaptionServiceProvider.php index 3f63153e33..09fd2096e7 100644 --- a/lib/Alchemy/Phrasea/Databox/Caption/CaptionServiceProvider.php +++ b/lib/Alchemy/Phrasea/Databox/Caption/CaptionServiceProvider.php @@ -10,7 +10,7 @@ namespace Alchemy\Phrasea\Databox\Caption; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Databox\ClosureDataboxBoundRepositoryFactory; use Alchemy\Phrasea\Databox\DataboxBoundRepositoryProvider; use Alchemy\Phrasea\Databox\DataboxConnectionProvider; diff --git a/lib/Alchemy/Phrasea/Databox/Field/DataboxFieldFactory.php b/lib/Alchemy/Phrasea/Databox/Field/DataboxFieldFactory.php index 6354b985bb..931609f6ae 100644 --- a/lib/Alchemy/Phrasea/Databox/Field/DataboxFieldFactory.php +++ b/lib/Alchemy/Phrasea/Databox/Field/DataboxFieldFactory.php @@ -27,12 +27,12 @@ class DataboxFieldFactory } /** - * @param array $raw + * @param array $row * @return databox_field */ - public function create(array $raw) + public function create(array $row) { - return new databox_field($this->app, $this->databox, $raw); + return new databox_field($this->app, $this->databox, $row); } /** @@ -43,8 +43,8 @@ class DataboxFieldFactory { $instances = []; - foreach ($rows as $index => $raw) { - $instances[$index] = new databox_field($this->app, $this->databox, $raw); + foreach ($rows as $index => $row) { + $instances[$index] = new databox_field($this->app, $this->databox, $row); } return $instances; diff --git a/lib/Alchemy/Phrasea/Helper/User/Edit.php b/lib/Alchemy/Phrasea/Helper/User/Edit.php index 2fab6b47f4..eea90e776b 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Edit.php +++ b/lib/Alchemy/Phrasea/Helper/User/Edit.php @@ -13,7 +13,7 @@ namespace Alchemy\Phrasea\Helper\User; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application\Helper\NotifierAware; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Exception\InvalidArgumentException; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\Notification\Mail\MailSuccessEmailUpdate; diff --git a/lib/Alchemy/Phrasea/Helper/User/Manage.php b/lib/Alchemy/Phrasea/Helper/User/Manage.php index bea053f56c..588b55695b 100644 --- a/lib/Alchemy/Phrasea/Helper/User/Manage.php +++ b/lib/Alchemy/Phrasea/Helper/User/Manage.php @@ -12,7 +12,7 @@ namespace Alchemy\Phrasea\Helper\User; use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application\Helper\NotifierAware; -use Alchemy\Phrasea\Controller\LazyLocator; +use Alchemy\Phrasea\Core\LazyLocator; use Alchemy\Phrasea\Helper\Helper; use Alchemy\Phrasea\Notification\Receiver; use Alchemy\Phrasea\Notification\Mail\MailRequestPasswordSetup; diff --git a/lib/Alchemy/Phrasea/Model/Entities/User.php b/lib/Alchemy/Phrasea/Model/Entities/User.php index 74b29a484f..d7c44dfde9 100644 --- a/lib/Alchemy/Phrasea/Model/Entities/User.php +++ b/lib/Alchemy/Phrasea/Model/Entities/User.php @@ -289,6 +289,7 @@ class User /** * @param string $login + * @return $this */ public function setLogin($login) { @@ -307,6 +308,7 @@ class User /** * @param string $email + * @return $this */ public function setEmail($email) { @@ -326,6 +328,7 @@ class User /** * * @param string $password + * @return $this */ public function setPassword($password) { @@ -344,6 +347,7 @@ class User /** * @param string $nonce + * @return $this */ public function setNonce($nonce) { @@ -362,6 +366,7 @@ class User /** * @param boolean $saltedPassword + * @return $this */ public function setSaltedPassword($saltedPassword) { @@ -380,6 +385,7 @@ class User /** * @param string $firstName + * @return $this */ public function setFirstName($firstName) { @@ -399,6 +405,7 @@ class User /** * * @param string $lastName + * @return $this */ public function setLastName($lastName) { @@ -452,6 +459,7 @@ class User /** * @param string $address + * @return $this */ public function setAddress($address) { @@ -470,6 +478,7 @@ class User /** * @param string $city + * @return $this */ public function setCity($city) { @@ -488,6 +497,7 @@ class User /** * @param string $country + * @return $this */ public function setCountry($country) { @@ -506,6 +516,7 @@ class User /** * @param string $zipCode + * @return $this */ public function setZipCode($zipCode) { @@ -524,6 +535,7 @@ class User /** * @param integer $geonameId + * @return $this */ public function setGeonameId($geonameId) { @@ -546,6 +558,7 @@ class User /** * @param string $locale + * @return $this * * @throws InvalidArgumentException */ @@ -570,6 +583,7 @@ class User /** * @param string $timezone + * @return $this */ public function setTimezone($timezone) { @@ -588,6 +602,7 @@ class User /** * @param string $job + * @return $this */ public function setJob($job) { @@ -606,6 +621,7 @@ class User /** * @param string $activity + * @return $this */ public function setActivity($activity) { @@ -624,6 +640,7 @@ class User /** * @param string $company + * @return $this */ public function setCompany($company) { @@ -642,6 +659,7 @@ class User /** * @param string $phone + * @return $this */ public function setPhone($phone) { @@ -660,6 +678,7 @@ class User /** * @param string $fax + * @return $this */ public function setFax($fax) { @@ -678,6 +697,7 @@ class User /** * @param boolean $admin + * @return $this */ public function setAdmin($admin) { @@ -696,6 +716,7 @@ class User /** * @param boolean $guest + * @return $this */ public function setGuest($guest) { @@ -714,6 +735,7 @@ class User /** * @param boolean $mailNotifications + * @return $this */ public function setMailNotificationsActivated($mailNotifications) { @@ -732,6 +754,7 @@ class User /** * @param boolean $requestNotifications + * @return $this */ public function setRequestNotificationsActivated($requestNotifications) { @@ -750,6 +773,7 @@ class User /** * @param boolean $ldapCreated + * @return $this */ public function setLdapCreated($ldapCreated) { @@ -768,6 +792,7 @@ class User /** * @param User $owner + * @return $this */ public function setTemplateOwner(User $owner) { @@ -786,6 +811,7 @@ class User /** * @param User $lastAppliedTemplate + * @return $this */ public function setLastAppliedTemplate(User $lastAppliedTemplate) { @@ -804,6 +830,7 @@ class User /** * @param string $pushList + * @return $this */ public function setPushList($pushList) { @@ -822,6 +849,7 @@ class User /** * @param boolean $canChangeProfil + * @return $this */ public function setCanChangeProfil($canChangeProfil) { @@ -840,6 +868,7 @@ class User /** * @param boolean $canChangeFtpProfil + * @return $this */ public function setCanChangeFtpProfil($canChangeFtpProfil) { @@ -858,6 +887,7 @@ class User /** * @param \DateTime $lastConnection + * @return $this */ public function setLastConnection(\DateTime $lastConnection) { @@ -876,6 +906,7 @@ class User /** * @param boolean $mailLocked + * @return $this */ public function setMailLocked($mailLocked) { @@ -922,6 +953,7 @@ class User /** * @param \Datetime $created + * @return $this */ public function setCreated(\Datetime $created) { @@ -932,6 +964,7 @@ class User /** * @param \Datetime $updated + * @return $this */ public function setUpdated(\Datetime $updated) { diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php index 8445c86fb5..033c4ec400 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/ElasticSearchEngine.php @@ -11,6 +11,7 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\Exception\LogicException; use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\RecordIndexer; use Alchemy\Phrasea\SearchEngine\Elastic\Search\AggregationHelper; @@ -55,7 +56,15 @@ class ElasticSearchEngine implements SearchEngineInterface */ private $context_factory; - public function __construct(Application $app, Structure $structure, Client $client, $indexName, QueryContextFactory $context_factory, Closure $facetsResponseFactory, ElasticsearchOptions $options) + /** + * @param Application $app + * @param Structure $structure + * @param Client $client + * @param QueryContextFactory $context_factory + * @param callable $facetsResponseFactory + * @param ElasticsearchOptions $options + */ + public function __construct(Application $app, Structure $structure, Client $client, QueryContextFactory $context_factory, Closure $facetsResponseFactory, ElasticsearchOptions $options) { $this->app = $app; $this->structure = $structure; @@ -64,11 +73,7 @@ class ElasticSearchEngine implements SearchEngineInterface $this->facetsResponseFactory = $facetsResponseFactory; $this->options = $options; - if ('' === trim($indexName)) { - throw new \InvalidArgumentException('The provided index name is invalid.'); - } - - $this->indexName = $indexName; + $this->indexName = $options->getIndexName(); } @@ -306,7 +311,7 @@ class ElasticSearchEngine implements SearchEngineInterface $query['ast'] = $query_compiler->parse($string)->dump(); $query['query_main'] = $recordQuery; $query['query'] = $params['body']; - $query['query_string'] = json_encode($params['body']); + $query['query_string'] = json_encode($params['body'], JSON_PRETTY_PRINT); return new SearchEngineResult( $options, @@ -366,7 +371,34 @@ class ElasticSearchEngine implements SearchEngineInterface */ public function autocomplete($query, SearchEngineOptions $options) { - throw new RuntimeException('Elasticsearch engine currently does not support auto-complete.'); + $params = $this->createCompletionParams($query, $options); + + $res = $this->client->suggest($params); + + $ret = [ + 'text' => [], + 'byField' => [] + ]; + foreach(array_keys($params['body']) as $fname) { + $t = []; + foreach($res[$fname] as $suggest) { // don't know why there is a sub-array level + foreach($suggest['options'] as $option) { + $text = $option['text']; + if(!in_array($text, $ret['text'])) { + $ret['text'][] = $text; + } + $t[] = [ + 'label' => $text, + 'query' => $fname.':'.$text + ]; + } + } + if(!empty($t)) { + $ret['byField'][$fname] = $t; + } + } + + return $ret; } /** @@ -390,6 +422,40 @@ class ElasticSearchEngine implements SearchEngineInterface { } + private function createCompletionParams($query, SearchEngineOptions $options) + { + $body = []; + $context = [ + 'record_type' => $options->getSearchType() === SearchEngineOptions::RECORD_RECORD ? + SearchEngineInterface::GEM_TYPE_RECORD : SearchEngineInterface::GEM_TYPE_STORY + ]; + + $base_ids = $options->getBasesIds(); + if (count($base_ids) > 0) { + $context['base_id'] = $base_ids; + } + + $search_context = $this->context_factory->createContext($options); + $fields = $search_context->getUnrestrictedFields(); + foreach($fields as $field) { + if($field->getType() == FieldMapping::TYPE_STRING) { + $k = '' . $field->getName(); + $body[$k] = [ + 'text' => $query, + 'completion' => [ + 'field' => "caption." . $field->getName() . ".suggest", + 'context' => &$context + ] + ]; + } + } + + return [ + 'index' => $this->indexName, + 'body' => $body + ]; + } + private function createRecordQueryParams($ESQuery, SearchEngineOptions $options, \record_adapter $record = null) { $params = [ @@ -502,9 +568,9 @@ class ElasticSearchEngine implements SearchEngineInterface $filters[]['term']['type'] = $type; } - $collections = $options->getCollections(); - if (count($collections) > 0) { - $filters[]['terms']['base_id'] = array_keys($collections); + $bases = $options->getBasesIds(); + if (count($bases) > 0) { + $filters[]['terms']['base_id'] = $bases; } if (count($options->getStatus()) > 0) { @@ -645,12 +711,12 @@ class ElasticSearchEngine implements SearchEngineInterface { $filters = []; - $collections = $options->getCollections(); + $bases = $options->getBasesIds(); $collectionsWoRules = []; $collectionsWoRules['terms']['base_id'] = []; foreach ($aclRules as $baseId => $flagsRules) { - if(!array_key_exists($baseId, $collections)) { + if(!in_array($baseId, $bases)) { // no need to add a filter if the collection is not searched continue; } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/FieldMapping.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/FieldMapping.php index dbfda581f4..b113bc4bfd 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/FieldMapping.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/FieldMapping.php @@ -20,9 +20,10 @@ class FieldMapping const DATE_FORMAT_CAPTION_PHP = 'Y/m/d'; // PHP format // Core types - const TYPE_STRING = 'string'; - const TYPE_BOOLEAN = 'boolean'; - const TYPE_DATE = 'date'; + const TYPE_STRING = 'string'; + const TYPE_BOOLEAN = 'boolean'; + const TYPE_DATE = 'date'; + const TYPE_COMPLETION = 'completion'; // Number core types const TYPE_FLOAT = 'float'; @@ -47,7 +48,8 @@ class FieldMapping self::TYPE_SHORT, self::TYPE_BYTE, self::TYPE_IP, - self::TYPE_OBJECT + self::TYPE_OBJECT, + self::TYPE_COMPLETION ); /** diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndex.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndex.php index 8f3ca67283..a2d287084f 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndex.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Indexer/RecordIndex.php @@ -52,8 +52,10 @@ class RecordIndex implements MappingProvider // Database name (still indexed for facets) $mapping->addStringField('databox_name')->disableAnalysis(); - // Unique collection ID - $mapping->addIntegerField('base_id'); + // Unique base ID + //$mapping->addIntegerField('base_id')->enableIndexing(); + $mapping->addStringField('base_id')->disableAnalysis(); // must be a string to match completion context ? + // Useless collection ID (local to databox) $mapping->addIntegerField('collection_id')->disableIndexing(); // Collection name (still indexed for facets) @@ -64,6 +66,7 @@ class RecordIndex implements MappingProvider $mapping->addStringField('original_name')->disableIndexing(); $mapping->addStringField('mime')->disableAnalysis(); $mapping->addStringField('type')->disableAnalysis(); + $mapping->addStringField('record_type')->disableAnalysis(); $mapping->addIntegerField('width')->disableIndexing(); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/CompletionFieldMapping.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/CompletionFieldMapping.php new file mode 100644 index 0000000000..a4be974066 --- /dev/null +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/CompletionFieldMapping.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Phrasea\SearchEngine\Elastic\Mapping; + +use Alchemy\Phrasea\SearchEngine\Elastic\FieldMapping; + +class CompletionFieldMapping extends FieldMapping +{ + /** + * @param string $name + */ + public function __construct($name) + { + parent::__construct($name, 'completion'); + } + + /** + * @return array + */ + protected function getProperties() + { + return [ + 'context' => [ + 'base_id' => [ + 'type' => 'category', + 'path' => 'base_id', + 'default' => '' + ], + 'record_type' => [ + 'type' => 'category', + 'path' => 'record_type', + 'default' => '' + ] + ], + // 'analyzer' => 'simple', + // 'search_analyzer' => 'simple', + // 'payloads' => false + ]; + } +} \ No newline at end of file diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/ComplexMapping.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/ComplexMapping.php index 0e154f832f..6468ea9e6c 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/ComplexMapping.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/ComplexMapping.php @@ -30,9 +30,9 @@ class ComplexMapping extends FieldMapping throw new \LogicException(sprintf('There is already a "%s" multi field.', $child->getName())); } - if ($child->getType() !== $this->getType() && $this->getType() !== self::TYPE_OBJECT) { - throw new \LogicException('Child field type must match parent type.'); - } + // if ($child->getType() !== $this->getType() && $this->getType() !== self::TYPE_OBJECT) { + // throw new \LogicException('Child field type must match parent type.'); + // } return $this->children[$child->getName()] = $child; } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/FieldToFieldMappingConverter.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/FieldToFieldMappingConverter.php index 46c28b9cce..d5bf8c36c6 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/FieldToFieldMappingConverter.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Mapping/FieldToFieldMappingConverter.php @@ -30,6 +30,10 @@ class FieldToFieldMappingConverter $fieldMapping->disableIndexing(); } else { $fieldMapping->addChild((new StringFieldMapping('raw'))->enableRawIndexing()); + + $child = new CompletionFieldMapping('suggest'); + $fieldMapping->addChild($child); + $fieldMapping->addAnalyzedChildren($locales); $fieldMapping->enableTermVectors(true); } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php index 91d63e15fe..e1772aaac1 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Search/QueryContext.php @@ -46,6 +46,9 @@ class QueryContext return new static($this->structure, $this->locales, $this->queryLocale, $fields); } + /** + * @return Field[] + */ public function getUnrestrictedFields() { // TODO Restore search optimization by using "caption_all" field @@ -63,6 +66,10 @@ class QueryContext return $this->filterFields($this->structure->getAllFields()); } + /** + * @param Field[] $fields + * @return Field[] + */ private function filterFields(array $fields) { if ($this->fields !== null) { diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php index cd8629cd91..7ea7d68d43 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Field.php @@ -45,25 +45,29 @@ class Field implements Typed private $used_by_collections; - public static function createFromLegacyField(databox_field $field) + public static function createFromLegacyField(databox_field $field, $with = Structure::WITH_EVERYTHING) { $type = self::getTypeFromLegacy($field); $databox = $field->get_databox(); - // Thesaurus concept inference - $xpath = $field->get_tbranch(); - if ($type === FieldMapping::TYPE_STRING && !empty($xpath)) { - $roots = ThesaurusHelper::findConceptsByXPath($databox, $xpath); - } else { - $roots = null; + $roots = null; + if(($with & Structure::FIELD_WITH_THESAURUS) && $type === FieldMapping::TYPE_STRING) { + // Thesaurus concept inference + $xpath = $field->get_tbranch(); + if (!empty($xpath)) { + $roots = ThesaurusHelper::findConceptsByXPath($databox, $xpath); + } } - // Facet (enable + optional limit) - $facet = $field->getFacetValuesLimit(); - if ($facet === databox_field::FACET_DISABLED) { - $facet = self::FACET_DISABLED; - } elseif ($facet === databox_field::FACET_NO_LIMIT) { - $facet = self::FACET_NO_LIMIT; + $facet = self::FACET_DISABLED; + if($with & Structure::FIELD_WITH_FACETS) { + // Facet (enable + optional limit) + $facet = $field->getFacetValuesLimit(); + if ($facet === databox_field::FACET_DISABLED) { + $facet = self::FACET_DISABLED; + } elseif ($facet === databox_field::FACET_NO_LIMIT) { + $facet = self::FACET_NO_LIMIT; + } } return new self($field->get_name(), $type, [ diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php index 2016e134b3..912bbe934a 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/GlobalStructure.php @@ -46,20 +46,26 @@ final class GlobalStructure implements Structure /** * @param \databox[] $databoxes + * @param int $what bitmask of what should be included in this structure, in fields, ... + * * @return GlobalStructure */ - public static function createFromDataboxes(array $databoxes) + public static function createFromDataboxes(array $databoxes, $what = self::WITH_EVERYTHING) { $fields = []; $flags = []; foreach ($databoxes as $databox) { - foreach ($databox->get_meta_structure() as $fieldStructure) { - $fields[] = Field::createFromLegacyField($fieldStructure); + if($what & self::STRUCTURE_WITH_FIELDS) { + foreach ($databox->get_meta_structure() as $fieldStructure) { + $fields[] = Field::createFromLegacyField($fieldStructure, $what); + } } - foreach ($databox->getStatusStructure() as $status) { - $flags[] = Flag::createFromLegacyStatus($status); + if($what & self::STRUCTURE_WITH_FLAGS) { + foreach ($databox->getStatusStructure() as $status) { + $flags[] = Flag::createFromLegacyStatus($status); + } } } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php index e997ab81fd..671bf87c93 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/LimitedStructure.php @@ -135,13 +135,12 @@ final class LimitedStructure implements Structure ]); } + /** + * @return int[] // base_id's + */ private function allowedCollections() { // Get all collections (base_id) with allowed private field access (user rights are computed in options object) - $allowed_collections = []; - foreach ($this->search_options->getBusinessFieldsOn() as $collection) { - $allowed_collections[] = $collection->get_base_id(); - } - return $allowed_collections; + return $this->search_options->getBusinessFieldsOn(); } } diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php index c017edb239..3c2be701e1 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/Structure.php @@ -12,6 +12,12 @@ namespace Alchemy\Phrasea\SearchEngine\Elastic\Structure; interface Structure { + const STRUCTURE_WITH_FIELDS = 0x10; + const STRUCTURE_WITH_FLAGS = 0x20; + const FIELD_WITH_THESAURUS = 0x01; + const FIELD_WITH_FACETS = 0x02; + const WITH_EVERYTHING = 0xFF; + /** * @return Field[] */ diff --git a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php index 530b668363..ad704575d9 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php +++ b/lib/Alchemy/Phrasea/SearchEngine/SearchEngineOptions.php @@ -15,10 +15,12 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Authentication\ACLProvider; use Alchemy\Phrasea\Authentication\Authenticator; use Alchemy\Phrasea\Collection\CollectionRepository; -use Alchemy\Phrasea\Collection\Reference\CollectionReferenceCollection; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; +use Alchemy\Phrasea\Collection\Reference\DbalCollectionReferenceRepository; use Assert\Assertion; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use databox_descriptionStructure; class SearchEngineOptions { @@ -38,104 +40,25 @@ class SearchEngineOptions const SORT_MODE_ASC = 'asc'; const SORT_MODE_DESC = 'desc'; - private static $serializable_properties = [ - 'record_type', - 'search_type', - 'collections', - 'fields', - 'status', - 'date_min', - 'date_max', - 'date_fields', - 'i18n', - 'stemming', - 'sort_by', - 'sort_ord', - 'business_fields', - 'max_results', - 'first_result', - ]; - - /** - * @param Application $app - * @return callable[] - */ - private static function getHydrateMethods(Application $app) - { - $fieldNormalizer = function ($value) use ($app) { - return array_map(function ($serialized) use ($app) { - $data = explode('_', $serialized, 2); - - return $app->findDataboxById($data[0])->get_meta_structure()->get_element($data[1]); - }, $value); - }; - - $collectionNormalizer = function ($value) use ($app) { - $references = new CollectionReferenceCollection($app['repo.collection-references']->findMany($value)); - - $collections = []; - - foreach ($references->groupByDataboxIdAndCollectionId() as $databoxId => $indexes) { - /** @var CollectionRepository $repository */ - $repository = $app['repo.collections-registry']->getRepositoryByDatabox($databoxId); - - foreach ($indexes as $collectionId => $index) { - $coll = $repository->find($collectionId); - $collections[$coll->get_base_id()] = $coll; - } - } - - return $collections; - }; - - $optionSetter = function ($setter) { - return function ($value, SearchEngineOptions $options) use ($setter) { - $options->{$setter}($value); - }; - }; - - return [ - 'record_type' => $optionSetter('setRecordType'), - 'search_type' => $optionSetter('setSearchType'), - 'status' => $optionSetter('setStatus'), - 'date_min' => function ($value, SearchEngineOptions $options) { - $options->setMinDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); - }, - 'date_max' => function ($value, SearchEngineOptions $options) { - $options->setMaxDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); - }, - 'i18n' => function ($value, SearchEngineOptions $options) { - if ($value) { - $options->setLocale($value); - } - }, - 'stemming' => $optionSetter('setStemming'), - 'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { - $options->setDateFields($fieldNormalizer($value)); - }, - 'fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { - $options->setFields($fieldNormalizer($value)); - }, - 'collections' => function ($value, SearchEngineOptions $options) use ($collectionNormalizer) { - $options->onCollections($collectionNormalizer($value)); - }, - 'business_fields' => function ($value, SearchEngineOptions $options) use ($collectionNormalizer) { - $options->allowBusinessFieldsOn($collectionNormalizer($value)); - }, - 'first_result' => $optionSetter('setFirstResult'), - 'max_results' => $optionSetter('setMaxResults'), - ]; - } + /** @var DbalCollectionReferenceRepository $dbalCollectionReferenceRepository */ + private $collectionReferenceRepository; /** @var string */ protected $record_type = self::TYPE_ALL; protected $search_type = self::RECORD_RECORD; - /** @var \collection[] */ - protected $collections = []; - /** @var null|\databox[] */ - private $databoxes; + + /** @var null|int[] bases ids where searching is done */ + private $basesIds = null; + + /** @var null|CollectionReference[][] */ + private $collectionsReferencesByDatabox = null; + + /** @var null|\int[] */ + private $databoxesIds; + /** @var \databox_field[] */ + protected $fields = []; protected $status = []; /** @var \DateTime */ @@ -152,6 +75,8 @@ class SearchEngineOptions /** @var string */ protected $sort_ord = self::SORT_MODE_DESC; + + /** @var int[] */ protected $business_fields = []; /** @@ -164,6 +89,24 @@ class SearchEngineOptions */ private $first_result = 0; + private static $serializable_properties = [ + 'record_type', + 'search_type', + 'basesIds', + 'fields', + 'status', + 'date_min', + 'date_max', + 'date_fields', + 'i18n', + 'stemming', + 'sort_by', + 'sort_ord', + 'business_fields', + 'max_results', + 'first_result' + ]; + /** * Defines locale code to use for query * @@ -205,14 +148,14 @@ class SearchEngineOptions } /** - * Allows business fields query on the given collections + * Allows business fields query on the given bases * - * @param \collection[] $collection An array of collection + * @param int[] $basesIds * @return $this */ - public function allowBusinessFieldsOn(array $collection) + public function allowBusinessFieldsOn(array $basesIds) { - $this->business_fields = $collection; + $this->business_fields = $basesIds; return $this; } @@ -230,10 +173,10 @@ class SearchEngineOptions } /** - * Returns an array of collection on which business fields are allowed to + * Returns an array of bases ids on which business fields are allowed to * search on * - * @return \collection[] An array of collection + * @return int[] */ public function getBusinessFieldsOn() { @@ -316,48 +259,33 @@ class SearchEngineOptions } /** - * Set the collections where to search for + * Set the bases where to search for * - * @param \collection[] $collections An array of collection + * @param int[] $basesIds An array of ids * @return $this */ - public function onCollections(array $collections) + public function onBasesIds(array $basesIds) { - $this->collections = $collections; + $this->basesIds = $basesIds; + // Defer databox retrieval - $this->databoxes = null; + $this->databoxesIds = null; return $this; } /** - * Returns the collections on which the search occurs + * Returns the bases ids on which the search occurs * - * @return \collection[] An array of collection + * @return int[] */ - public function getCollections() + public function getBasesIds() { - return $this->collections; - } - - /** - * Returns an array containing all the databoxes where the search will - * happen - * - * @return \databox[] - */ - public function getDataboxes() - { - if (null === $this->databoxes) { - $databoxes = []; - foreach ($this->collections as $collection) { - $databoxes[$collection->get_databox()->get_sbas_id()] = $collection->get_databox(); - } - - $this->databoxes = array_values($databoxes); + if($this->basesIds === null) { + throw new \LogicException('onBasesIds() must be called before getBasesIds()'); } - return $this->databoxes; + return $this->basesIds; } /** @@ -429,13 +357,16 @@ class SearchEngineOptions return $this; } - /** @return string */ + /** + * @return string + */ public function getRecordType() { return $this->record_type; } /** + * @param \DateTime $min_date * @return $this */ public function setMinDate(\DateTime $min_date = null) @@ -449,7 +380,8 @@ class SearchEngineOptions return $this; } - /** @return \DateTime + /** + * @return \DateTime */ public function getMinDate() { @@ -471,7 +403,9 @@ class SearchEngineOptions return $this; } - /** @return \DateTime */ + /** + * @return \DateTime + */ public function getMaxDate() { return $this->date_max; @@ -494,89 +428,6 @@ class SearchEngineOptions return $this->date_fields; } - public function serialize() - { - $ret = []; - foreach (self::$serializable_properties as $key) { - $value = $this->{$key}; - if ($value instanceof \DateTime) { - $value = $value->format(DATE_ATOM); - } - if (in_array($key, ['date_fields', 'fields'])) { - $value = array_map(function (\databox_field $field) { - return $field->get_databox()->get_sbas_id() . '_' . $field->get_id(); - }, $value); - } - if (in_array($key, ['collections', 'business_fields'])) { - $value = array_map(function (\collection $collection) { - return $collection->get_base_id(); - }, $value); - } - - $ret[$key] = $value; - } - - return \p4string::jsonencode($ret); - } - - /** - * - * @param Application $app - * @param string $serialized - * - * @return $this - * - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public static function hydrate(Application $app, $serialized) - { - $serialized = json_decode($serialized, true); - - if (!is_array($serialized)) { - throw new \InvalidArgumentException('SearchEngineOptions data are corrupted'); - } - - $options = new static(); - $options->disallowBusinessFields(); - - $methods = self::getHydrateMethods($app); - - $sort_by = null; - $methods['sort_by'] = function ($value) use (&$sort_by) { - $sort_by = $value; - }; - - $sort_ord = null; - $methods['sort_ord'] = function ($value) use (&$sort_ord) { - $sort_ord = $value; - }; - - foreach ($serialized as $key => $value) { - if (!isset($methods[$key])) { - throw new \RuntimeException(sprintf('Unable to handle key `%s`', $key)); - } - - if ($value instanceof \stdClass) { - $value = (array)$value; - } - - $callable = $methods[$key]; - - $callable($value, $options); - } - - if ($sort_by) { - if ($sort_ord) { - $options->setSort($sort_by, $sort_ord); - } else { - $options->setSort($sort_by); - } - } - - return $options; - } - /** * Creates options based on a Symfony Request object * @@ -587,99 +438,21 @@ class SearchEngineOptions */ public static function fromRequest(Application $app, Request $request) { + /** @var Authenticator $authenticator */ + $authenticator = $app->getAuthenticator(); + $isAuthenticated = $authenticator->isAuthenticated(); + $options = new static(); + $options->collectionReferenceRepository = $app['repo.collection-references']; + $options->disallowBusinessFields(); $options->setLocale($app['locale']); - /** @var Authenticator $authenticator */ - $authenticator = $app->getAuthenticator(); - $isAuthenticated = $authenticator->isAuthenticated(); - /** @var ACLProvider $aclProvider */ - $aclProvider = $app['acl']; - $acl = $isAuthenticated ? $aclProvider->get($authenticator->getUser()) : null; - - $selected_bases = $request->get('bases'); - if (is_array($selected_bases)) { - $bas = []; - foreach ($selected_bases as $bas_id) { - try { - $bas[$bas_id] = \collection::getByBaseId($app, $bas_id); - } catch (\Exception_Databox_CollectionNotFound $e) { - // Ignore - } - } - } elseif (!$isAuthenticated) { - $bas = $app->getOpenCollections(); - } else { - $bas = $acl->get_granted_base(); - } - - // Filter out not found collections - $bas = array_filter($bas); - - if ($acl) { - $filter = function (\collection $collection) use ($acl) { - return $acl->has_access_to_base($collection->get_base_id()); - }; - } else { - $openCollections = $app->getOpenCollections(); - - $filter = function (\collection $collection) use ($openCollections) { - return in_array($collection, $openCollections); - }; - } - - /** @var \collection[] $bas */ - $bas = array_filter($bas, $filter); - - if (!empty($selected_bases) && empty($bas)) { - throw new BadRequestHttpException('No collections match your criteria'); - } - - $options->onCollections($bas); - - if ($isAuthenticated && $acl->has_right(\ACL::CANMODIFRECORD)) { - $bf = array_filter($bas, function (\collection $collection) use ($acl) { - return $acl->has_right_on_base($collection->get_base_id(), \ACL::CANMODIFRECORD); - }); - - $options->allowBusinessFieldsOn($bf); - } - - $status = is_array($request->get('status')) ? $request->get('status') : []; - $fields = is_array($request->get('fields')) ? $request->get('fields') : []; - if (empty($fields)) { - // Select all fields (business included) - foreach ($options->getDataboxes() as $databox) { - foreach ($databox->get_meta_structure() as $field) { - $fields[] = $field->get_name(); - } - } - $fields = array_unique($fields); - } - - $databoxFields = []; - $databoxes = $options->getDataboxes(); - foreach ($databoxes as $databox) { - $metaStructure = $databox->get_meta_structure(); - foreach ($fields as $field) { - try { - $databoxField = $metaStructure->get_element_by_name($field); - } catch (\Exception $e) { - continue; - } - if ($databoxField) { - $databoxFields[] = $databoxField; - } - } - } - - $options->setFields($databoxFields); - $options->setStatus($status); - $options->setSearchType($request->get('search_type')); $options->setRecordType($request->get('record_type')); + $options->setSort($request->get('sort'), $request->get('ord', SearchEngineOptions::SORT_MODE_DESC)); + $options->setStemming((Boolean) $request->get('stemme')); $min_date = $max_date = null; if ($request->get('date_min')) { @@ -688,33 +461,106 @@ class SearchEngineOptions if ($request->get('date_max')) { $max_date = \DateTime::createFromFormat('Y/m/d H:i:s', $request->get('date_max') . ' 23:59:59'); } - $options->setMinDate($min_date); $options->setMaxDate($max_date); + $status = is_array($request->get('status')) ? $request->get('status') : []; + $options->setStatus($status); + + /** @var ACLProvider $aclProvider */ + $aclProvider = $app['acl']; + $acl = $isAuthenticated ? $aclProvider->get($authenticator->getUser()) : null; + if ($acl) { + $searchableBaseIds = $acl->getSearchableBasesIds(); + if (is_array($request->get('bases'))) { + $selected_bases = array_map(function($bid){return (int)$bid;}, $request->get('bases')); + $searchableBaseIds = array_values(array_intersect($searchableBaseIds, $selected_bases)); + if (empty($searchableBaseIds)) { + throw new BadRequestHttpException('No collections match your criteria'); + } + } + $options->onBasesIds($searchableBaseIds); + if ($acl->has_right(\ACL::CANMODIFRECORD)) { + /** @var int[] $bf */ + $bf = array_filter($searchableBaseIds, function ($baseId) use ($acl) { + return $acl->has_right_on_base($baseId, \ACL::CANMODIFRECORD); + }); + $options->allowBusinessFieldsOn($bf); + } + } + else { + $options->onBasesIds([]); + } + + /** @var \databox[] $databoxes */ + $databoxes = []; + foreach($options->getCollectionsReferencesByDatabox() as $sbid=>$refs) { + $databoxes[] = $app->findDataboxById($sbid); + } + + $queryFields = is_array($request->get('fields')) ? $request->get('fields') : []; + if (empty($queryFields)) { + // Select all fields (business included) + foreach ($databoxes as $databox) { + foreach ($databox->get_meta_structure() as $field) { + $queryFields[] = $field->get_name(); + } + } + } + $queryFields = array_unique($queryFields); + + $queryDateFields = array_unique(explode('|', $request->get('date_field'))); + + $databoxFields = []; $databoxDateFields = []; foreach ($databoxes as $databox) { $metaStructure = $databox->get_meta_structure(); - foreach (explode('|', $request->get('date_field')) as $field) { + + foreach ($queryFields as $fieldName) { try { - $databoxField = $metaStructure->get_element_by_name($field); + if( ($databoxField = $metaStructure->get_element_by_name($fieldName, databox_descriptionStructure::STRICT_COMPARE)) ) { + $databoxFields[] = $databoxField; + } } catch (\Exception $e) { - continue; + // no-op } - if ($databoxField) { - $databoxDateFields[] = $databoxField; + } + + foreach ($queryDateFields as $fieldName) { + try { + if( ($databoxField = $metaStructure->get_element_by_name($fieldName, databox_descriptionStructure::STRICT_COMPARE)) ) { + $databoxDateFields[] = $databoxField; + } + } catch (\Exception $e) { + // no-op } } } + $options->setFields($databoxFields); $options->setDateFields($databoxDateFields); - $options->setSort($request->get('sort'), $request->get('ord', SearchEngineOptions::SORT_MODE_DESC)); - $options->setStemming((Boolean) $request->get('stemme')); return $options; } + public function getCollectionsReferencesByDatabox() + { + if($this->collectionsReferencesByDatabox === null) { + $this->collectionsReferencesByDatabox = []; + $refs = $this->collectionReferenceRepository->findMany($this->getBasesIds()); + foreach($refs as $ref) { + $sbid = $ref->getDataboxId(); + if(!array_key_exists($sbid, $this->collectionsReferencesByDatabox)) { + $this->collectionsReferencesByDatabox[$sbid] = []; + } + $this->collectionsReferencesByDatabox[$sbid][] = $ref; + } + } + + return $this->collectionsReferencesByDatabox; + } + public function setMaxResults($max_results) { Assertion::greaterOrEqualThan($max_results, 0); @@ -745,4 +591,133 @@ class SearchEngineOptions { return $this->first_result; } + + /** + * @param Application $app + * @return callable[] + */ + private static function getHydrateMethods(Application $app) + { + $fieldNormalizer = function ($value) use ($app) { + return array_map(function ($serialized) use ($app) { + $data = explode('_', $serialized, 2); + return $app->findDataboxById($data[0])->get_meta_structure()->get_element($data[1]); + }, $value); + }; + + $optionSetter = function ($setter) { + return function ($value, SearchEngineOptions $options) use ($setter) { + $options->{$setter}($value); + }; + }; + + return [ + 'record_type' => $optionSetter('setRecordType'), + 'search_type' => $optionSetter('setSearchType'), + 'status' => $optionSetter('setStatus'), + 'date_min' => function ($value, SearchEngineOptions $options) { + $options->setMinDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); + }, + 'date_max' => function ($value, SearchEngineOptions $options) { + $options->setMaxDate($value ? \DateTime::createFromFormat(DATE_ATOM, $value) : null); + }, + 'i18n' => function ($value, SearchEngineOptions $options) { + if ($value) { + $options->setLocale($value); + } + }, + 'stemming' => $optionSetter('setStemming'), + 'date_fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { + $options->setDateFields($fieldNormalizer($value)); + }, + 'fields' => function ($value, SearchEngineOptions $options) use ($fieldNormalizer) { + $options->setFields($fieldNormalizer($value)); + }, + 'basesIds' => function ($value, SearchEngineOptions $options) { + $options->onBasesIds($value); + }, + //'business_fields' => function ($value, SearchEngineOptions $options) use ($collectionNormalizer) { + // $options->allowBusinessFieldsOn($collectionNormalizer($value)); + //}, + 'business_fields' => function ($value, SearchEngineOptions $options) { + $options->allowBusinessFieldsOn($value); + }, + 'first_result' => $optionSetter('setFirstResult'), + 'max_results' => $optionSetter('setMaxResults'), + ]; + } + + public function serialize() + { + $ret = []; + foreach (self::$serializable_properties as $key) { + $value = $this->{$key}; + if ($value instanceof \DateTime) { + $value = $value->format(DATE_ATOM); + } + if (in_array($key, ['date_fields', 'fields'])) { + $value = array_map(function (\databox_field $field) { + return $field->get_databox()->get_sbas_id() . '_' . $field->get_id(); + }, $value); + } + $ret[$key] = $value; + } + + return \p4string::jsonencode($ret); + } + + /** + * + * @param Application $app + * @param string $serialized + * + * @return $this + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public static function hydrate(Application $app, $serialized) + { + $serialized = json_decode($serialized, true); + if (!is_array($serialized)) { + throw new \InvalidArgumentException('SearchEngineOptions data are corrupted'); + } + + $options = new static(); + $options->disallowBusinessFields(); + + $methods = self::getHydrateMethods($app); + + $sort_by = null; + $methods['sort_by'] = function ($value) use (&$sort_by) { + $sort_by = $value; + }; + + $sort_ord = null; + $methods['sort_ord'] = function ($value) use (&$sort_ord) { + $sort_ord = $value; + }; + + foreach ($serialized as $key => $value) { + if (!isset($methods[$key])) { + throw new \RuntimeException(sprintf('Unable to handle key `%s`', $key)); + } + if ($value instanceof \stdClass) { + $value = (array)$value; + } + $callable = $methods[$key]; + $callable($value, $options); + } + + if ($sort_by) { + if ($sort_ord) { + $options->setSort($sort_by, $sort_ord); + } else { + $options->setSort($sort_by); + } + } + + return $options; + } + } diff --git a/lib/classes/ACL.php b/lib/classes/ACL.php index 86817a3f5f..cbeb9b5672 100644 --- a/lib/classes/ACL.php +++ b/lib/classes/ACL.php @@ -10,7 +10,9 @@ */ use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\Collection\Reference\CollectionReferenceCollection; +use Alchemy\Phrasea\Collection\Reference\DbalCollectionReferenceRepository; use Alchemy\Phrasea\Core\Event\Acl\AccessPeriodChangedEvent; use Alchemy\Phrasea\Core\Event\Acl\AccessToBaseGrantedEvent; use Alchemy\Phrasea\Core\Event\Acl\AccessToBaseRevokedEvent; @@ -775,6 +777,41 @@ class ACL implements cache_cacheableInterface return $ret; } + /** + * @return array baseIds where user can search + */ + public function getSearchableBasesIds() + { + static $ret = null; + + if($ret === null) { + $this->load_rights_bas(); + foreach ($this->_rights_bas as $baseId => $rights) { + if ($this->has_access_to_base($baseId) && !$this->is_limited($baseId)) { + $ret[] = $baseId; + } + } + } + + return $ret; + } + + /** + * @return CollectionReference[] CollectionsReferences where the user can search; + */ + public function getSearchableBasesReferences() + { + static $ret = null; + + if($ret == null) { + /** @var DbalCollectionReferenceRepository $dbalCollectionReferenceRepository */ + $dbalCollectionReferenceRepository = $this->app['repo.collection-references']; + $ret = $dbalCollectionReferenceRepository->findMany($this->getSearchableBasesIds()); + } + + return $ret; + } + /** * Return an array of databox (key=sbas_id) which are granted, with * optionnal filter by rights diff --git a/lib/classes/databox.php b/lib/classes/databox.php index 5603bec2cd..d216acdd7f 100644 --- a/lib/classes/databox.php +++ b/lib/classes/databox.php @@ -888,7 +888,7 @@ class databox extends base implements ThumbnailedElement /** @var \Alchemy\Phrasea\Databox\Field\DataboxFieldRepository $fieldRepository */ $fieldRepository = $this->app['repo.fields.factory']($this); - $this->meta_struct = new databox_descriptionStructure($fieldRepository->findAll()); + $this->meta_struct = new databox_descriptionStructure($fieldRepository->findAll(), $this->app['unicode']); return $this->meta_struct; } diff --git a/lib/classes/databox/descriptionStructure.php b/lib/classes/databox/descriptionStructure.php index d772d0e514..945b2fad4d 100644 --- a/lib/classes/databox/descriptionStructure.php +++ b/lib/classes/databox/descriptionStructure.php @@ -17,18 +17,26 @@ class databox_descriptionStructure implements IteratorAggregate, Countable */ protected $elements = []; + /** @var unicode */ + private $unicode; + + const STRICT_COMPARE = 1; + const SLUG_COMPARE = 2; + /** * Cache array for the get element by name function * - * @var array|null + * @var int[]|null */ protected $cache_name_id; /** * @param databox_field[] $fields + * @param unicode $unicode */ - public function __construct($fields = []) + public function __construct($fields, unicode $unicode) { + $this->unicode = $unicode; Assertion::allIsInstanceOf($fields, databox_field::class); foreach ($fields as $field) { @@ -95,9 +103,11 @@ class databox_descriptionStructure implements IteratorAggregate, Countable /** * @param string $name + * @param int $compareMode // use STRICT_COMPARE if the name already comes from phrasea (faster) + * * @return databox_field|null */ - public function get_element_by_name($name) + public function get_element_by_name($name, $compareMode=self::SLUG_COMPARE) { if (null === $this->cache_name_id) { $this->cache_name_id = []; @@ -107,7 +117,9 @@ class databox_descriptionStructure implements IteratorAggregate, Countable } } - $name = databox_field::generateName($name); + if($compareMode == self::SLUG_COMPARE) { + $name = databox_field::generateName($name, $this->unicode); + } return isset($this->cache_name_id[$name]) ? $this->elements[$this->cache_name_id[$name]] diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php index 2cbdc9679b..ffe3996597 100644 --- a/lib/classes/databox/field.php +++ b/lib/classes/databox/field.php @@ -32,7 +32,7 @@ class databox_field implements cache_cacheableInterface protected $databox; /** DO NOT IMPORT, makes PHPSTORM HANG. PHPExiftool\Driver\TagInterface */ - protected $tag; + private $tag; protected $name; protected $indexable; @@ -79,12 +79,12 @@ class databox_field implements cache_cacheableInterface /** * @var databox_Field_DCESAbstract|null */ - protected $dces_element; + private $dces_element; /** * @var ControlProviderInterface|null */ - protected $Vocabulary; + protected $vocabulary_control; /** * @var string|null @@ -95,8 +95,9 @@ class databox_field implements cache_cacheableInterface * @var bool */ protected $VocabularyRestriction = false; - protected $on_error = false; + private $on_error = false; protected $original_src; + protected $original_dces; protected $aggregable; const TYPE_TEXT = "text"; @@ -146,13 +147,11 @@ class databox_field implements cache_cacheableInterface $this->id = (int)$row['id']; $this->name = $row['name']; $this->original_src = $row['src']; - $this->tag = in_array($row['src'], ['', 'Phraseanet:no-source'], true) - ? new NoSource($this->name) - : self::loadClassFromTagName($row['src'], false); - - if ($row['src'] !== '' && $row['src'] !== $this->tag->getTagname()) { - $this->on_error = true; - } + $this->original_dces = $row['dces_element']; + $this->tag = false; // lazy loaded on this->get_tag(), will become an object + $this->dces_element = false; // loazy loaded on this->get_dces_element(), will become an object or null + $this->on_error = false; // lazy calculated on this->is_on_error() + $this->vocabulary_control = false; // lazy loaded foreach (['en', 'fr', 'de', 'nl'] as $code) { $this->labels[$code] = $row['label_' . $code]; @@ -171,16 +170,9 @@ class databox_field implements cache_cacheableInterface $this->VocabularyType = $row['VocabularyControlType']; $this->VocabularyRestriction = (bool)$row['RestrictToVocabularyControl']; - if (isset($row['dces_element'])) { - $class = self::$knownDCES[$row['dces_element']]; - $this->dces_element = new $class(); - } - $this->separator = self::checkMultiSeparator($row['separator'], $this->multi); $this->thumbtitle = $row['thumbtitle']; - - $this->loadVocabulary(); } /** @@ -188,7 +180,7 @@ class databox_field implements cache_cacheableInterface */ public function getVocabularyControl() { - return $this->Vocabulary; + return $this->vocabulary_control; } /** @@ -196,7 +188,12 @@ class databox_field implements cache_cacheableInterface */ public function isVocabularyRestricted() { - return $this->VocabularyRestriction; + // lazy load + if($this->vocabulary_control === false) { + $this->loadVocabulary(); + } + + return $this->vocabulary_control; } /** @@ -322,7 +319,7 @@ class databox_field implements cache_cacheableInterface $params = [ ':name' => $this->name, - ':source' => $this->tag->getTagname(), + ':source' => $this->get_tag()->getTagname(), ':indexable' => $this->indexable ? '1' : '0', ':readonly' => $this->readonly ? '1' : '0', ':required' => $this->required ? '1' : '0', @@ -335,8 +332,8 @@ class databox_field implements cache_cacheableInterface ':tbranch' => $this->tbranch, ':position' => $this->position, ':thumbtitle' => $this->thumbtitle, - ':VocabularyControlType' => $this->Vocabulary ? $this->Vocabulary->getType() : null, - ':RestrictVocab' => $this->Vocabulary ? ($this->VocabularyRestriction ? '1' : '0') : '0', + ':VocabularyControlType' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null, + ':RestrictVocab' => $this->getVocabularyControl() ? ($this->VocabularyRestriction ? '1' : '0') : '0', ':id' => $this->id, ':label_en' => isset($this->labels['en']) ? $this->labels['en'] : null, ':label_fr' => isset($this->labels['fr']) ? $this->labels['fr'] : null, @@ -374,7 +371,7 @@ class databox_field implements cache_cacheableInterface $nodes_parent->item(0)->replaceChild($meta, $old_meta); } } - $meta->setAttribute('src', $this->tag->getTagname()); + $meta->setAttribute('src', $this->get_tag()->getTagname()); $meta->setAttribute('index', $this->indexable ? '1' : '0'); $meta->setAttribute('readonly', $this->readonly ? '1' : '0'); $meta->setAttribute('required', $this->required ? '1' : '0'); @@ -444,15 +441,16 @@ class databox_field implements cache_cacheableInterface } /** - * - * @param string $name + * @param string $name * @return databox_field + * + * @throws Exception_InvalidArgument */ public function set_name($name) { $previous_name = $this->name; - $name = self::generateName($name); + $name = self::generateName($name, $this->app['unicode']); if ($name === '') { throw new \Exception_InvalidArgument(); @@ -513,6 +511,13 @@ class databox_field implements cache_cacheableInterface */ public function get_tag() { + // lazy loading + if ($this->tag === false) { + $this->tag = in_array($this->original_src, ['', 'Phraseanet:no-source'], true) + ? new NoSource($this->name) + : self::loadClassFromTagName($this->original_src, false); + } + return $this->tag; } @@ -521,6 +526,16 @@ class databox_field implements cache_cacheableInterface */ public function get_dces_element() { + // lazy loading + if ($this->dces_element === false) { + if (array_key_exists($this->original_dces, self::$knownDCES)) { + $class = self::$knownDCES[$this->original_dces]; + $this->dces_element = new $class(); + } else { + $this->dces_element = null; + } + } + return $this->dces_element; } @@ -579,12 +594,12 @@ class databox_field implements cache_cacheableInterface /** * Set a vocabulary * - * @param ControlProviderInterface $vocabulary + * @param ControlProviderInterface $vocabulary_control * @return \databox_field */ - public function setVocabularyControl(ControlProviderInterface $vocabulary = null) + public function setVocabularyControl(ControlProviderInterface $vocabulary_control = null) { - $this->Vocabulary = $vocabulary; + $this->vocabulary_control = $vocabulary_control; return $this; } @@ -864,7 +879,7 @@ class databox_field implements cache_cacheableInterface */ public function is_on_error() { - return $this->on_error; + return $this->original_src !== '' && $this->original_src !== $this->get_tag()->getTagname(); } public function toArray() @@ -874,7 +889,7 @@ class databox_field implements cache_cacheableInterface 'sbas-id' => $this->sbas_id, 'labels' => $this->labels, 'name' => $this->name, - 'tag' => $this->tag->getTagname(), + 'tag' => $this->get_tag()->getTagname(), 'business' => $this->Business, 'aggregable' => $this->aggregable, 'type' => $this->type, @@ -887,8 +902,8 @@ class databox_field implements cache_cacheableInterface 'readonly' => $this->readonly, 'multi' => $this->multi, 'indexable' => $this->indexable, - 'dces-element' => $this->dces_element ? $this->dces_element->get_label() : null, - 'vocabulary-type' => $this->Vocabulary ? $this->Vocabulary->getType() : null, + 'dces-element' => $this->get_dces_element() ? $this->get_dces_element()->get_label() : null, + 'vocabulary-type' => $this->getVocabularyControl() ? $this->getVocabularyControl()->getType() : null, 'vocabulary-restricted' => $this->VocabularyRestriction, ]; } @@ -926,7 +941,7 @@ class databox_field implements cache_cacheableInterface null, 0, 0, 0, 1, :sorter, '')"; - $name = self::generateName($name); + $name = self::generateName($name, $app['unicode']); if ($name === '') { throw new \Exception_InvalidArgument(); @@ -942,10 +957,8 @@ class databox_field implements cache_cacheableInterface return $databox->get_meta_structure()->get_element($id); } - public static function generateName($name) + public static function generateName($name, unicode $unicode_processor) { - $unicode_processor = new unicode(); - $name = $unicode_processor->remove_nonazAZ09($name, false, false); return $unicode_processor->remove_first_digits($name); @@ -959,7 +972,7 @@ class databox_field implements cache_cacheableInterface $vars = []; foreach ($this as $key => $value) { - if (in_array($key, ['databox', 'app', 'Vocabulary'])) + if (in_array($key, ['databox', 'app', 'vocabulary_control'])) continue; $vars[] = $key; } @@ -1020,7 +1033,7 @@ class databox_field implements cache_cacheableInterface } try { - $this->Vocabulary = $this->app['vocabularies'][$this->VocabularyType]; + $this->vocabulary_control = $this->app['vocabularies'][$this->VocabularyType]; } catch (\InvalidArgumentException $e) { // Could not find Vocabulary } diff --git a/resources/www/prod/js/jquery.main-prod.js b/resources/www/prod/js/jquery.main-prod.js index 2107c4e4f0..f7cc9708bc 100644 --- a/resources/www/prod/js/jquery.main-prod.js +++ b/resources/www/prod/js/jquery.main-prod.js @@ -1529,6 +1529,49 @@ $(document).ready(function () { } }); + $("#EDIT_query").autocomplete({ + delay: 200, + minLength: 2, + source: function (request, response) { + var inp = document.getElementById("EDIT_query"); + var data={ + '_selectionStart': inp.selectionStart, + '_selectionEnd': inp.selectionEnd + }; + var a = $("#searchForm").serializeArray(); + for(var i=0; iauthenticate($app); $options = new SearchEngineOptions(); - $options->onCollections($app->getAclForUser($app->getAuthenticatedUser())->get_granted_base()); + $searchableBasesIds = $app->getAclForUser($app->getAuthenticatedUser())->getSearchableBasesIds(); + $options->onBasesIds($searchableBasesIds); $serializedOptions = $options->serialize(); $response = $this->request('POST', '/prod/query/answer-train/', [ diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php index 2a94180c1f..be53404972 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/RecordsTest.php @@ -103,7 +103,8 @@ class RecordsTest extends \PhraseanetAuthenticatedWebTestCase $options = new SearchEngineOptions(); $acl = $app->getAclForUser($app->getAuthenticatedUser()); - $options->onCollections($acl->get_granted_base()); + $searchableBasesIds = $acl->getSearchableBasesIds(); + $options->onBasesIds($searchableBasesIds); $serializedOptions = $options->serialize(); $response = $this->XMLHTTPRequest('POST', '/prod/records/', [ diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php index 7ebe080917..be10c81432 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/SearchEngineOptionsTest.php @@ -3,6 +3,7 @@ namespace Alchemy\Tests\Phrasea\SearchEngine; use Alchemy\Phrasea\Application; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\SearchEngine\SearchEngineOptions; use Symfony\Component\HttpFoundation\Request; @@ -20,14 +21,13 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase /** @var Application $app */ $app = self::$DI['app']; /** @var \collection $collection */ - $collection = self::$DI['collection']; - $collections[$collection->get_base_id()] = $collection; + $collection = $this->getCollection(); $options = new SearchEngineOptions($app); - $options->onCollections($collections); + $options->onBasesIds([$collection->get_base_id()]); $options->setRecordType(SearchEngineOptions::TYPE_ALL); $options->setSearchType(SearchEngineOptions::RECORD_RECORD); - $options->allowBusinessFieldsOn($collections); + $options->allowBusinessFieldsOn([$collection->get_base_id()]); foreach ($collection->get_databox()->get_meta_structure() as $field) { $options->setFields([$field]); @@ -57,6 +57,9 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase $app = self::$DI['app']; $this->authenticate($app); + /** @var \collection $collection */ + $collection = self::$DI['collection']; + $sbid = $collection->get_sbas_id(); foreach ($this->provideRequestData() as $pack) { list ($query, $request, $field, $dateField) = $pack; @@ -65,9 +68,12 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase $options = SearchEngineOptions::fromRequest($app, $httpRequest); // Check done this way because returned array can be indexed differently - $collections = $options->getCollections(); - $this->assertCount(1, $collections); - $this->assertContains(self::$DI['collection'], $collections); + $collectionsReferences = $options->getCollectionsReferencesByDatabox(); + $this->assertCount(1, $collectionsReferences); + $this->assertCount(1, $collectionsReferences[$sbid]); + /** @var CollectionReference $collRef */ + $collRef = $collectionsReferences[$sbid][0]; + $this->assertEquals($collection->get_base_id(), $collRef->getBaseId()); $this->assertEquals([$field], $options->getFields()); $this->assertEquals('video', $options->getRecordType()); $this->assertEquals('1', $options->getSearchType()); @@ -99,7 +105,7 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase $options = SearchEngineOptions::fromRequest(self::$DI['app'], $httpRequest); - $this->assertEquals([], $options->getCollections()); + $this->assertEquals([], $options->getCollectionsReferencesByDatabox()); $this->assertEquals([], $options->getFields()); $this->assertEquals('video', $options->getRecordType()); $this->assertEquals('1', $options->getSearchType()); @@ -119,7 +125,7 @@ class SearchEngineOptionsTest extends \PhraseanetTestCase { $options = SearchEngineOptions::fromRequest(self::$DI['app'], new Request()); - $this->assertEquals([], $options->getCollections()); + $this->assertEquals([], $options->getCollectionsReferencesByDatabox()); $this->assertEquals([], $options->getFields()); $this->assertEquals(null, $options->getRecordType()); $this->assertEquals('0', $options->getSearchType()); diff --git a/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/LimitedStructureTest.php b/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/LimitedStructureTest.php index 174b5bef6a..e16cb2e376 100644 --- a/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/LimitedStructureTest.php +++ b/tests/Alchemy/Tests/Phrasea/SearchEngine/Structure/LimitedStructureTest.php @@ -33,7 +33,7 @@ class LimitedStructureTest extends \PHPUnit_Framework_TestCase { $wrapped = $this->prophesize(Structure::class); $options = $this->prophesize(SearchEngineOptions::class); - $options->getBusinessFieldsOn()->willReturn([$this->getCollectionStub(2)]); + $options->getBusinessFieldsOn()->willReturn([2]); $structure = new LimitedStructure($wrapped->reveal(), $options->reveal()); $wrapped->get('foo') @@ -58,10 +58,7 @@ class LimitedStructureTest extends \PHPUnit_Framework_TestCase public function testGetAllFields() { $options = $this->prophesize(SearchEngineOptions::class); - $options->getBusinessFieldsOn()->willReturn([ - $this->getCollectionStub(1), - $this->getCollectionStub(3) - ]); + $options->getBusinessFieldsOn()->willReturn([1, 3]); $wrapped = $this->prophesize(Structure::class); $structure = new LimitedStructure($wrapped->reveal(), $options->reveal()); diff --git a/tests/classes/PhraseanetTestCase.php b/tests/classes/PhraseanetTestCase.php index 3dd2af9cfe..10a121af87 100644 --- a/tests/classes/PhraseanetTestCase.php +++ b/tests/classes/PhraseanetTestCase.php @@ -5,6 +5,7 @@ use Alchemy\Phrasea\Authentication\ACLProvider; use Alchemy\Phrasea\Border\File; use Alchemy\Phrasea\Cache\Manager as CacheManager; use Alchemy\Phrasea\CLI; +use Alchemy\Phrasea\Collection\Reference\CollectionReference; use Alchemy\Phrasea\Model\Entities\Session; use Alchemy\Phrasea\Model\Entities\User; use Alchemy\Phrasea\SearchEngine\SearchEngineInterface; @@ -199,6 +200,12 @@ abstract class PhraseanetTestCase extends WebTestCase return collection::getByBaseId($DI['app'], self::$fixtureIds['collection']['coll']); }); + self::$DI['collectionReference'] = self::$DI->share(function ($DI) { + /** @var \Alchemy\Phrasea\Collection\Reference\DbalCollectionReferenceRepository $repo */ + $repo = self::$DI['app']['repo.collection-references']; + return $repo->find(self::$fixtureIds['collection']['coll']); + }); + self::$DI['collection_no_access'] = self::$DI->share(function ($DI) { return collection::getByBaseId($DI['app'], self::$fixtureIds['collection']['coll_no_access']); }); @@ -329,6 +336,14 @@ abstract class PhraseanetTestCase extends WebTestCase return self::$DI['collection']; } + /** + * @return CollectionReference + */ + public function getCollectionReference() + { + return self::$DI['collectionReference']; + } + public static function tearDownAfterClass() { gc_collect_cycles(); diff --git a/tests/classes/databox/descriptionStructureTest.php b/tests/classes/databox/descriptionStructureTest.php index 618cf2e40f..cce3bfd9cc 100644 --- a/tests/classes/databox/descriptionStructureTest.php +++ b/tests/classes/databox/descriptionStructureTest.php @@ -8,7 +8,7 @@ class databox_descriptionStructureTest extends \PhraseanetTestCase { public function testToArray() { - $structure = new \databox_descriptionStructure(); + $structure = new \databox_descriptionStructure([], new unicode()); $array = ['name1' => 'value1', 'name2' => 'value2']; diff --git a/tests/classes/media/subdefTest.php b/tests/classes/media/subdefTest.php index f097da6a7b..d59b2c2834 100644 --- a/tests/classes/media/subdefTest.php +++ b/tests/classes/media/subdefTest.php @@ -279,8 +279,13 @@ class media_subdefTest extends \PhraseanetTestCase self::$objectPresent->rotate(90, self::$DI['app']['media-alchemyst'], self::$DI['app']['mediavorus']); - $this->assertEquals($width_before, self::$objectPresent->get_height()); - $this->assertEquals($height_before, self::$objectPresent->get_width()); + // because rotate may cause round errors we check with +-1? + + $this->assertGreaterThanOrEqual($width_before-1, self::$objectPresent->get_height()); + $this->assertLessThanOrEqual($width_before+1, self::$objectPresent->get_height()); + + $this->assertGreaterThanOrEqual($height_before-1, self::$objectPresent->get_width()); + $this->assertLessThanOrEqual($height_before+1, self::$objectPresent->get_width()); } /**