Merge branch 4.0

This commit is contained in:
Thibaud Fabre
2016-12-05 15:26:48 +01:00
parent f6700fbe12
commit b5bbb1851f
315 changed files with 6552 additions and 29445 deletions

View File

@@ -1,11 +1,5 @@
language: php
sudo: require
env:
- TEST_SUITE=1
- TEST_SUITE=2
php:
- 5.5
- 5.6
branches:
except:
@@ -14,14 +8,33 @@ branches:
matrix:
fast_finish: true
allow_failures:
- php: 7.0
env: TEST_SUITE=1
- php: 7.0
env: TEST_SUITE=2
include:
- php: 5.5
env: TEST_SUITE=1
- php: 5.6
env: TEST_SUITE=1
- php: 7.0
env: TEST_SUITE=1
- php: 5.5
env: TEST_SUITE=2
- php: 5.6
env: TEST_SUITE=2
- php: 7.0
env: TEST_SUITE=2
services:
- mysql
- memcached
- redis
- rabbitmq
before_install:
- nvm install 0.12.0
- nvm install stable
- phpenv config-rm xdebug.ini
- composer self-update --no-progress --no-interaction
- sudo apt-get purge elasticsearch
@@ -30,12 +43,15 @@ before_install:
- sudo service elasticsearch start
- echo 'session.cache_limiter = ""' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- echo 'extension="redis.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- echo 'extension="memcache.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- if [[ $TRAVIS_PHP_VERSION = 5.* ]];then echo 'extension="memcache.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
- echo 'extension="memcached.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- echo "extension=zmq.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- yes | pecl install imagick
- yes | pecl install imagick-beta
- sudo apt-get install librabbitmq-dev
- pecl install amqp-1.4.0
install:
- travis_retry composer install --no-progress --no-interaction --optimize-autoloader
- if [[ $TRAVIS_PHP_VERSION = 5.* ]];then travis_retry composer install --no-progress --no-interaction --optimize-autoloader; fi;
- if [[ $TRAVIS_PHP_VERSION = 7.* ]];then travis_retry composer update --no-progress --no-interaction --optimize-autoloader; fi;
- travis_retry npm install
before_script:
- mysql -e 'CREATE DATABASE update39_test;CREATE DATABASE ab_test;CREATE DATABASE db_test;SET @@global.sql_mode=STRICT_ALL_TABLES;SET @@global.max_allowed_packet=33554432;SET @@global.wait_timeout=999999;'
@@ -51,7 +67,6 @@ before_script:
- "./bin/developer phraseanet:generate-js-fixtures"
script:
- "./node_modules/.bin/gulp test"
- |
echo "$TEST_SUITE";
case "$TEST_SUITE" in

View File

@@ -30,7 +30,3 @@ See https://docs.phraseanet.com/Devel/
#License :
Phraseanet is licensed under GPL-v3 license.
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/alchemy-fr/phraseanet/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

View File

@@ -11,15 +11,22 @@
namespace KonsoleKommander;
use Alchemy\Phrasea\CLI;
use Alchemy\Phrasea\Command\BuildMissingSubdefs;
use Alchemy\Phrasea\Command\BuildSubdefs;
use Alchemy\Phrasea\Command\CheckConfig;
use Alchemy\Phrasea\Command\Compile\Configuration;
use Alchemy\Phrasea\Command\CreateCollection;
use Alchemy\Phrasea\Command\MailTest;
use Alchemy\Phrasea\Command\Plugin\AddPlugin;
use Alchemy\Phrasea\Command\Plugin\ListPlugin;
use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper;
use Alchemy\Phrasea\Command\Setup\H264MappingGenerator;
use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
use Alchemy\Phrasea\Command\RecordAdd;
use Alchemy\Phrasea\Command\RescanTechnicalDatas;
use Alchemy\Phrasea\Command\SearchEngine\Debug\QueryParseCommand;
use Alchemy\Phrasea\Command\SearchEngine\Debug\QuerySampleCommand;
use Alchemy\Phrasea\Command\SearchEngine\IndexCreateCommand;
use Alchemy\Phrasea\Command\SearchEngine\IndexDropCommand;
use Alchemy\Phrasea\Command\SearchEngine\MappingUpdateCommand;
use Alchemy\Phrasea\Command\SearchEngine\IndexPopulateCommand;
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
use Alchemy\Phrasea\Command\WebsocketServer;
@@ -37,17 +44,23 @@ use Alchemy\Phrasea\Command\Plugin\AddPlugin;
use Alchemy\Phrasea\Command\Plugin\RemovePlugin;
use Alchemy\Phrasea\Command\CheckConfig;
use Alchemy\Phrasea\Command\Setup\XSendFileMappingGenerator;
use Alchemy\Phrasea\Command\SearchEngine\MappingUpdateCommand;
use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper;
use Alchemy\Phrasea\Command\Setup\H264MappingGenerator;
use Alchemy\Phrasea\Command\Setup\XSendFileConfigurationDumper;
use Alchemy\Phrasea\Command\Task\SchedulerResumeTasks;
use Alchemy\Phrasea\Command\Task\SchedulerState;
use Alchemy\Phrasea\Command\Setup\XSendFileMappingGenerator;
use Alchemy\Phrasea\Command\Task\SchedulerPauseTasks;
use Alchemy\Phrasea\Command\Task\SchedulerResumeTasks;
use Alchemy\Phrasea\Command\Task\SchedulerRun;
use Alchemy\Phrasea\Command\Task\SchedulerState;
use Alchemy\Phrasea\Command\Task\TaskList;
use Alchemy\Phrasea\Command\Task\TaskRun;
use Alchemy\Phrasea\Command\Task\TaskStart;
use Alchemy\Phrasea\Command\Task\TaskStop;
use Alchemy\Phrasea\Command\Task\TaskState;
use Alchemy\Phrasea\SearchEngine\SearchEngineInterface;
use Alchemy\Phrasea\Command\Task\TaskStop;
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
use Alchemy\Phrasea\Command\UpgradeDBDatas;
use Alchemy\Phrasea\Core\Version;
require_once __DIR__ . '/../lib/autoload.php';
@@ -68,11 +81,12 @@ $cli = new CLI("
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions; type `about:license' for details.\n\n"
. ' KONSOLE KOMMANDER', $version->getName() . ' ' . $version->getNumber());
. ' KONSOLE KOMMANDER', $version->getName() . ' ' . $version->getNumber());
if (!$cli['phraseanet.configuration-tester']->isInstalled()) {
throw new \RuntimeException('Phraseanet is not installed, use setup command instead');
}
if (!$cli['phraseanet.configuration-tester']->isUpToDate()) {
throw new \RuntimeException('Phraseanet is not up-to-date, use setup command instead');
}
@@ -132,6 +146,10 @@ $cli->command(new QueryParseCommand());
$cli->command(new QuerySampleCommand());
$cli->command(new FindConceptsCommand());
$cli->command($cli['alchemy_worker.commands.run_dispatcher_command']);
$cli->command($cli['alchemy_worker.commands.run_worker_command']);
$cli->command($cli['alchemy_worker.commands.show_configuration']);
$cli->loadPlugins();
exit(is_int($cli->run()) ? : 1);
$cli->run();

View File

@@ -122,4 +122,4 @@ if ($cli['configuration.store']->isSetup()) {
}
}
exit(is_int($cli->run()) ? : 1);
$cli->run();

View File

@@ -79,4 +79,4 @@ $app->command(new CrossDomainGenerator());
$app['phraseanet.setup_mode'] = true;
exit(is_int($app->run()) ? : 1);
$app->run();

View File

@@ -27,7 +27,7 @@
"bootstrap-sass": "v2.3.2.2",
"jquery.lazyload": "~1.9.7",
"jquery-treeview": "~1.4.2",
"alchemy-embed-medias": "~0.3.2"
"html5shiv": "^3.7.3"
},
"devDependencies": {
"mocha": "latest",

View File

@@ -9,11 +9,12 @@ machine:
php:
version: 5.5.21
node:
version: 0.12.0
version: stable
services:
- memcached
- redis
- mysql
- rabbitmq-server
dependencies:
cache_directories:
@@ -21,10 +22,13 @@ dependencies:
- node_modules
- ~/.composer
pre:
- sudo apt-get install librabbitmq-dev
- pecl install amqp-1.4.0
- yes '' | pecl install imagick
- pecl install json
- yes '' | pecl install zmq-beta
- sed -i 's/^\(session.cache_limiter = \).*/\1""/' ~/.phpenv/versions/$(phpenv global)/etc/php.ini
- npm rebuild node-sass
override:
- composer install --no-progress --no-interaction --optimize-autoloader
post:
@@ -32,7 +36,6 @@ dependencies:
- if [[ ! -e elasticsearch-1.6.0 ]]; then wget --no-check-certificate https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.6.0.tar.gz && tar -xvf elasticsearch-1.6.0.tar.gz && elasticsearch-1.6.0/bin/plugin install elasticsearch/elasticsearch-analysis-icu/2.6.0; fi
- elasticsearch-1.6.0/bin/elasticsearch: {background: true}
database:
override:
- mysql -u ubuntu -e 'CREATE DATABASE update39_test;CREATE DATABASE ab_test;CREATE DATABASE db_test;SET @@global.sql_mode=STRICT_ALL_TABLES;SET @@global.max_allowed_packet=33554432;SET @@global.wait_timeout=999999;';
@@ -46,5 +49,5 @@ database:
test:
override:
- case $CIRCLE_NODE_INDEX in 0) EXIT=0; php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-unit.xml --exclude-group legacy || EXIT=$?; ./node_modules/.bin/gulp test || EXIT=$?; php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-legacy-no-web.xml --group legacy --exclude-group web || EXIT=$?; exit $EXIT;; 1) php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-legacy-web.xml --group web ;; esac:
- case $CIRCLE_NODE_INDEX in 0) EXIT=0; php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-unit.xml --exclude-group legacy || EXIT=$?; php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-legacy-no-web.xml --group legacy --exclude-group web || EXIT=$?; exit $EXIT;; 1) php -d memory_limit=-1 bin/phpunit --log-junit $CIRCLE_TEST_REPORTS/phpunit/junit-legacy-web.xml --group web ;; esac:
parallel: true

View File

@@ -1,125 +1,123 @@
{
"name": "phraseanet/phraseanet",
"description": "Phraseanet",
"license": "GPL-3.0",
"config": {
"bin-dir": "bin/"
"name": "phraseanet/phraseanet",
"description": "Phraseanet",
"license": "GPL-3.0",
"config": {
"bin-dir": "bin/"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/tcpdf-clone"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/tcpdf-clone"
},
{
"type": "git",
"url": "https://github.com/romainneutron/ProcessManager.git"
},
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/imagine"
},
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/JMSTranslationBundle"
},
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/embed-bundle.git"
},
{
"type": "git",
"url": "https://github.com/bburnichon/fractal.git"
}
],
"require": {
"php": ">=5.5.9",
"ext-intl": "*",
"alchemy-fr/tcpdf-clone": "~6.0",
"alchemy/embed-bundle": "^0.3.2",
"alchemy/geonames-api-consumer": "~0.1.0",
"alchemy/google-plus-api-client": "~0.6.2",
"alchemy/mediavorus": "^0.4.4",
"alchemy/oauth2php": "1.0.0",
"alchemy/phlickr": "0.2.9",
"alchemy/phpexiftool": "^0.5.0",
"alchemy/rest-bundle": "^0.0.5",
"alchemy/symfony-cors": "^0.1.0",
"alchemy/task-manager": "2.0.x-dev@dev",
"alchemy/zippy": "^0.3.0",
"beberlei/assert": "^2.3",
"cocur/slugify": "^2.0",
"dailymotion/sdk": "~1.5",
"data-uri/data-uri": "~0.1.0",
"dflydev/doctrine-orm-service-provider": "~1.0",
"doctrine/cache": "1.6.x-dev",
"doctrine/dbal": "^2.4.0",
"doctrine/migrations": "^1.0.0",
"doctrine/orm": "^2.4.0",
"elasticsearch/elasticsearch": "~2.0",
"facebook/php-sdk": "~3.0",
"firebase/php-jwt": "^3.0.0",
"gedmo/doctrine-extensions": "~2.3.0",
"goodby/csv": "^1.3.0",
"guzzle/guzzle": "~3.0",
"hoa/compiler": "~2.0",
"hoa/console": "~2.0",
"hoa/dispatcher": "~0.0",
"hoa/router": "~2.0",
"igorw/get-in": "~1.0",
"imagine/imagine": "0.6.x-dev",
"ircmaxell/random-lib": "~1.0",
"jms/serializer": "~0.10",
"jms/translation-bundle": "dev-rebase-2015-10-20",
"justinrainbow/json-schema": "2.0.3 as 1.6.1",
"league/flysystem": "^1.0",
"league/flysystem-aws-s3-v2": "^1.0",
"league/fractal": "dev-bug/null-resource-serialization#891856f as 0.13.0",
"media-alchemyst/media-alchemyst": "^0.5",
"monolog/monolog": "~1.3",
"mrclay/minify": "~2.1.6",
"neutron/process-manager": "2.0.x-dev@dev",
"neutron/recaptcha": "~0.1.0",
"neutron/silex-filesystem-provider": "~1.0",
"neutron/silex-imagine-provider": "~0.1.0",
"neutron/temporary-filesystem": "~2.1",
"pagerfanta/pagerfanta": "^1.0",
"php-ffmpeg/php-ffmpeg": "~0.5.0",
"php-xpdf/php-xpdf": "~0.2.1",
"phpexiftool/exiftool": "10.07",
"ramsey/uuid": "^3.0",
"roave/security-advisories": "dev-master",
"silex/silex": "^1.3.0",
"silex/web-profiler": "~1.0",
"simple-bus/doctrine-orm-bridge": "^4.0",
"simple-bus/jms-serializer-bridge": "^1.0",
"simple-bus/message-bus": "^2.1",
"simple-bus/serialization": "^2.0",
"sorien/silex-dbal-profiler": "^1.1",
"sorien/silex-pimple-dumper": "^1.0",
"swiftmailer/swiftmailer": "~5.3.0",
"symfony/symfony": "~2.7.10|~2.8.3",
"themattharris/tmhoauth": "~0.7",
"twig/extensions": "^1.2.0",
"twig/twig": "~1.14, >=1.14.2",
"vierbergenlars/php-semver": "~2.1",
"webmozart/json": "^1.1",
"willdurand/negotiation": "^2.0.0-alpha1",
"zend/gdata": "~1.12.1"
{
"type": "git",
"url": "https://github.com/romainneutron/ProcessManager.git"
},
"require-dev": {
"phpunit/phpunit": "~4.5",
"mikey179/vfsStream": "~1.5"
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/imagine"
},
"autoload": {
"psr-0": {
"Alchemy\\": "lib",
"": "lib/classes"
}
{
"type": "vcs",
"url": "https://github.com/alchemy-fr/JMSTranslationBundle"
},
"include-path": ["vendor/zend/gdata/library"],
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
{
"type": "git",
"url": "https://github.com/bburnichon/fractal.git"
}
],
"require": {
"php": ">=5.5.9",
"ext-intl": "*",
"alchemy-fr/tcpdf-clone": "~6.0",
"alchemy/embed-bundle": "^0.4.1",
"alchemy/geonames-api-consumer": "~0.1.0",
"alchemy/google-plus-api-client": "~0.6.2",
"alchemy/mediavorus": "^0.4.4",
"alchemy/oauth2php": "1.0.0",
"alchemy/phlickr": "0.2.9",
"alchemy/phpexiftool": "^0.5.0",
"alchemy/rest-bundle": "^0.0.5",
"alchemy/symfony-cors": "^0.1.0",
"alchemy/task-manager": "2.0.x-dev@dev",
"alchemy/zippy": "^0.3.0",
"beberlei/assert": "^2.3",
"cocur/slugify": "^2.0",
"dailymotion/sdk": "~1.5",
"data-uri/data-uri": "~0.1.0",
"dflydev/doctrine-orm-service-provider": "~1.0",
"doctrine/cache": "1.6.x-dev",
"doctrine/dbal": "^2.4.0",
"doctrine/migrations": "^1.0.0",
"doctrine/orm": "^2.4.0",
"elasticsearch/elasticsearch": "~2.0",
"facebook/php-sdk": "~3.0",
"firebase/php-jwt": "^3.0.0",
"gedmo/doctrine-extensions": "~2.3.0",
"goodby/csv": "^1.3.0",
"guzzle/guzzle": "~3.0",
"hoa/compiler": "~2.0",
"hoa/console": "~2.0",
"hoa/dispatcher": "~0.0",
"hoa/router": "~2.0",
"igorw/get-in": "~1.0",
"imagine/imagine": "0.6.x-dev",
"ircmaxell/random-lib": "~1.0",
"jms/serializer": "~0.10",
"jms/translation-bundle": "dev-rebase-2015-10-20",
"justinrainbow/json-schema": "2.0.3 as 1.6.1",
"league/flysystem": "^1.0",
"league/flysystem-aws-s3-v2": "^1.0",
"league/fractal": "dev-bug/null-resource-serialization#891856f as 0.13.0",
"media-alchemyst/media-alchemyst": "^0.5",
"monolog/monolog": "~1.3",
"mrclay/minify": "~2.1.6",
"neutron/process-manager": "2.0.x-dev@dev",
"neutron/recaptcha": "~0.1.0",
"neutron/silex-filesystem-provider": "~1.0",
"neutron/silex-imagine-provider": "~0.1.0",
"neutron/temporary-filesystem": "~2.1",
"pagerfanta/pagerfanta": "^1.0",
"php-ffmpeg/php-ffmpeg": "~0.5.0",
"php-xpdf/php-xpdf": "~0.2.1",
"phpexiftool/exiftool": "10.07",
"ramsey/uuid": "^3.0",
"roave/security-advisories": "dev-master",
"silex/silex": "^1.3.0",
"silex/web-profiler": "~1.0",
"simple-bus/doctrine-orm-bridge": "^4.0",
"simple-bus/jms-serializer-bridge": "^1.0",
"simple-bus/message-bus": "^2.1",
"simple-bus/serialization": "^2.0",
"sorien/silex-dbal-profiler": "^1.1",
"sorien/silex-pimple-dumper": "^1.0",
"swiftmailer/swiftmailer": "~5.3.0",
"symfony/symfony": "~2.7.10|~2.8.3",
"themattharris/tmhoauth": "~0.7",
"twig/extensions": "^1.2.0",
"twig/twig": "~1.14, >=1.14.2",
"vierbergenlars/php-semver": "~2.1",
"webmozart/json": "^1.1",
"willdurand/negotiation": "^2.0.0-alpha1",
"zend/gdata": "~1.12.1",
"alchemy/worker-bundle": "^0.1.5",
"alchemy/queue-bundle": "^0.1.5"
},
"require-dev": {
"mikey179/vfsStream": "~1.5",
"phpunit/phpunit": "^4.8|^5.0"
},
"autoload": {
"psr-0": {
"Alchemy\\": "lib",
"": "lib/classes"
}
},
"include-path": ["vendor/zend/gdata/library"],
"extra": {
"branch-alias": {
"dev-master": "4.1.x-dev"
}
}
}

1206
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -236,3 +236,16 @@ embed_bundle:
document:
player: flexpaper
enable-pdfjs: true
geocoding-providers:
-
name: 'mapBox'
public-key: ''
workers:
queue:
worker-queue:
registry: alchemy_worker.queue_registry
host: localhost
port: 5672
user: guest
password: guest
vhost: /

View File

@@ -73,6 +73,7 @@ use Alchemy\Phrasea\Core\Provider\TasksServiceProvider;
use Alchemy\Phrasea\Core\Provider\TokensServiceProvider;
use Alchemy\Phrasea\Core\Provider\UnicodeServiceProvider;
use Alchemy\Phrasea\Core\Provider\WebhookServiceProvider;
use Alchemy\Phrasea\Core\Provider\WorkerConfigurationServiceProvider;
use Alchemy\Phrasea\Core\Provider\ZippyServiceProvider;
use Alchemy\Phrasea\Core\Provider\WebProfilerServiceProvider as PhraseaWebProfilerServiceProvider;
use Alchemy\Phrasea\Databox\Caption\CaptionServiceProvider;
@@ -86,6 +87,8 @@ use Alchemy\Phrasea\Media\MediaAccessorResolver;
use Alchemy\Phrasea\Media\PermalinkMediaResolver;
use Alchemy\Phrasea\Media\TechnicalDataServiceProvider;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\QueueProvider\QueueServiceProvider;
use Alchemy\WorkerProvider\WorkerServiceProvider;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use MediaVorus\Media\MediaInterface;
use MediaVorus\MediaVorus;
@@ -243,6 +246,10 @@ class Application extends SilexApplication
$this->setupEventDispatcher();
$this->register(new DataboxServiceProvider());
$this->register(new QueueServiceProvider());
$this->register(new WorkerServiceProvider());
$this->register(new WorkerConfigurationServiceProvider());
$this->register(new OrderServiceProvider());
$this->register(new WebhookServiceProvider());

View File

@@ -178,7 +178,7 @@ class GooglePlus extends AbstractProvider
$token = @json_decode($this->client->getAccessToken(), true);
if (JSON_ERROR_NONE !== json_last_error()) {
throw new NotAuthenticatedException('Unable to parse Google+ JSON', $e->getCode(), $e);
throw new NotAuthenticatedException('Unable to parse Google+ JSON');
}
$ticket = $this->client->verifyIdToken($token['id_token']);

View File

@@ -23,6 +23,7 @@ use Alchemy\Phrasea\Core\CLIProvider\DoctrineMigrationServiceProvider;
use Alchemy\Phrasea\Core\CLIProvider\PluginServiceProvider;
use Alchemy\Phrasea\Core\CLIProvider\SignalHandlerServiceProvider;
use Alchemy\Phrasea\Core\CLIProvider\TaskManagerServiceProvider;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -72,6 +73,10 @@ class CLI extends Application
$this->bindRoutes();
$this['logger'] = $this->extend('logger', function () {
return new Console\Logger\ConsoleLogger(new Console\Output\ConsoleOutput(Console\Output\ConsoleOutput::VERBOSITY_DEBUG));
});
error_reporting(-1);
ErrorHandler::register();
PhraseaCLIExceptionHandler::register();
@@ -87,6 +92,7 @@ class CLI extends Application
$this->boot();
$app = $this['console'];
if ($interactive) {
$app = new Console\Shell($app);
}
@@ -115,11 +121,14 @@ class CLI extends Application
*
* If a command with the same name already exists, it will be overridden.
*
* @param CommandInterface $command A Command object
* @param Command|CommandInterface $command A Command object
*/
public function command(CommandInterface $command)
public function command($command)
{
$command->setContainer($this);
if ($command instanceof CommandInterface) {
$command->setContainer($this);
}
$this['console']->add($command);
}

View File

@@ -139,7 +139,6 @@ class CollectionService
. "WHERE r.coll_id = :coll_id\n"
. "AND r.type='image' AND s.name IN ('preview', 'document')";
$params = [':coll_id' => $collection->getCollectionId()];
if ($record_id) {

View File

@@ -24,6 +24,8 @@ interface CommandInterface
/**
* Factory for the command.
*
* @deprecated Will be removed in a future release.
*/
public static function create();
}

View File

@@ -26,7 +26,7 @@ class Configuration extends Command
protected function doExecute(InputInterface $input, OutputInterface $output)
{
$this->container['configuration.store']->compileAndWrite();
$output->writeln("Confguration compiled.");
$output->writeln("Configuration compiled.");
return 0;
}

View File

@@ -210,7 +210,7 @@ class FieldsController extends Controller
$this->validateTagField($data);
try {
$field = \databox_field::create($this->app, $databox, $data['name'], $data['multi']);
$field = \databox_field::create($this->app, $databox, $data['name']);
$this->updateFieldWithData($field, $data);
$field->save();
} catch (\Exception $e) {
@@ -308,6 +308,7 @@ class FieldsController extends Controller
->set_business($data['business'])
->set_aggregable($data['aggregable'])
->set_indexable($data['indexable'])
->set_multi($data['multi'])
->set_required($data['required'])
->set_separator($data['separator'])
->set_readonly($data['readonly'])

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Controller\Admin;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Databox\SubdefGroup;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -25,7 +26,7 @@ class SubdefsController extends Controller
return $this->render('admin/subdefs.html.twig', [
'databox' => $databox,
'subdefs' => $databox->get_subdef_structure()
'subdefs' => $databox->get_subdef_structure(),
]);
}
@@ -70,6 +71,8 @@ class SubdefsController extends Controller
} else {
$subdefs = $databox->get_subdef_structure();
$this->updateSubdefGroups($subdefs, $request);
foreach ($Parmsubdefs as $post_sub) {
$options = [];
@@ -80,6 +83,7 @@ class SubdefsController extends Controller
$class = $request->request->get($post_sub . '_class');
$downloadable = $request->request->get($post_sub . '_downloadable');
$orderable = $request->request->get($post_sub . '_orderable');
$defaults = ['path', 'meta', 'mediatype'];
@@ -107,7 +111,7 @@ class SubdefsController extends Controller
$labels = $request->request->get($post_sub . '_label', []);
$subdefs->set_subdef($group, $name, $class, $downloadable, $options, $labels);
$subdefs->set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable);
}
}
@@ -115,4 +119,37 @@ class SubdefsController extends Controller
'sbas_id' => $databox->get_sbas_id(),
]);
}
/**
* Update Databox subdefsStructure DOM according to defined groups.
*
* @param \databox_subdefsStructure $subdefs
* @param Request $request
*/
private function updateSubdefGroups(\databox_subdefsStructure $subdefs, Request $request)
{
$subdefsGroups = $request->request->get('subdefsgroups', []);
$changedGroups = [];
/** @var SubdefGroup $subdefsGroup */
foreach ($subdefs as $groupName => $subdefsGroup) {
$documentOrderable = isset($subdefsGroups[$groupName]['document_orderable'])
? \p4field::isyes($subdefsGroups[$groupName]['document_orderable'])
: false;
if ($subdefsGroup->isDocumentOrderable() !== $documentOrderable) {
if ($documentOrderable) {
$subdefsGroup->allowDocumentOrdering();
} else {
$subdefsGroup->disallowDocumentOrdering();
}
$changedGroups[] = $subdefsGroup;
}
}
if ($changedGroups) {
$subdefs->updateSubdefGroups($changedGroups);
}
}
}

View File

@@ -8,10 +8,12 @@
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Controller\Api;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Model\Manipulator\LazaretManipulator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class LazaretController extends Controller
{
/**
@@ -23,7 +25,9 @@ class LazaretController extends Controller
{
/** @var LazaretManipulator $lazaretManipulator */
$lazaretManipulator = $this->app['manipulator.lazaret'];
$ret = $lazaretManipulator->deny($lazaret_id);
return Result::create($request, $ret)->createResponse();
}
@@ -31,7 +35,9 @@ class LazaretController extends Controller
{
/** @var LazaretManipulator $lazaretManipulator */
$lazaretManipulator = $this->app['manipulator.lazaret'];
$ret = $lazaretManipulator->add($lazaret_id);
return Result::create($request, $ret)->createResponse();
}
@@ -44,9 +50,12 @@ class LazaretController extends Controller
if( $maxTodo <= 0) {
$maxTodo = -1; // all
}
/** @var LazaretManipulator $lazaretManipulator */
$lazaretManipulator = $this->app['manipulator.lazaret'];
$ret = $lazaretManipulator->clear($maxTodo);
return Result::create($request, $ret)->createResponse();
}
}
}

View File

@@ -85,6 +85,7 @@ 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;

View File

@@ -42,11 +42,7 @@ class LightboxController extends Controller
$repository->findActiveValidationByUser($this->getAuthenticatedUser())
);
$template = $this->isBrowserNewGenerationOrMobile()
? 'lightbox/index.html.twig'
: 'lightbox/IE6/index.html.twig';
return $this->renderResponse($template, [
return $this->renderResponse('lightbox/index.html.twig', [
'baskets_collection' => $basket_collection,
'module_name' => 'Lightbox',
'module' => 'lightbox',
@@ -92,7 +88,6 @@ class LightboxController extends Controller
]);
}
$isNewGenerationBrowser = $this->app['browser']->isNewGeneration();
$basket = $basketElement->getBasket();
$ret = [];
@@ -104,11 +99,11 @@ class LightboxController extends Controller
['record' => $basketElement->getRecord($this->app), 'not_wrapped' => true]
);
$ret['options_html'] = $this->render(
$isNewGenerationBrowser ? 'lightbox/sc_options_box.html.twig' : 'lightbox/IE6/sc_options_box.html.twig',
'lightbox/sc_options_box.html.twig',
['basket_element' => $basketElement]
);
$ret['agreement_html'] = $this->render(
$isNewGenerationBrowser ? 'lightbox/agreement_box.html.twig' : 'lightbox/IE6/agreement_box.html.twig',
'lightbox/agreement_box.html.twig',
['basket' => $basket, 'basket_element' => $basketElement]
);
$ret['selector_html'] = $this->render('lightbox/selector_box.html.twig', ['basket_element' => $basketElement]);
@@ -149,10 +144,7 @@ class LightboxController extends Controller
'record' => $record,
'not_wrapped' => true,
]);
$template_options = $browser->isNewGeneration()
? 'lightbox/feed_options_box.html.twig'
: 'lightbox/IE6/feed_options_box.html.twig';
$ret['options_html'] = $this->render($template_options, ['feed_element' => $item]);
$ret['options_html'] = $this->render('lightbox/feed_options_box.html.twig', ['feed_element' => $item]);
$ret['caption'] = $this->render(
'common/caption.html.twig', [
'view' => 'preview',
@@ -213,7 +205,7 @@ class LightboxController extends Controller
$basket = $this->markBasketRead($basket);
$basket = $this->markBasketUserAwareOfValidation($basket);
$response = $this->renderResponse($this->getValidationTemplate(), [
'baskets_collection' => $basket_collection,
'basket' => $basket,
@@ -238,28 +230,16 @@ class LightboxController extends Controller
$basket->markRead();
$this->app['orm.em']->flush();
}
return $basket;
}
/**
* @return bool
*/
private function isBrowserNewGenerationOrMobile()
{
/** @var \Browser $browser */
$browser = $this->app['browser'];
return $browser->isNewGeneration() || $browser->isMobile();
}
/**
* @return string
*/
private function getValidationTemplate()
{
return $this->isBrowserNewGenerationOrMobile()
? 'lightbox/validate.html.twig'
: 'lightbox/IE6/validate.html.twig';
return 'lightbox/validate.html.twig';
}
/**
@@ -280,7 +260,7 @@ class LightboxController extends Controller
;
$this->app['orm.em']->flush();
}
return $basket;
}
@@ -300,14 +280,10 @@ class LightboxController extends Controller
/** @var FeedEntry $feed_entry */
$feed_entry = $app['repo.feed-entries']->find($entry_id);
$template = $this->isBrowserNewGenerationOrMobile()
? 'lightbox/feed.html.twig'
: 'lightbox/IE6/feed.html.twig';
$content = $feed_entry->getItems();
$first = $content->first();
$response = $this->renderResponse($template, [
$response = $this->renderResponse('lightbox/feed.html.twig', [
'feed_entry' => $feed_entry,
'first_item' => $first,
'local_title' => $feed_entry->getTitle(),
@@ -359,7 +335,7 @@ class LightboxController extends Controller
return $this->app->json($output);
}
public function ajaxSetElementAgreementAction(Request $request, $sselcont_id)
{
$agreement = $request->request->get('agreement');

View File

@@ -22,6 +22,7 @@ use Alchemy\Phrasea\Model\Entities\Preset;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Model\Manipulator\PresetManipulator;
use Alchemy\Phrasea\Model\Repositories\PresetRepository;
use Alchemy\Phrasea\Twig\PhraseanetExtension;
use Alchemy\Phrasea\Vocabulary\ControlProvider\ControlProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -156,7 +157,9 @@ class EditController extends Controller
];
$elements[$indice]['statbits'] = [];
$elements[$indice]['editableStatus'] = false;
if ($this->getAclForUser()->has_right_on_base($record->getBaseId(), 'chgstatus')) {
$elements[$indice]['editableStatus'] = true;
foreach ($status as $n => $s) {
$tmp_val = substr(strrev($record->getStatus()), $n, 1);
$elements[$indice]['statbits'][$n]['value'] = ($tmp_val == '1') ? '1' : '0';
@@ -206,18 +209,21 @@ class EditController extends Controller
'h' => $thumbnail->get_height(),
];
$elements[$indice]['preview'] = $this->render(
$elements[$indice]['template'] = $this->render(
'common/preview.html.twig',
['record' => $record]
['record' => $record, 'not_wrapped' => true]
);
$elements[$indice]['data'] = $this->getRecordElementData($record);
$elements[$indice]['type'] = $record->getType();
}
}
$conf = $this->getConf();
$params = [
'multipleDataboxes' => $multipleDataboxes,
'recordsRequest' => $records,
'videoEditorConfig' => $conf->get(['video-editor']),
'databox' => $databox,
'JSonStatus' => json_encode($status),
'JSonRecords' => json_encode($elements),
@@ -529,4 +535,35 @@ class EditController extends Controller
return $this->app['manipulator.preset'];
}
/**
* @param \record_adapter $record
* @return array
*/
private function getRecordElementData($record) {
$helpers = new PhraseanetExtension($this->app);
$recordData = [
'databoxId' => $record->getBaseId(),
'id' => $record->getId(),
'isGroup' => $record->isStory(),
'url' => (string)$helpers->getThumbnailUrl($record),
];
$userHaveAccess = $this->app->getAclForUser($this->getAuthenticatedUser())->has_access_to_subdef($record, 'preview');
if ($userHaveAccess) {
$recordPreview = $record->get_preview();
} else {
$recordPreview = $record->get_thumbnail();
}
$recordData['preview'] = [
'width' => $recordPreview->get_width(),
'height' => $recordPreview->get_height(),
'url' => $this->app->url('alchemy_embed_view', [
'url' => (string)($this->getAuthenticatedUser() ? $recordPreview->get_url() : $recordPreview->get_permalink()->get_url()),
'autoplay' => false
])
];
return $recordData;
}
}

View File

@@ -54,7 +54,7 @@ class FeedController extends Controller
$user = $this->getAuthenticatedUser();
$publisher = $this->getFeedPublisherRepository()->findOneBy([
'feed' => $feed,
'feed' => $feed,
'user' => $user,
]);
@@ -148,7 +148,7 @@ class FeedController extends Controller
$entry->setFeed($new_feed);
}
$items = explode(';', $request->request->get('sorted_lst'));
$items = explode(';', $request->request->get('lst'));
$item_repository = $this->getFeedItemRepository();
$manager = $this->getEntityManager();
@@ -167,7 +167,7 @@ class FeedController extends Controller
return $this->app->json([
'error' => false,
'message' => 'succes',
'message' => 'success',
'datas' => $this->render('prod/results/entry.html.twig', ['entry' => $entry]),
]);
}

View File

@@ -17,13 +17,13 @@ class LanguageController
/** @var TranslatorInterface */
private $translator;
private $serverName;
public function __construct(TranslatorInterface $translator, $serverName)
{
$this->translator = $translator;
$this->serverName = $serverName;
}
public function getTranslationsAction()
{
$translator = $this->translator;
@@ -111,6 +111,39 @@ class LanguageController
'toolbox' => $translator->trans('Tool box'),
'print' => $translator->trans('Print'),
'attention' => $translator->trans('Attention !'),
'mapMarkerEdit' => $translator->trans('Edit position'),
'mapMarkerAdd' => $translator->trans('Add a position'),
'mapMarkerMoveLabel' => $translator->trans('Drag and drop the pin to move position'),
'mapMarkerEditCancel' => $translator->trans('Cancel'),
'mapMarkerEditSubmit' => $translator->trans('Submit'),
'Keyboard shortcuts' => $translator->trans('Keyboard shortcuts'),
'Play' => $translator->trans('Play'),
'Change play speed' => $translator->trans('Change play speed'),
'Pause' => $translator->trans('Pause'),
'One frame forward' => $translator->trans('One frame forward'),
'One frame backward' => $translator->trans('One frame backward'),
'Add an entry point' => $translator->trans('Add an entry point'),
'Add an end point' => $translator->trans('Add an end point'),
'Navigate to entry point' => $translator->trans('Navigate to entry point'),
'Navigate to end point' => $translator->trans('Navigate to end point'),
'Delete current' => $translator->trans('Delete current'),
'Toggle loop' => $translator->trans('Toggle loop'),
'Shift' => $translator->trans('Shift'),
'Ctrl' => $translator->trans('Ctrl'),
'Space bar' => $translator->trans('Space bar'),
'or' => $translator->trans('or'),
'Suppr' => $translator->trans('Suppr'),
'Add new range' => $translator->trans('Add new range'),
'Export ranges' => $translator->trans('Export ranges'),
'Start Range' => $translator->trans('Start Range'),
'End Range' => $translator->trans('End Range'),
'Remove current Range' => $translator->trans('Remove current Range'),
'Go to start point' => $translator->trans('Go to start point'),
'Go 1 frame backward' => $translator->trans('Go 1 frame backward'),
'Go 1 frame forward' => $translator->trans('Go 1 frame forward'),
'Go to end point' => $translator->trans('Go to end point'),
'Move up range' => $translator->trans('Move up range'),
'Move down range' => $translator->trans('Move down range'),
]);
}
}

View File

@@ -16,14 +16,11 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
use Alchemy\Phrasea\Application\Helper\FilesystemAware;
use Alchemy\Phrasea\Application\Helper\SubDefinitionSubstituerAware;
use Alchemy\Phrasea\Border;
use Alchemy\Phrasea\Border\Attribute\AttributeInterface;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Http\DeliverDataInterface;
use Alchemy\Phrasea\Media\SubdefSubstituer;
use Alchemy\Phrasea\Model\Entities\LazaretFile;
use Alchemy\Phrasea\Model\Manipulator\LazaretManipulator;
use Alchemy\Phrasea\Model\Repositories\LazaretFileRepository;
use PHPExiftool\Driver\Metadata\Metadata;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

View File

@@ -23,15 +23,34 @@ class MoveCollectionController extends Controller
return $databox->get_sbas_id();
}, $records->databoxes());
$message = '';
$template = '';
$collections = $this->getAclForUser()->get_granted_base(['canaddrecord'], $sbas_ids);
$parameters = [
'records' => $records,
'message' => '',
'collections' => $collections,
if (count($records->databoxes()) > 1) {
$success = false;
$message = $this->app->trans('prod::Les enregistrements ne provienent pas tous de la meme base et ne peuvent donc etre traites ensemble');
} elseif (count($records) == 0) {
$success = false;
$message = $this->app->trans('prod::Vous n\'avez le droit d\'effectuer l\'operation sur aucun document');
} else {
// is able to move:
$success = true;
$parameters = [
'records' => $records,
'message' => '',
'collections' => $collections,
];
$template = $this->render('prod/actions/collection_default.html.twig', $parameters);
}
$datas = [
'success' => $success,
'message' => $message,
'template' => $template
];
return $this->render('prod/actions/collection_default.html.twig', $parameters);
return $this->app->json($datas);
}
public function apply(Request $request)

View File

@@ -97,41 +97,41 @@ class QueryController extends Controller
}
for ($i = 1; ($i <= 4 && (($i <= $npages) === true)); $i++) {
if ($i == $page)
$string .= '<input onkeypress="if(event.keyCode == 13 && !isNaN(parseInt(this.value)))gotopage(parseInt(this.value))" type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini" />';
$string .= '<input type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini search-navigate-input-action" data-initial-value="' . $i . '" data-total-pages="'.$npages.'"/>';
else
$string .= "<a onclick='gotopage(" . $i . ");return false;' class='btn btn-primary btn-mini'>" . $i . "</a>";
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
}
if ($npages > 4)
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini'></a>";
$string .= "<a onclick='gotopage(" . ($npages) . ");return false;' class='btn btn-primary btn-mini' id='last'></a>";
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action" data-page="' . $npages . '" id="last"></a>';
} else {
$start = $npages - 4;
if (($start) > 0){
$string .= "<a onclick='gotopage(1);return false;' class='btn btn-primary btn-mini' id='first'></a>";
$string .= "<a id='PREV_PAGE' class='btn btn-primary btn-mini'></a>";
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="1" id="first"></a>';
$string .= '<a id="PREV_PAGE" class="btn btn-primary btn-mini"></a>';
}else
$start = 1;
for ($i = ($start); $i <= $npages; $i++) {
if ($i == $page)
$string .= '<input onkeypress="if(event.keyCode == 13 && !isNaN(parseInt(this.value)))gotopage(parseInt(this.value))" type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini" />';
$string .= '<input type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini search-navigate-input-action" data-initial-value="' . $i . '" data-total-pages="'.$npages.'" />';
else
$string .= "<a onclick='gotopage(" . $i . ");return false;' class='btn btn-primary btn-mini'>" . $i . "</a>";
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
}
if($page < $npages){
$string .= "<a id='NEXT_PAGE' class='btn btn-primary btn-mini'></a>";
}
}
} else {
$string .= "<a onclick='gotopage(1);return false;' class='btn btn-primary btn-mini' id='first'></a>";
$string .= '<a class="btn btn-primary btn-mini btn-mini search-navigate-action" data-page="1" id="first"></a>';
for ($i = ($page - 2); $i <= ($page + 2); $i++) {
if ($i == $page)
$string .= '<input onkeypress="if(event.keyCode == 13 && !isNaN(parseInt(this.value)))gotopage(parseInt(this.value))" type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini" />';
$string .= '<input type="text" value="' . $i . '" size="' . (strlen((string) $i)) . '" class="btn btn-mini search-navigate-input-action" data-initial-value="' . $i . '" data-total-pages="'.$npages.'" />';
else
$string .= "<a onclick='gotopage(" . $i . ");return false;' class='btn btn-primary btn-mini'>" . $i . "</a>";
$string .= '<a class="btn btn-primary btn-mini search-navigate-action" data-page="'.$i.'">' . $i . '</a>';
}
$string .= "<a onclick='gotopage(" . ($npages) . ");return false;' class='btn btn-primary btn-mini' id='last'></a>";
$string .= '<a href="#" class="btn btn-primary btn-mini search-navigate-action" data-page="' . $npages . '" id="last"></a>';
}
}
$string .= '<div style="display:none;"><div id="NEXT_PAGE"></div><div id="PREV_PAGE"></div></div>';
@@ -152,7 +152,7 @@ class QueryController extends Controller
$infoResult = '<div id="docInfo">'
. $this->app->trans('%number% documents<br/>selectionnes', ['%number%' => '<span id="nbrecsel"></span>'])
. '</div><a href="#" class="infoDialog" infos="' . str_replace('"', '&quot;', $explain) . '">'
. '</div><a href="#" class="infoDialog search-display-info" data-infos="' . str_replace('"', '&quot;', $explain) . '">'
. $this->app->trans('%total% reponses', ['%total%' => '<span>'.$result->getTotal().'</span>']) . '</a>';
$json['infos'] = $infoResult;

View File

@@ -14,9 +14,12 @@ use Alchemy\Phrasea\Application\Helper\EntityManagerAware;
use Alchemy\Phrasea\Application\Helper\SearchEngineAware;
use Alchemy\Phrasea\Controller\Controller;
use Alchemy\Phrasea\Controller\RecordsRequest;
use Alchemy\Phrasea\Model\Entities\BasketElement;
use Alchemy\Phrasea\Model\Repositories\BasketElementRepository;
use Alchemy\Phrasea\Model\Repositories\StoryWZRepository;
use Alchemy\Phrasea\SearchEngine\SearchEngineOptions;
use Alchemy\Phrasea\Twig\Fit;
use Alchemy\Phrasea\Twig\PhraseanetExtension;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -68,16 +71,20 @@ class RecordController extends Controller
$options
);
$currentRecord = $this->getContainerResult($record);
if ($record->is_from_reg()) {
$train = $this->render('prod/preview/reg_train.html.twig', ['record' => $record]);
}
if ($record->is_from_basket() && $reloadTrain) {
} else if ($record->is_from_basket() && $reloadTrain) {
$train = $this->render('prod/preview/basket_train.html.twig', ['record' => $record]);
} else if ($record->is_from_feed()) {
$train = $this->render('prod/preview/feed_train.html.twig', ['record' => $record]);
}
if ($record->is_from_feed()) {
$train = $this->render('prod/preview/feed_train.html.twig', ['record' => $record]);
$recordCaptions = [];
foreach ($record->get_caption()->get_fields(null, true) as $field) {
// get field's values
$recordCaptions[$field->get_name()] = $field->get_serialized_values();
}
return $this->app->json([
@@ -87,6 +94,7 @@ class RecordController extends Controller
'searchEngine' => $searchEngine,
'searchOptions' => $options,
]),
"recordCaptions"=> $recordCaptions,
"html_preview" => $this->render('common/preview.html.twig', [
'record' => $record
]),
@@ -95,6 +103,7 @@ class RecordController extends Controller
'baskets' => $record->get_container_baskets($this->getEntityManager(), $this->getAuthenticatedUser()),
]),
"current" => $train,
"record" => $currentRecord,
"history" => $this->render('prod/preview/short_history.html.twig', [
'record' => $record,
]),
@@ -209,4 +218,38 @@ class RecordController extends Controller
{
return $this->app['repo.story-wz'];
}
/**
* @param \record_preview $recordContainer
* @return array
*/
private function getContainerResult(\record_preview $recordContainer)
{
/* @var $recordPreview \media_subdef */
$helpers = new PhraseanetExtension($this->app);
$recordData = [
'databoxId' => $recordContainer->getBaseId(),
'id' => $recordContainer->getId(),
'isGroup' => $recordContainer->isStory(),
'url' => (string)$helpers->getThumbnailUrl($recordContainer),
];
$userHaveAccess = $this->app->getAclForUser($this->getAuthenticatedUser())->has_access_to_subdef($recordContainer, 'preview');
if ($userHaveAccess) {
$recordPreview = $recordContainer->get_preview();
} else {
$recordPreview = $recordContainer->get_thumbnail();
}
$recordData['preview'] = [
'width' => $recordPreview->get_width(),
'height' => $recordPreview->get_height(),
'url' => $this->app->url('alchemy_embed_view', [
'url' => (string)($this->getAuthenticatedUser() ? $recordPreview->get_url() : $recordPreview->get_permalink()->get_url()),
'autoplay' => false
])
];
return $recordData;
}
}

View File

@@ -45,29 +45,12 @@ class RootController extends Controller
return $this->app->redirectPath('logout');
}
$cssPath = $this->app['root.path'] . '/www/assets/prod/skins';
$css = [];
$finder = new Finder();
/** @var SplFileInfo[] $iterator */
$iterator = $finder
->directories()
->depth(0)
->filter(function (\SplFileInfo $fileinfo) {
return ctype_xdigit($fileinfo->getBasename());
})
->in($cssPath);
foreach ($iterator as $dir) {
$baseName = $dir->getBaseName();
$css[$baseName] = $baseName;
}
$user = $this->getAuthenticatedUser();
$cssfile = $this->getSettings()->getUserSetting($user, 'css');
if (!$cssfile && isset($css['000000'])) {
if (!$cssfile) {
$cssfile = '000000';
}
@@ -76,14 +59,7 @@ class RootController extends Controller
$thjslist = "";
$queries_topics = '';
$conf = $this->getConf();
if ($conf->get(['registry', 'classic', 'render-topics']) == 'popups') {
$queries_topics = \queries::dropdown_topics($this->app['translator'], $this->app['locale']);
} elseif ($conf->get(['registry', 'classic', 'render-topics']) == 'tree') {
$queries_topics = \queries::tree_topics($this->app['locale']);
}
$sbas = $bas2sbas = [];
@@ -126,13 +102,11 @@ class RootController extends Controller
'GV_multiAndReport' => $conf->get(['registry', 'modules', 'stories']),
'GV_thesaurus' => $conf->get(['registry', 'modules', 'thesaurus']),
'cgus_agreement' => \databox_cgu::askAgreement($this->app),
'css' => $css,
'feeds' => $feeds,
'aggregate' => $aggregate,
'GV_google_api' => $conf->get(['registry', 'webservices', 'google-charts-enabled']),
'queries_topics' => $queries_topics,
'geocodingProviders' => $conf->get(['geocoding-providers']),
'search_status' => \databox_status::getSearchStatus($this->app),
'queries_history' => \queries::history($this->app, $user->getId()),
'thesau_js_list' => $thjslist,
'thesau_json_sbas' => json_encode($sbas),
'thesau_json_bas2sbas' => json_encode($bas2sbas),

View File

@@ -95,10 +95,12 @@ class ToolsController extends Controller
}
}
}
$conf = $this->getConf();
return $this->render('prod/actions/Tools/index.html.twig', [
'records' => $records,
'record' => $record,
'videoEditorConfig' => $conf->get(['video-editor']),
'recordSubdefs' => $recordAccessibleSubdefs,
'metadatas' => $metadata,
]);

View File

@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Model\Manipulator\ApiOauthTokenManipulator;
use Alchemy\Phrasea\Model\Repositories\ApiAccountRepository;
use Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository;
use Alchemy\Phrasea\Model\Repositories\ApiOauthTokenRepository;
use Alchemy\Phrasea\Model\Repositories\WebhookEventDeliveryRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -244,8 +245,12 @@ class DeveloperController extends Controller
throw new AccessDeniedHttpException();
}
$deliveries = $this->getWebhookDeliveryRepository()
->findLastDeliveries($account->getApplication(), 10);
return $this->render('developers/application.html.twig', [
"application" => $application,
"deliveries" => $deliveries,
"user" => $user,
"token" => $token,
]);
@@ -298,4 +303,12 @@ class DeveloperController extends Controller
{
return $this->app['repo.api-applications'];
}
/**
* @return WebhookEventDeliveryRepository
*/
private function getWebhookDeliveryRepository()
{
return $this->app['webhook.delivery_repository'];
}
}

View File

@@ -1294,6 +1294,8 @@ class ThesaurusXmlHttpController extends Controller
public function replaceCandidateJson(Request $request)
{
$tsbas = [];
$ret = [
'ctermsDeleted' => [],
'maxRecsUpdatable' => self::SEARCH_REPLACE_MAXREC,
@@ -1302,48 +1304,158 @@ class ThesaurusXmlHttpController extends Controller
'msg' => ''
];
// group ids by base
$tsbas = [];
foreach ($request->get('id') as $id) {
$id = explode('.', $id);
$sbas_id = array_shift($id);
if (!array_key_exists($sbas_id, $tsbas)) {
$tsbas[$sbas_id] = [];
if (!array_key_exists('b' . $sbas_id, $tsbas)) {
$tsbas['b' . $sbas_id] = [
'sbas_id' => (int) $sbas_id,
'tids' => [],
'domct' => null,
'tvals' => [],
'lid' => '',
'trids' => []
];
}
$tsbas[$sbas_id][] = implode('.', $id);
$tsbas['b' . $sbas_id]['tids'][] = implode('.', $id);
}
// loop on bases
foreach ($tsbas as $sbas_id => $sbas) {
// first, count the number of records to update
foreach ($tsbas as $ksbas => $sbas) {
try {
$databox = $this->findDataboxById($sbas_id);
$domct = $databox->get_dom_cterms();
$databox = $this->findDataboxById($sbas['sbas_id']);
$connbas = $databox->get_connection();
$tsbas[$ksbas]['domct'] = $databox->get_dom_cterms();
} catch (\Exception $e) {
continue;
}
if (!$domct) {
if (!$tsbas[$ksbas]['domct']) {
continue;
}
$domct_changed = false;
$xpathct = new \DOMXPath($domct);
$lids = [];
$xpathct = new \DOMXPath($tsbas[$ksbas]['domct']);
foreach ($sbas as $tid) {
foreach ($sbas['tids'] as $tid) {
$xp = '//te[@id="' . $tid . '"]/sy';
$nodes = $xpathct->query($xp);
if ($nodes->length == 1) {
$sy = $nodes->item(0);
$te = $sy->parentNode;
$ret['ctermsDeleted'][] = $sbas_id . '.' . $te->getAttribute('id');
$te->parentNode->removeChild($te);
$domct_changed = true;
$syid = str_replace('.', 'd', $sy->getAttribute('id')) . 'd';
$lids[] = $syid;
$field = $sy->parentNode->parentNode->getAttribute('field');
if (!array_key_exists($field, $tsbas[$ksbas]['tvals'])) {
$tsbas[$ksbas]['tvals'][$field] = [];
}
$tsbas[$ksbas]['tvals'][$field][] = $sy;
}
}
if ($domct_changed && !$request->get('debug')) {
$databox->saveCterms($domct);
if (empty($lids)) {
// no cterm was found
continue;
}
$tsbas[$ksbas]['lid'] = "'" . implode("','", $lids) . "'";
// count records
$sql = 'SELECT DISTINCT record_id AS r'
. ' FROM thit WHERE value IN (:lids)'
. ' ORDER BY record_id';
$stmt = $connbas->prepare($sql);
$stmt->execute(['lids' => $lids]);
$tsbas[$ksbas]['trids'] = $stmt->fetchAll(\PDO::FETCH_COLUMN, 0);
$stmt->closeCursor();
$ret['nRecsToUpdate'] += count($tsbas[$ksbas]['trids']);
}
if ($ret['nRecsToUpdate'] <= self::SEARCH_REPLACE_MAXREC) {
foreach ($tsbas as $sbas) {
try {
$databox = $this->findDataboxById($sbas['sbas_id']);
} catch (\Exception $e) {
continue;
}
// fix caption of records
foreach ($sbas['trids'] as $rid) {
try {
$record = $databox->get_record($rid);
$metadatask = []; // datas to keep
$metadatasd = []; // datas to delete
/* @var $field caption_field */
foreach ($record->get_caption()->get_fields(null, true) as $field) {
$meta_struct_id = $field->get_meta_struct_id();
/* @var $v caption_Field_Value */
$fname = $field->get_name();
if (!array_key_exists($fname, $sbas['tvals'])) {
foreach ($field->get_values() as $v) {
$metadatask[] = [
'meta_struct_id' => $meta_struct_id,
'meta_id' => $v->getId(),
'value' => $v->getValue()
];
}
} else {
foreach ($field->get_values() as $v) {
$keep = true;
$vtxt = $this->getUnicode()->remove_indexer_chars($v->getValue());
/** @var DOMElement $sy */
foreach ($sbas['tvals'][$fname] as $sy) {
if ($sy->getAttribute('w') == $vtxt) {
$keep = false;
}
}
if ($keep) {
$metadatask[] = [
'meta_struct_id' => $meta_struct_id,
'meta_id' => $v->getId(),
'value' => $v->getValue()
];
} else {
$metadatasd[] = [
'meta_struct_id' => $meta_struct_id,
'meta_id' => $v->getId(),
'value' => $request->get('t') ? $request->get('t') : ''
];
}
}
}
}
if (count($metadatasd) > 0) {
if (!$request->get('debug')) {
$record->set_metadatas($metadatasd, true);
$ret['nRecsUpdated']++;
}
}
} catch (\Exception $e) {
continue;
}
}
foreach ($sbas['tvals'] as $tval) {
foreach ($tval as $sy) {
// remove candidate from cterms
$te = $sy->parentNode;
$te->parentNode->removeChild($te);
$ret['ctermsDeleted'][] = $sbas['sbas_id'] . '.' . $te->getAttribute('id');
}
}
if (!$request->get('debug')) {
$databox->saveCterms($sbas['domct']);
}
}
$ret['msg'] = $this->app->trans('prod::thesaurusTab:dlg:%number% record(s) updated', ['%number%' => $ret['nRecsUpdated']]);
} else {
// too many records to update
$ret['msg'] = $this->app->trans('prod::thesaurusTab:dlg:too many (%number%) records to update (limit=%maximum%)', ['%number%' => $ret['nRecsToUpdate'], '%maximum%' => self::SEARCH_REPLACE_MAXREC]);
}
return $this->app->json($ret);
@@ -1397,12 +1509,12 @@ class ThesaurusXmlHttpController extends Controller
$t = $this->splitTermAndContext($request->get('t'));
$unicode = $this->getUnicode();
$q2 = 'starts-with(@w, \'' . \thesaurus::xquery_escape($unicode->remove_indexer_chars($t[0])) . '\')';
if ($t[1])
$q2 .= ' and starts-with(@k, \'' . \thesaurus::xquery_escape(
$unicode->remove_indexer_chars($t[1])) . '\')';
$q2 = '//sy[' . $q2 . ' and @lng=\'' . $lng . '\']';
$q .= $q2;
if ($t[1]) {
$q2 .= ' and starts-with(@k, \'' . \thesaurus::xquery_escape($unicode->remove_indexer_chars($t[1])) . '\')';
}
$q2 .= ' and @lng=\'' . \thesaurus::xquery_escape($lng) . '\'';
$q .= ('//sy[' . $q2 . ']');
$nodes = $xpath->query($q);

View File

@@ -39,7 +39,6 @@ class V2 extends Api implements ControllerProviderInterface, ServiceProviderInte
->setJsonBodyHelper($app['json.body_helper']);
}
);
$app['controller.api.v2.lazaret'] = $app->share(
function (PhraseaApplication $app) {
return (new LazaretController($app));

View File

@@ -0,0 +1,37 @@
<?php
/**
* This file is part of Phraseanet
*
* (c) 2005-2016 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Event;
use Alchemy\Phrasea\Authentication\Context;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
class AuthenticationEvent extends Event
{
private $request;
private $context;
public function __construct(Request $request, Context $context)
{
$this->request = $request;
$this->context = $context;
}
public function getRequest()
{
return $this->request;
}
public function getContext()
{
return $this->context;
}
}

View File

@@ -15,21 +15,18 @@ use Alchemy\Phrasea\Authentication\Context;
use Alchemy\Phrasea\Model\Entities\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class PostAuthenticate extends SfEvent
class PostAuthenticate extends AuthenticationEvent
{
private $context;
private $user;
private $request;
private $response;
public function __construct(Request $request, Response $response, User $user, Context $context)
{
$this->request = $request;
parent::__construct($request, $context);
$this->response = $response;
$this->user = $user;
$this->context = $context;
}
public function getUser()
@@ -37,11 +34,6 @@ class PostAuthenticate extends SfEvent
return $this->user;
}
public function getRequest()
{
return $this->request;
}
public function getResponse()
{
return $this->response;
@@ -51,9 +43,4 @@ class PostAuthenticate extends SfEvent
{
$this->response = $response;
}
public function getContext()
{
return $this->context;
}
}

View File

@@ -11,28 +11,6 @@
namespace Alchemy\Phrasea\Core\Event;
use Alchemy\Phrasea\Authentication\Context;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\Event as SfEvent;
class PreAuthenticate extends SfEvent
class PreAuthenticate extends AuthenticationEvent
{
private $request;
private $context;
public function __construct(Request $request, Context $context)
{
$this->request = $request;
$this->context = $context;
}
public function getRequest()
{
return $this->request;
}
public function getContext()
{
return $this->context;
}
}

View File

@@ -108,7 +108,11 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
});
$app['manipulator.webhook-event'] = $app->share(function (Application $app) {
return new WebhookEventManipulator($app['orm.em'], $app['repo.webhook-event']);
return new WebhookEventManipulator(
$app['orm.em'],
$app['repo.webhook-event'],
$app['webhook.publisher']
);
});
$app['manipulator.webhook-delivery'] = $app->share(function (Application $app) {
@@ -122,6 +126,7 @@ class ManipulatorServiceProvider implements ServiceProviderInterface
$app['manipulator.lazaret'] = $app->share(function (Application $app) {
return new LazaretManipulator($app, $app['repo.lazaret-files'], $app['filesystem'], $app['orm.em']);
});
}
public function boot(SilexApplication $app)

View File

@@ -219,7 +219,7 @@ class SearchEngineServiceProvider implements ServiceProviderInterface
$clientBuilder = ClientBuilder::create()
->setHosts($clientParams['hosts']);
if (array_key_exists('logObject', $clientParams)) {
if(array_key_exists('logObject', $clientParams)) {
$clientBuilder->setLogger($clientParams['logObject']);
}

View File

@@ -3,6 +3,11 @@
namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Webhook\EventProcessorFactory;
use Alchemy\Phrasea\Webhook\EventProcessorWorker;
use Alchemy\Phrasea\Webhook\WebhookInvoker;
use Alchemy\Phrasea\Webhook\WebhookPublisher;
use Alchemy\Worker\CallableWorkerFactory;
use Alchemy\Worker\TypeBasedWorkerResolver;
use Silex\Application;
use Silex\ServiceProviderInterface;
@@ -11,9 +16,55 @@ class WebhookServiceProvider implements ServiceProviderInterface
public function register(Application $app)
{
$this->createAlias($app, 'webhook.event_repository', 'repo.webhook-event');
$this->createAlias($app, 'webhook.event_manipulator', 'manipulator.webhook-event');
$this->createAlias($app, 'webhook.delivery_repository', 'repo.webhook-delivery');
$this->createAlias($app, 'webhook.delivery_manipulator', 'manipulator.webhook-delivery');
$app['webhook.delivery_payload_repository'] = $app->share(function ($app) {
return $app['orm.em']->getRepository('Phraseanet:WebhookEventPayload');
});
$app['webhook.processor_factory'] = $app->share(function ($app) {
return new EventProcessorFactory($app);
});
$app['webhook.invoker'] = $app->share(function ($app) {
return new WebhookInvoker(
$app['repo.api-applications'],
$app['webhook.processor_factory'],
$app['webhook.event_repository'],
$app['webhook.event_manipulator'],
$app['webhook.delivery_repository'],
$app['webhook.delivery_manipulator'],
$app['webhook.delivery_payload_repository']
);
});
$app['webhook.publisher'] = $app->share(function ($app) {
return new WebhookPublisher($app['alchemy_worker.queue_registry'], $app['alchemy_worker.queue_name']);
});
$app['alchemy_worker.worker_resolver'] = $app->extend(
'alchemy_worker.type_based_worker_resolver',
function (TypeBasedWorkerResolver $resolver, Application $app) {
$resolver->setFactory('webhook', new CallableWorkerFactory(function () use ($app) {
return new EventProcessorWorker(
$app['webhook.event_repository'],
$app['webhook.invoker']
);
}));
return $resolver;
}
);
}
private function createAlias(Application $app, $alias, $targetServiceKey)
{
$app[$alias] = $app->share(function () use ($app, $targetServiceKey) {
return $app[$targetServiceKey];
});
}
public function boot(Application $app)

View File

@@ -0,0 +1,81 @@
<?php
namespace Alchemy\Phrasea\Core\Provider;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Worker\CallableWorkerFactory;
use Alchemy\Worker\TypeBasedWorkerResolver;
use Silex\Application;
use Silex\ServiceProviderInterface;
class WorkerConfigurationServiceProvider implements ServiceProviderInterface
{
/**
* Registers services on the given app.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*/
public function register(Application $app)
{
// Define the first defined queue as the worker queue
$app['alchemy_worker.queue_name'] = $app->share(function (Application $app) {
$queues = $app['alchemy_queues.queues'];
reset($queues);
return key($queues);
});
$app['alchemy_queues.queues'] = $app->share(function (Application $app) {
$defaultConfiguration = [
'worker-queue' => [
'registry' => 'alchemy_worker.queue_registry',
'host' => 'localhost',
'port' => 5672,
'user' => 'guest',
'vhost' => '/'
]
];
try {
/** @var PropertyAccess $configuration */
$configuration = $app['conf'];
$queueConfigurations = $configuration->get(['workers', 'queue'], $defaultConfiguration);
$queueConfiguration = reset($queueConfigurations);
$queueKey = key($queueConfigurations);
if (! isset($queueConfiguration['name'])) {
if (! is_string($queueKey)) {
throw new \RuntimeException('Invalid queue configuration: configuration has no key or name.');
}
$queueConfiguration['name'] = $queueKey;
}
$config = [ $queueConfiguration['name'] => $queueConfiguration ];
return $config;
}
catch (RuntimeException $exception) {
return [];
}
});
}
/**
* Bootstraps the application.
*
* This method is called after all services are registered
* and should be used for "dynamic" configuration (whenever
* a service must be requested).
*/
public function boot(Application $app)
{
// No-op
}
}

View File

@@ -56,6 +56,16 @@ class SubdefGroup implements \IteratorAggregate, \Countable
return $this->type;
}
public function allowDocumentOrdering()
{
$this->isDocumentOrderable = true;
}
public function disallowDocumentOrdering()
{
$this->isDocumentOrderable = false;
}
/**
* @return bool
*/

View File

@@ -62,7 +62,7 @@ class PhraseaRegisterForm extends AbstractType
'label' => 'Terms of Use',
'mapped' => false,
"constraints" => [
new Assert\True([
new Assert\IsTrue([
"message" => "Please accept the Terms and conditions in order to register."
])],
]);

View File

@@ -56,6 +56,11 @@ class WebhookEventDelivery
*/
private $created;
/**
* @ORM\OneToOne(targetEntity="WebhookEventPayload", mappedBy="delivery")
*/
private $payload;
/**
* @param \DateTime $created
*
@@ -163,4 +168,12 @@ class WebhookEventDelivery
{
return $this->event;
}
/**
* @return WebhookEventPayload
*/
public function getPayload()
{
return $this->payload;
}
}

View File

@@ -0,0 +1,127 @@
<?php
/*
* This file is part of phrasea-4.1.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
/**
* @ORM\Table(name="WebhookEventPayloads")
* @ORM\Entity(repositoryClass="Alchemy\Phrasea\Model\Repositories\WebhookEventPayloadRepository")
*/
class WebhookEventPayload
{
/**
* @ORM\Column(type="guid")
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
*
* @var string
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="WebhookEventDelivery")
* @ORM\JoinColumn(name="delivery_id", referencedColumnName="id")
*/
private $delivery;
/**
* @ORM\Column(type="text", name="request")
* @var string
*/
private $requestPayload;
/**
* @ORM\Column(type="text", name="response")
* @var string
*/
private $responsePayload;
/**
* @ORM\Column(type="integer", name="status")
* @var int
*/
private $statusCode;
/**
* @ORM\Column(type="text")
* @var string
*/
private $headers;
/**
* @param WebhookEventDelivery $eventDelivery
* @param string $requestPayload
* @param string $responsePayload
* @param int $statusCode
* @param string $headers
*/
public function __construct(WebhookEventDelivery $eventDelivery, $requestPayload, $responsePayload, $statusCode, $headers)
{
$this->id = Uuid::uuid4()->toString();
$this->delivery = $eventDelivery;
$this->requestPayload = $requestPayload;
$this->responsePayload = $responsePayload;
$this->statusCode = $statusCode;
$this->headers = $headers;
}
/**
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* @return WebhookEventDelivery
*/
public function getDelivery()
{
return $this->delivery;
}
/**
* @return string
*/
public function getRequestPayload()
{
return $this->requestPayload;
}
/**
* @return string
*/
public function getResponsePayload()
{
return $this->responsePayload;
}
/**
* @return int
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @return string
*/
public function getResponseHeaders()
{
return $this->headers;
}
}

View File

@@ -147,7 +147,8 @@ class LazaretManipulator
$this->app,
$lazaretFile->getOriginalName()
);
} catch (\Exception $e) {
}
catch(\Exception $e) {
// the file is not in tmp anymore ?
// delete the quarantine item
$this->denyLazaretFile($lazaretFile);
@@ -196,6 +197,7 @@ class LazaretManipulator
case AttributeInterface::NAME_METADATA:
/** @var Metadata $value */
$value = $attribute->getValue();
// $metadataBag->set($value->getTag()->getTagname(), new Metadata($value->getTag(), $value->getValue()));
$metadataBag->set($value->getTag()->getTagname(), $value);
break;
case AttributeInterface::NAME_STORY:

View File

@@ -35,21 +35,52 @@ use Alchemy\Phrasea\Core\Event\User\DeletedEvent;
*/
class UserManipulator implements ManipulatorInterface
{
/** @var PasswordEncoderInterface */
/**
* @var PasswordEncoderInterface
*/
protected $passwordEncoder;
/** @var UserManager */
/**
* @var UserManager
*/
private $manager;
/** @var GeonamesConnector */
/**
* @var GeonamesConnector
*/
private $geonamesConnector;
/** @var Generator */
/**
* @var Generator
*/
private $generator;
/** @var EntityRepository */
/**
* @var EntityRepository
*/
private $repository;
/** @var EventDispatcherInterface */
/**
* @var EventDispatcherInterface
*/
private $dispatcher;
public function __construct(UserManager $manager, PasswordEncoderInterface $passwordEncoder, GeonamesConnector $connector, EntityRepository $repo, Generator $generator, EventDispatcherInterface $dispatcher)
/**
* @param UserManager $manager
* @param PasswordEncoderInterface $passwordEncoder
* @param GeonamesConnector $connector
* @param EntityRepository $repo
* @param Generator $generator
* @param EventDispatcherInterface $dispatcher
*/
public function __construct(
UserManager $manager,
PasswordEncoderInterface $passwordEncoder,
GeonamesConnector $connector,
EntityRepository $repo,
Generator $generator,
EventDispatcherInterface $dispatcher
)
{
$this->manager = $manager;
$this->generator = $generator;

View File

@@ -12,29 +12,46 @@
namespace Alchemy\Phrasea\Model\Manipulator;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Phrasea\Webhook\WebhookPublisher;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityRepository;
class WebhookEventManipulator implements ManipulatorInterface
{
/**
* @var ObjectManager
*/
private $om;
/**
* @var EntityRepository
*/
private $repository;
public function __construct(ObjectManager $om, EntityRepository $repo)
/**
* @var WebhookPublisher
*/
private $publisher;
public function __construct(ObjectManager $om, EntityRepository $repo, WebhookPublisher $publisher)
{
$this->om = $om;
$this->repository = $repo;
$this->publisher = $publisher;
}
public function create($eventName, $type, array $data)
{
$event = new WebhookEvent();
$event->setName($eventName);
$event->setType($type);
$event->setData($data);
$this->update($event);
$this->publisher->publishWebhookEvent($event);
return $event;
}

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\WebhookEventDelivery;
use Doctrine\ORM\EntityRepository;
@@ -22,6 +23,10 @@ use Doctrine\ORM\EntityRepository;
*/
class WebhookEventDeliveryRepository extends EntityRepository
{
/**
* @return WebhookEventDelivery[]
*/
public function findUndeliveredEvents()
{
$qb = $this->createQueryBuilder('e');
@@ -34,4 +39,22 @@ class WebhookEventDeliveryRepository extends EntityRepository
return $qb->getQuery()->getResult();
}
/**
* @param ApiApplication $apiApplication
* @param int $count
* @return WebhookEventDelivery[]
*/
public function findLastDeliveries(ApiApplication $apiApplication, $count = 10)
{
$qb = $this->createQueryBuilder('e');
$qb
->where('e.application = :app')
->setMaxResults(max(0, (int) $count))
->orderBy('e.created', 'DESC')
->setParameters([ 'app' => $apiApplication ]);
return $qb->getQuery()->getResult();
}
}

View File

@@ -0,0 +1,27 @@
<?php
/*
* This file is part of phrasea-4.1.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Model\Repositories;
use Alchemy\Phrasea\Model\Entities\WebhookEventPayload;
use Doctrine\ORM\EntityRepository;
class WebhookEventPayloadRepository extends EntityRepository
{
public function save(WebhookEventPayload $payload)
{
$this->_em->persist($payload);
$this->_em->persist($payload->getDelivery());
$this->_em->flush([ $payload, $payload->getDelivery() ]);
}
}

View File

@@ -242,7 +242,7 @@ class ApiOrderController extends BaseOrderController
$filtered = [];
foreach ($records as $index => $record) {
if ($acl->has_right_on_base($record->getBaseId(), 'cancmd')) {
if (!$record->isStory() && $acl->has_right_on_base($record->getBaseId(), 'cancmd')) {
$filtered[$index] = $record;
}
}

View File

@@ -287,7 +287,6 @@ class ElasticSearchEngine implements SearchEngineInterface
}
$aggs = $this->getAggregationQueryParams($options);
if ($aggs) {
$params['body']['aggs'] = $aggs;
}

View File

@@ -139,6 +139,8 @@ class Indexer
// everything ready to search
$bulk->flush();
$this->client->indices()->refresh();
$this->client->indices()->clearCache();
$this->client->indices()->flushSynced();
}
if ($what & self::RECORDS) {

View File

@@ -78,7 +78,9 @@ class ThesaurusHydrator implements HydratorInterface
}
}
}
if(empty($terms)) {
return;
}
$bulk = $this->thesaurus->findConceptsBulk($terms, null, $filters, true);
foreach ($bulk as $offset => $item_concepts) {

View File

@@ -79,19 +79,138 @@ class Thesaurus
* @return Concept[] Matching concepts
*/
public function findConcepts($term, $lang = null, Filter $filter = null, $strict = false)
{
return $strict ?
$this->findConceptsStrict($term, $lang, $filter)
:
$this->findConceptsFuzzy($term, $lang, $filter)
;
}
private function findConceptsStrict($term, $lang = null, Filter $filter = null)
{
if (!($term instanceof TermInterface)) {
$term = new Term($term);
}
$this->logger->info(sprintf('Searching for term %s', $term), array(
'strict' => $strict,
'strict' => true,
'lang' => $lang
));
if ($strict) {
$field_suffix = '.strict';
} elseif ($lang) {
$must = [];
$filters = [];
$must[] = [
'match' => [
'value.strict' => [
'query' => $term->getValue(),
'operator' => 'and',
],
],
];
if ($term->hasContext()) {
$must[] = [
'match' => [
'context.strict' => [
'query' => $term->getContext(),
'operator' => 'and',
],
],
];
} else {
$filters[] = [
'missing' => [
'field' => 'context'
]
];
}
if ($lang) {
$filters[] = [
'term' => [
'lang' => $lang
]
];
}
if ($filter) {
$filters = array_merge($filters, $filter->getQueryFilters());
}
if(!empty($filters)) {
if (count($filters) > 1) {
$must[] = [
'constant_score' => [
'filter' => [
'and' => $filters
]
]
];
}
else {
$must[] = [
'constant_score' => [
'filter' => $filters[0]
]
];
}
}
if(count($must) > 1) {
$query = [
'bool' => [
'must' => $must
]
];
}
else {
$query = $must[0];
}
// Path deduplication
$aggs = array();
$aggs['dedup']['terms']['field'] = 'path.raw';
// Search request
$params = array();
$params['index'] = $this->options->getIndexName();
$params['type'] = TermIndexer::TYPE_NAME;
$params['body']['query'] = $query;
$params['body']['aggs'] = $aggs;
// No need to get any hits since we extract data from aggs
$params['body']['size'] = 0;
$this->logger->debug('Sending search', $params['body']);
$response = $this->client->search($params);
// Extract concept paths from response
$concepts = array();
$buckets = \igorw\get_in($response, ['aggregations', 'dedup', 'buckets'], []);
$keys = array();
foreach ($buckets as $bucket) {
if (isset($bucket['key'])) {
$keys[] = $bucket['key'];
$concepts[] = new Concept($bucket['key']);
}
}
$this->logger->info(sprintf('Found %d matching concepts', count($concepts)),
array('concepts' => $keys)
);
return $concepts;
}
private function findConceptsFuzzy($term, $lang = null, Filter $filter = null)
{
if (!($term instanceof TermInterface)) {
$term = new Term($term);
}
$this->logger->info(sprintf('Searching for term %s', $term), array(
'strict' => false,
'lang' => $lang
));
if($lang) {
$field_suffix = sprintf('.%s', $lang);
} else {
$field_suffix = '';
@@ -114,10 +233,6 @@ class Thesaurus
$query = array();
$query['bool']['must'][0] = $value_query;
$query['bool']['must'][1] = $context_query;
} elseif ($strict) {
$context_filter = array();
$context_filter['missing']['field'] = 'context';
$query = self::applyQueryFilter($query, $context_filter);
}
if ($lang) {

View File

@@ -46,4 +46,34 @@ class Filter
return $filter;
}
public function getQueryFilters()
{
$filters = [
[
'term' => [
'databox_id' => $this->databox_id
]
]
];
if(!empty($this->paths)) {
if (count($this->paths) == 1) {
$filters[] = [
'term' => [
'path' => $this->paths[0]
]
];
}
else {
$filters[] = [
'terms' => [
'path' => $this->paths
]
];
}
}
return $filters;
}
}

View File

@@ -130,7 +130,7 @@ class SearchEngineOptions
/** @var string */
protected $record_type = self::TYPE_ALL;
protected $search_type = self::RECORD_RECORD;
protected $search_type = self::RECORD_RECORD;
/** @var \collection[] */
protected $collections = [];
/** @var null|\databox[] */

View File

@@ -0,0 +1,40 @@
<?php
namespace Alchemy\Phrasea\Setup\DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20161013115559 extends AbstractMigration
{
public function isAlreadyApplied()
{
return $this->tableExists('Orders');
}
/**
* @param Schema $schema
*/
public function doUpSql(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql("CREATE TABLE WebhookEventPayloads (id CHAR(36) NOT NULL COMMENT '(DC2Type:guid)', delivery_id INT DEFAULT NULL, request LONGTEXT NOT NULL, response LONGTEXT NOT NULL, status INT NOT NULL, headers LONGTEXT NOT NULL, UNIQUE INDEX UNIQ_B949629612136921 (delivery_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;");
$this->addSql("ALTER TABLE WebhookEventPayloads ADD CONSTRAINT FK_B949629612136921 FOREIGN KEY (delivery_id) REFERENCES WebhookEventDeliveries (id);");
}
/**
* @param Schema $schema
*/
public function doDownSql(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('DROP TABLE WebhookEventPayloads');
}
}

View File

@@ -12,21 +12,11 @@
namespace Alchemy\Phrasea\TaskManager\Job;
use Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Phrasea\Model\Entities\WebhookEventDelivery;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\TaskManager\Editor\DefaultEditor;
use Alchemy\Phrasea\Webhook\EventProcessorFactory;
use Guzzle\Http\Client as GuzzleClient;
use Guzzle\Batch\BatchBuilder;
use Guzzle\Http\Message\Request;
use Silex\Application;
use Guzzle\Common\Event;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Guzzle\Plugin\Backoff\CallbackBackoffStrategy;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Psr\Log\LoggerInterface;
use Silex\Application;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Translation\TranslatorInterface;
@@ -34,15 +24,12 @@ class WebhookJob extends AbstractJob
{
private $httpClient;
private $firstRun = true;
public function __construct(
TranslatorInterface $translator,
EventDispatcherInterface $dispatcher = null,
LoggerInterface $logger = null,
GuzzleClient $httpClient = null
)
{
) {
parent::__construct($translator, $dispatcher, $logger);
$this->httpClient = $httpClient ?: new GuzzleClient();
@@ -89,57 +76,6 @@ class WebhookJob extends AbstractJob
{
$app = $data->getApplication();
$thirdPartyApplications = $app['repo.api-applications']->findWithDefinedWebhookCallback();
$that = $this;
if ($this->firstRun) {
$this->httpClient->getEventDispatcher()->addListener('request.error', function (Event $event) {
// override guzzle default behavior of throwing exceptions
// when 4xx & 5xx responses are encountered
$event->stopPropagation();
}, -254);
// Set callback which logs success or failure
$subscriber = new CallbackBackoffStrategy(function ($retries, Request $request, $response, $e) use ($app, $that) {
$retry = true;
if ($response && (null !== $deliverId = parse_url($request->getUrl(), PHP_URL_FRAGMENT))) {
$delivery = $app['repo.webhook-delivery']->find($deliverId);
$logContext = [ 'host' => $request->getHost() ];
if ($response->isSuccessful()) {
$app['manipulator.webhook-delivery']->deliverySuccess($delivery);
$logType = 'info';
$logEntry = sprintf('Deliver success event "%d:%s" for app "%s"',
$delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
$delivery->getThirdPartyApplication()->getName()
);
$retry = false;
} else {
$app['manipulator.webhook-delivery']->deliveryFailure($delivery);
$logType = 'error';
$logEntry = sprintf('Deliver failure event "%d:%s" for app "%s"',
$delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
$delivery->getThirdPartyApplication()->getName()
);
}
$that->log($logType, $logEntry, $logContext);
return $retry;
}
}, true, new CurlBackoffStrategy());
// set max retries
$subscriber = new TruncatedBackoffStrategy(WebhookEventDelivery::MAX_DELIVERY_TRIES, $subscriber);
$subscriber = new BackoffPlugin($subscriber);
$this->httpClient->addSubscriber($subscriber);
$this->firstRun = false;
}
/** @var EventProcessorFactory $eventFactory */
$eventFactory = $app['webhook.processor_factory'];
@@ -155,41 +91,4 @@ class WebhookJob extends AbstractJob
$this->deliverEvent($eventFactory, $app, $thirdPartyApplications, $event);
}
}
private function deliverEvent(EventProcessorFactory $eventFactory, Application $app, array $thirdPartyApplications, WebhookEvent $event)
{
if (count($thirdPartyApplications) === 0) {
$this->log('info', sprintf('No applications defined to listen for webhook events'));
return;
}
// format event data
$eventProcessor = $eventFactory->get($event);
$data = $eventProcessor->process($event);
// batch requests
$batch = BatchBuilder::factory()
->transferRequests(10)
->build();
foreach ($thirdPartyApplications as $thirdPartyApplication) {
$delivery = $app['manipulator.webhook-delivery']->create($thirdPartyApplication, $event);
// append delivery id as url anchor
$uniqueUrl = $this->getUrl($thirdPartyApplication, $delivery);
// create http request with data as request body
$batch->add($this->httpClient->createRequest('POST', $uniqueUrl, [
'Content-Type' => 'application/vnd.phraseanet.event+json'
], json_encode($data)));
}
$batch->flush();
}
private function getUrl(ApiApplication $application, WebhookEventDelivery $delivery)
{
return sprintf('%s#%s', $application->getWebhookUrl(), $delivery->getId());
}
}

View File

@@ -16,7 +16,7 @@ class EventProcessorFactory
{
/**
* @var ProcessorFactory
* @var ProcessorFactory[]
*/
private $processorFactories = [];
@@ -59,10 +59,20 @@ class EventProcessorFactory
/**
* @param WebhookEvent $event
* @return Processor\ProcessorInterface
* @deprecated Use getProcessor() instead
*/
public function get(WebhookEvent $event)
{
if (! isset($this->processorFactories[$event->getType()])) {
return $this->getProcessor($event);
}
/**
* @param WebhookEvent $event
* @return ProcessorInterface
*/
public function getProcessor(WebhookEvent $event)
{
if (!isset($this->processorFactories[$event->getType()])) {
throw new \RuntimeException(sprintf('No processor found for %s', $event->getType()));
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of phrasea-4.1.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Webhook;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Phrasea\Model\Repositories\WebhookEventRepository;
use Alchemy\Worker\Worker;
class EventProcessorWorker implements Worker
{
/**
* @var WebhookEventRepository
*/
private $eventRepository;
/**
* @var WebhookInvoker
*/
private $invoker;
/**
* @param WebhookEventRepository $eventRepository
* @param WebhookInvoker $invoke
*/
public function __construct(WebhookEventRepository $eventRepository, WebhookInvoker $invoke)
{
$this->eventRepository = $eventRepository;
$this->invoker = $invoke;
}
/**
* @param array $payload
* @return void
*/
public function process(array $payload)
{
$eventId = $payload['id'];
/** @var WebhookEvent $event */
$event = $this->eventRepository->find($eventId);
if ($event === null || $event->isProcessed()) {
return;
}
$this->invoker->invoke($event);
}
}

View File

@@ -0,0 +1,292 @@
<?php
/*
* This file is part of phrasea-4.1.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Webhook;
use Alchemy\Phrasea\Model\Entities\ApiApplication;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Phrasea\Model\Entities\WebhookEventDelivery;
use Alchemy\Phrasea\Model\Entities\WebhookEventPayload;
use Alchemy\Phrasea\Model\Manipulator\WebhookEventDeliveryManipulator;
use Alchemy\Phrasea\Model\Manipulator\WebhookEventManipulator;
use Alchemy\Phrasea\Model\Repositories\ApiApplicationRepository;
use Alchemy\Phrasea\Model\Repositories\WebhookEventDeliveryRepository;
use Alchemy\Phrasea\Model\Repositories\WebhookEventPayloadRepository;
use Alchemy\Phrasea\Model\Repositories\WebhookEventRepository;
use Guzzle\Common\Event;
use Guzzle\Http\Client;
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
use Guzzle\Http\Message\Request;
use Guzzle\Http\Message\Response;
use Guzzle\Plugin\Backoff\BackoffPlugin;
use Guzzle\Plugin\Backoff\CallbackBackoffStrategy;
use Guzzle\Plugin\Backoff\CurlBackoffStrategy;
use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
/**
* Class WebhookInvoker invokes remote endpoints with webhook event data
* @package Alchemy\Phrasea\Webhook
*/
class WebhookInvoker implements LoggerAwareInterface
{
/**
* @var ApiApplicationRepository
*/
private $applicationRepository;
/**
* @var WebhookEventDeliveryManipulator
*/
private $eventDeliveryManipulator;
/**
* @var WebhookEventDeliveryRepository
*/
private $eventDeliveryRepository;
/**
* @var Client
*/
private $client;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var EventProcessorFactory
*/
private $processorFactory;
/**
* @var WebhookEventRepository
*/
private $eventRepository;
/**
* @var WebhookEventManipulator
*/
private $eventManipulator;
/**
* @var WebhookEventPayloadRepository
*/
private $eventPayloadRepository;
/**
* @param ApiApplicationRepository $applicationRepository
* @param EventProcessorFactory $processorFactory
* @param WebhookEventRepository $eventRepository
* @param WebhookEventManipulator $eventManipulator
* @param WebhookEventDeliveryRepository $eventDeliveryRepository
* @param WebhookEventDeliveryManipulator $eventDeliveryManipulator
* @param WebhookEventPayloadRepository $eventPayloadRepository
* @param Client $client
*
* @todo Extract classes to reduce number of required dependencies
*/
public function __construct(
ApiApplicationRepository $applicationRepository,
EventProcessorFactory $processorFactory,
WebhookEventRepository $eventRepository,
WebhookEventManipulator $eventManipulator,
WebhookEventDeliveryRepository $eventDeliveryRepository,
WebhookEventDeliveryManipulator $eventDeliveryManipulator,
WebhookEventPayloadRepository $eventPayloadRepository,
Client $client = null
) {
$this->applicationRepository = $applicationRepository;
$this->processorFactory = $processorFactory;
$this->eventRepository = $eventRepository;
$this->eventManipulator = $eventManipulator;
$this->eventDeliveryManipulator = $eventDeliveryManipulator;
$this->eventDeliveryRepository = $eventDeliveryRepository;
$this->eventPayloadRepository = $eventPayloadRepository;
$this->client = $client ?: new Client();
$this->logger = new NullLogger();
$this->configureClient();
}
/**
* Sets a logger instance on the object.
*
* @param LoggerInterface $logger
*
* @return null
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function invoke(WebhookEvent $event)
{
$this->doInvoke($event, $this->applicationRepository->findWithDefinedWebhookCallback());
}
public function invokeUnprocessedEvents()
{
$targetApplications = $this->applicationRepository->findWithDefinedWebhookCallback();
foreach ($this->eventRepository->getUnprocessedEventIterator() as $row) {
/** @var WebhookEvent $event */
$event = $row[0];
$this->doInvoke($event, $targetApplications);
}
}
/**
* @param WebhookEvent $event
* @param ApiApplication[] $targets
*/
private function doInvoke(WebhookEvent $event, array $targets)
{
$this->eventManipulator->processed($event);
$this->logger->info(sprintf('Processing event "%s" with id %d', $event->getName(), $event->getId()));
// send requests
$this->doHttpDelivery($event, $targets);
}
private function configureClient()
{
$this->client->getEventDispatcher()->addListener('request.error', function (Event $event) {
// Override guzzle default behavior of throwing exceptions
// when 4xx & 5xx responses are encountered
$event->stopPropagation();
}, -254);
// Set callback which logs success or failure
$subscriber = new CallbackBackoffStrategy(function ($retries, Request $request, $response, $e) {
$retry = true;
if ($response && (null !== $deliverId = parse_url($request->getUrl(), PHP_URL_FRAGMENT))) {
$delivery = $this->eventDeliveryRepository->find($deliverId);
$logContext = ['host' => $request->getHost()];
if ($response->isSuccessful()) {
$this->eventDeliveryManipulator->deliverySuccess($delivery);
$logType = 'info';
$logEntry = sprintf('Deliver success event "%d:%s" for app "%s"',
$delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
$delivery->getThirdPartyApplication()->getName()
);
$retry = false;
} else {
$this->eventDeliveryManipulator->deliveryFailure($delivery);
$logType = 'error';
$logEntry = sprintf('Deliver failure event "%d:%s" for app "%s"',
$delivery->getWebhookEvent()->getId(), $delivery->getWebhookEvent()->getName(),
$delivery->getThirdPartyApplication()->getName()
);
}
$this->logger->log($logType, $logEntry, $logContext);
return $retry;
}
}, true, new CurlBackoffStrategy());
// Set max retries
$subscriber = new TruncatedBackoffStrategy(WebhookEventDelivery::MAX_DELIVERY_TRIES, $subscriber);
$subscriber = new BackoffPlugin($subscriber);
$this->client->addSubscriber($subscriber);
}
/**
* @param WebhookEvent $event
* @param ApiApplication[] $targets
*/
private function doHttpDelivery(
WebhookEvent $event,
array $targets
) {
if (count($targets) === 0) {
$this->logger->info(sprintf('No applications defined to listen for webhook events'));
return;
}
// Format event data
$eventProcessor = $this->processorFactory->getProcessor($event);
$data = $eventProcessor->process($event);
foreach ($targets as $thirdPartyApplication) {
$delivery = $this->eventDeliveryManipulator->create($thirdPartyApplication, $event);
// Append delivery id as url anchor
$uniqueUrl = $this->buildUrl($thirdPartyApplication, $delivery);
// Create http request with data as request body
$request = $this->client->createRequest('POST', $uniqueUrl, [
'Content-Type' => 'application/vnd.phraseanet.event+json'
], json_encode($data));
$requestBody = $request instanceof EntityEnclosingRequestInterface ? $request->getBody() : '';
try {
$response = $request->send();
$responseBody = $response->getBody(true);
$statusCode = $response->getStatusCode();
$headers = $this->extractResponseHeaders($response);
}
catch (\Exception $exception) {
$responseBody = $exception->getMessage();
$statusCode = -1;
$headers = '';
}
$deliveryPayload = new WebhookEventPayload(
$delivery,
$requestBody,
$responseBody,
$statusCode,
$headers
);
$this->eventPayloadRepository->save($deliveryPayload);
}
}
/**
* @param ApiApplication $application
* @param WebhookEventDelivery $delivery
* @return string
*/
private function buildUrl(ApiApplication $application, WebhookEventDelivery $delivery)
{
return sprintf('%s#%s', $application->getWebhookUrl(), $delivery->getId());
}
private function extractResponseHeaders(Response $response)
{
$headerCollection = $response->getHeaders()->toArray();
$headers = '';
foreach ($headerCollection as $name => $value) {
$headers .= sprintf('%s: %s', $name, $value) . PHP_EOL;
}
return trim($headers);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of phrasea-4.1.
*
* (c) Alchemy <info@alchemy.fr>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Webhook;
use Alchemy\Phrasea\Model\Entities\WebhookEvent;
use Alchemy\Queue\Message;
use Alchemy\Queue\MessageQueueRegistry;
/**
* Class WebhookPublisher publishes webhook event notifications in message queues
* @package Alchemy\Phrasea\Webhook
*/
class WebhookPublisher
{
/**
* @var MessageQueueRegistry
*/
private $queueRegistry;
/**
* @var string
*/
private $queueName;
/**
* @param MessageQueueRegistry $queueRegistry
* @param $queueName
*/
public function __construct(MessageQueueRegistry $queueRegistry, $queueName)
{
$this->queueRegistry = $queueRegistry;
$this->queueName = $queueName;
}
/**
* @param WebhookEvent $event
*/
public function publishWebhookEvent(WebhookEvent $event)
{
$queue = $this->queueRegistry->getQueue($this->queueName);
$payload = [
'message_type' => 'webhook',
'payload' => [ 'id' => $event->getId() ]
];
$queue->publish(new Message(json_encode($payload)));
}
}

View File

@@ -1,95 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2016 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Exception\RuntimeException;
class API_Webhook
{
const NEW_FEED_ENTRY = "new_feed_entry";
protected $appbox;
protected $id;
protected $type;
protected $data;
protected $created;
public function __construct(appbox $appbox, $id)
{
$this->appbox = $appbox;
$this->id = $id;
$sql = 'SELECT `type`, `data`, created
FROM api_webhooks
WHERE id = :id';
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute([':id' => $id]);
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$row) {
throw new RuntimeException('Webhooks not found');
}
$stmt->closeCursor();
$this->type = $row['type'];
$this->data = json_decode($row['data']);
$this->created = new \DateTime($row['created']);
}
public function delete()
{
$sql = 'DELETE FROM api_webhooks WHERE id = :id';
$stmt = $this->appbox->get_connection()->prepare($sql);
$stmt->execute([':id' => $this->id]);
$stmt->closeCursor();
return;
}
public static function create(appbox $appbox, $type, array $data)
{
$sql = 'INSERT INTO api_webhooks (id, `type`, `data`, created)
VALUES (null, :type, :data, NOW())';
$stmt = $appbox->get_connection()->prepare($sql);
$stmt->execute([
'type' => $type,
'data' => json_encode($data),
]);
$stmt->closeCursor();
return new API_Webhook($appbox, $appbox->get_connection()->lastInsertId());
}
/**
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* @return mixed
*/
public function getData()
{
return $this->data;
}
/**
* @return mixed
*/
public function getType()
{
return $this->type;
}
}

View File

@@ -523,22 +523,16 @@ class Bridge_Api_Dailymotion extends Bridge_Api_Abstract implements Bridge_Api_I
switch ($connector_status) {
case self::UPLOAD_STATE_DELETED:
return $this->translator->trans('La video a ete supprimee');
break;
case self::UPLOAD_STATE_REJECTED:
return $this->translator->trans('La video a ete rejetee');
break;
case self::UPLOAD_STATE_ENCODING_ERROR:
return $this->translator->trans('Erreur d\'encodage');
break;
case self::UPLOAD_STATE_PROCESSING:
return $this->translator->trans('En cours d\'encodage');
break;
default:
return '';
break;
case self::UPLOAD_STATE_DONE:
return $this->translator->trans('OK');
break;
}
}

View File

@@ -484,11 +484,9 @@ class Bridge_Api_Flickr extends Bridge_Api_Abstract implements Bridge_Api_Interf
switch ($connector_status) {
case self::UPLOAD_STATE_FAILED:
return $this->translator->trans('L\'upload a echoue');
break;
default:
case self::UPLOAD_STATE_DONE:
return '';
break;
}
}

View File

@@ -17,6 +17,7 @@ use Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement;
use Alchemy\Phrasea\Core\Version\DataboxVersionRepository;
use Alchemy\Phrasea\Databox\DataboxRepository;
use Alchemy\Phrasea\Databox\Record\RecordRepository;
use Alchemy\Phrasea\Databox\SubdefGroup;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Status\StatusStructure;
@@ -450,8 +451,9 @@ class databox extends base implements ThumbnailedElement
$multi = isset($field['multi']) ? (Boolean) (string) $field['multi'] : false;
$meta_struct_field = databox_field::create($this->app, $this, $fname, $multi);
$meta_struct_field = databox_field::create($this->app, $this, $fname);
$meta_struct_field
->set_multi($multi)
->set_readonly(isset($field['readonly']) ? (string) $field['readonly'] : 0)
->set_indexable(isset($field['index']) ? (string) $field['index'] : '1')
->set_separator(isset($field['separator']) ? (string) $field['separator'] : '')
@@ -892,7 +894,7 @@ class databox extends base implements ThumbnailedElement
}
/**
* @return databox_subdefsStructure|\Alchemy\Phrasea\Databox\SubdefGroup[]|databox_subdef[][]
* @return databox_subdefsStructure|SubdefGroup[]
*/
public function get_subdef_structure()
{

View File

@@ -564,6 +564,18 @@ class databox_field implements cache_cacheableInterface
return $this;
}
/**
*
* @param boolean $bool
* @return databox_field
*/
public function set_multi($bool)
{
$this->multi = (bool)$bool;
$this->separator = self::checkMultiSeparator($this->separator, $this->multi);
return $this;
}
/**
* Set a vocabulary
*
@@ -892,7 +904,7 @@ class databox_field implements cache_cacheableInterface
*
* @throws \Exception_InvalidArgument
*/
public static function create(Application $app, databox $databox, $name, $multi)
public static function create(Application $app, databox $databox, $name)
{
$sorter = 0;
@@ -911,8 +923,8 @@ class databox_field implements cache_cacheableInterface
`thumbtitle`, `multi`, `business`, `aggregable`,
`report`, `sorter`, `separator`)
VALUES (null, :name, '', 0, 0, 1, 'string', '',
null, :multi,
0, 0, 1, :sorter, '')";
null, 0, 0, 0,
1, :sorter, '')";
$name = self::generateName($name);
@@ -920,10 +932,8 @@ class databox_field implements cache_cacheableInterface
throw new \Exception_InvalidArgument();
}
$multi = $multi ? 1 : 0;
$stmt = $databox->get_connection()->prepare($sql);
$stmt->execute([':name' => $name, ':sorter' => $sorter, ':multi' => $multi]);
$stmt->execute([':name' => $name, ':sorter' => $sorter]);
$id = $databox->get_connection()->lastInsertId();
$stmt->closeCursor();

View File

@@ -1,5 +1,4 @@
<?php
/*
* This file is part of Phraseanet
*
@@ -9,18 +8,37 @@
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Media\Subdef\Image;
use Alchemy\Phrasea\Media\Subdef\Audio;
use Alchemy\Phrasea\Media\Subdef\Video;
use Alchemy\Phrasea\Media\Subdef\FlexPaper;
use Alchemy\Phrasea\Media\Subdef\Gif;
use Alchemy\Phrasea\Media\Subdef\Image;
use Alchemy\Phrasea\Media\Subdef\Subdef as SubdefSpecs;
use Alchemy\Phrasea\Media\Subdef\Video;
use Alchemy\Phrasea\Media\Type\Type as SubdefType;
use MediaAlchemyst\Specification\SpecificationInterface;
use Symfony\Component\Translation\TranslatorInterface;
class databox_subdef
{
const CLASS_THUMBNAIL = 'thumbnail';
const CLASS_PREVIEW = 'preview';
const CLASS_DOCUMENT = 'document';
const DEVICE_ALL = 'all';
const DEVICE_HANDHELD = 'handheld';
const DEVICE_PRINT = 'print';
const DEVICE_PROJECTION = 'projection';
const DEVICE_SCREEN = 'screen';
const DEVICE_TV = 'tv';
protected static $mediaTypeToSubdefTypes = [
SubdefType::TYPE_AUDIO => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_AUDIO],
SubdefType::TYPE_DOCUMENT => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_FLEXPAPER],
SubdefType::TYPE_FLASH => [SubdefSpecs::TYPE_IMAGE],
SubdefType::TYPE_IMAGE => [SubdefSpecs::TYPE_IMAGE],
SubdefType::TYPE_VIDEO => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_VIDEO, SubdefSpecs::TYPE_ANIMATION],
];
/**
* The class type of the subdef
* Is null or one of the CLASS_* constants
@@ -33,37 +51,23 @@ class databox_subdef
protected $path;
protected $subdef_group;
protected $labels = [];
protected $downloadable;
protected $translator;
/**
* @var bool
*/
private $requiresMetadataUpdate;
protected $downloadable;
protected $translator;
protected static $mediaTypeToSubdefTypes = [
SubdefType::TYPE_AUDIO => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_AUDIO],
SubdefType::TYPE_DOCUMENT => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_FLEXPAPER],
SubdefType::TYPE_FLASH => [SubdefSpecs::TYPE_IMAGE],
SubdefType::TYPE_IMAGE => [SubdefSpecs::TYPE_IMAGE],
SubdefType::TYPE_VIDEO => [SubdefSpecs::TYPE_IMAGE, SubdefSpecs::TYPE_VIDEO, SubdefSpecs::TYPE_ANIMATION],
];
const CLASS_THUMBNAIL = 'thumbnail';
const CLASS_PREVIEW = 'preview';
const CLASS_DOCUMENT = 'document';
const DEVICE_ALL = 'all';
const DEVICE_HANDHELD = 'handheld';
const DEVICE_PRINT = 'print';
const DEVICE_PROJECTION = 'projection';
const DEVICE_SCREEN = 'screen';
const DEVICE_TV = 'tv';
/**
*
* @param SubdefType $type
* @var bool
*/
private $orderable;
/**
* @param SubdefType $type
* @param SimpleXMLElement $sd
*
* @return databox_subdef
* @param TranslatorInterface $translator
*/
public function __construct(SubdefType $type, SimpleXMLElement $sd, TranslatorInterface $translator)
{
@@ -77,6 +81,7 @@ class databox_subdef
$this->name = strtolower($sd->attributes()->name);
$this->downloadable = p4field::isyes($sd->attributes()->downloadable);
$this->orderable = isset($sd->attributes()->orderable) ? p4field::isyes($sd->attributes()->orderable) : true;
$this->path = trim($sd->path) !== '' ? p4string::addEndSlash(trim($sd->path)) : '';
$this->requiresMetadataUpdate = p4field::isyes((string) $sd->meta);
@@ -110,7 +115,130 @@ class databox_subdef
}
/**
* Build Image Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Image
*/
protected function buildImageSubdef(SimpleXMLElement $sd)
{
$image = new Image($this->translator);
if ($sd->icodec) {
$image->setOptionValue(Image::OPTION_ICODEC, (string) $sd->icodec);
}
if ($sd->size) {
$image->setOptionValue(Image::OPTION_SIZE, (int) $sd->size);
}
if ($sd->quality) {
$image->setOptionValue(Image::OPTION_QUALITY, (int) $sd->quality);
}
if ($sd->strip) {
$image->setOptionValue(Image::OPTION_STRIP, p4field::isyes($sd->strip));
}
if ($sd->dpi) {
$image->setOptionValue(Image::OPTION_RESOLUTION, (int) $sd->dpi);
}
if ($sd->flatten) {
$image->setOptionValue(Image::OPTION_FLATTEN, p4field::isyes($sd->flatten));
}
return $image;
}
/**
* Build Audio Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Audio
*/
protected function buildAudioSubdef(SimpleXMLElement $sd)
{
$audio = new Audio($this->translator);
if ($sd->acodec) {
$audio->setOptionValue(Audio::OPTION_ACODEC, (string) $sd->acodec);
}
if ($sd->audiobitrate) {
$audio->setOptionValue(Audio::OPTION_AUDIOBITRATE, (int) $sd->audiobitrate);
}
if ($sd->audiosamplerate) {
$audio->setOptionValue(Audio::OPTION_AUDIOSAMPLERATE, (int) $sd->audiosamplerate);
}
return $audio;
}
/**
* Build Video Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Video
*/
protected function buildVideoSubdef(SimpleXMLElement $sd)
{
$video = new Video($this->translator);
if ($sd->size) {
$video->setOptionValue(Video::OPTION_SIZE, (int) $sd->size);
}
if ($sd->acodec) {
$video->setOptionValue(Video::OPTION_ACODEC, (string) $sd->acodec);
}
if ($sd->vcodec) {
$video->setOptionValue(Video::OPTION_VCODEC, (string) $sd->vcodec);
}
if ($sd->fps) {
$video->setOptionValue(Video::OPTION_FRAMERATE, (int) $sd->fps);
}
if ($sd->bitrate) {
$video->setOptionValue(Video::OPTION_BITRATE, (int) $sd->bitrate);
}
if ($sd->audiobitrate) {
$video->setOptionValue(Video::OPTION_AUDIOBITRATE, (int) $sd->audiobitrate);
}
if ($sd->audiosamplerate) {
$video->setOptionValue(Video::OPTION_AUDIOSAMPLERATE, (int) $sd->audiosamplerate);
}
if ($sd->GOPsize) {
$video->setOptionValue(Video::OPTION_GOPSIZE, (int) $sd->GOPsize);
}
return $video;
}
/**
* Build GIF Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Gif
*/
protected function buildGifSubdef(SimpleXMLElement $sd)
{
$gif = new Gif($this->translator);
if ($sd->size) {
$gif->setOptionValue(Gif::OPTION_SIZE, (int) $sd->size);
}
if ($sd->delay) {
$gif->setOptionValue(Gif::OPTION_DELAY, (int) $sd->delay);
}
return $gif;
}
/**
* Build Flexpaper Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return FlexPaper
*/
protected function buildFlexPaperSubdef(SimpleXMLElement $sd)
{
return new FlexPaper($this->translator);
}
/**
* @return string
*/
public function get_class()
@@ -119,7 +247,6 @@ class databox_subdef
}
/**
*
* @return string
*/
public function get_path()
@@ -130,7 +257,7 @@ class databox_subdef
/**
* The devices matching this subdefinition
*
* @return Array
* @return array
*/
public function getDevices()
{
@@ -140,7 +267,7 @@ class databox_subdef
/**
* The current SubdefType the subdef converts documents
*
* @return Alchemy\Phrasea\Media\Subdef\Subdef
* @return \Alchemy\Phrasea\Media\Subdef\Subdef
*/
public function getSubdefType()
{
@@ -160,7 +287,7 @@ class databox_subdef
/**
* An associative label ; keys are i18n languages
*
* @return Array
* @return array
*/
public function get_labels()
{
@@ -179,15 +306,31 @@ class databox_subdef
}
/**
* boolean
* The name of the subdef
*
* @return type
* @return string
*/
public function is_downloadable()
public function get_name()
{
return $this->name;
}
/**
* @return bool
*/
public function isDownloadable()
{
return $this->downloadable;
}
/**
* @return bool
*/
public function isOrderable()
{
return $this->orderable;
}
/**
* Get an array of Alchemy\Phrasea\Media\Subdef\Subdef available for the current Media Type
*
@@ -254,16 +397,6 @@ class databox_subdef
return $this->requiresMetadataUpdate;
}
/**
* The name of the subdef
*
* @return string
*/
public function get_name()
{
return $this->name;
}
/**
* Get the MediaAlchemyst specs for the current subdef
*
@@ -283,128 +416,4 @@ class databox_subdef
{
return $this->subdef_type->getOptions();
}
/**
* Build Image Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Image
*/
protected function buildImageSubdef(SimpleXMLElement $sd)
{
$image = new Image($this->translator);
if ($sd->icodec) {
$image->setOptionValue(Image::OPTION_ICODEC, (string) $sd->icodec);
}
if ($sd->size) {
$image->setOptionValue(Image::OPTION_SIZE, (int) $sd->size);
}
if ($sd->quality) {
$image->setOptionValue(Image::OPTION_QUALITY, (int) $sd->quality);
}
if ($sd->strip) {
$image->setOptionValue(Image::OPTION_STRIP, p4field::isyes($sd->strip));
}
if ($sd->dpi) {
$image->setOptionValue(Image::OPTION_RESOLUTION, (int) $sd->dpi);
}
if ($sd->flatten) {
$image->setOptionValue(Image::OPTION_FLATTEN, p4field::isyes($sd->flatten));
}
return $image;
}
/**
* Build Audio Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Audio
*/
protected function buildAudioSubdef(SimpleXMLElement $sd)
{
$audio = new Audio($this->translator);
if ($sd->acodec) {
$audio->setOptionValue(Audio::OPTION_ACODEC, (string) $sd->acodec);
}
if ($sd->audiobitrate) {
$audio->setOptionValue(Audio::OPTION_AUDIOBITRATE, (int) $sd->audiobitrate);
}
if ($sd->audiosamplerate) {
$audio->setOptionValue(Audio::OPTION_AUDIOSAMPLERATE, (int) $sd->audiosamplerate);
}
return $audio;
}
/**
* Build Flexpaper Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return \Alchemy\Phrasea\Media\Subdef\Video
*/
protected function buildFlexPaperSubdef(SimpleXMLElement $sd)
{
return new FlexPaper($this->translator);
}
/**
* Build GIF Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Gif
*/
protected function buildGifSubdef(SimpleXMLElement $sd)
{
$gif = new Gif($this->translator);
if ($sd->size) {
$gif->setOptionValue(Gif::OPTION_SIZE, (int) $sd->size);
}
if ($sd->delay) {
$gif->setOptionValue(Gif::OPTION_DELAY, (int) $sd->delay);
}
return $gif;
}
/**
* Build Video Subdef object depending the SimpleXMLElement
*
* @param SimpleXMLElement $sd
* @return Video
*/
protected function buildVideoSubdef(SimpleXMLElement $sd)
{
$video = new Video($this->translator);
if ($sd->size) {
$video->setOptionValue(Video::OPTION_SIZE, (int) $sd->size);
}
if ($sd->acodec) {
$video->setOptionValue(Video::OPTION_ACODEC, (string) $sd->acodec);
}
if ($sd->vcodec) {
$video->setOptionValue(Video::OPTION_VCODEC, (string) $sd->vcodec);
}
if ($sd->fps) {
$video->setOptionValue(Video::OPTION_FRAMERATE, (int) $sd->fps);
}
if ($sd->bitrate) {
$video->setOptionValue(Video::OPTION_BITRATE, (int) $sd->bitrate);
}
if ($sd->audiobitrate) {
$video->setOptionValue(Video::OPTION_AUDIOBITRATE, (int) $sd->audiobitrate);
}
if ($sd->audiosamplerate) {
$video->setOptionValue(Video::OPTION_AUDIOSAMPLERATE, (int) $sd->audiosamplerate);
}
if ($sd->GOPsize) {
$video->setOptionValue(Video::OPTION_GOPSIZE, (int) $sd->GOPsize);
}
return $video;
}
}

View File

@@ -10,6 +10,7 @@
use Alchemy\Phrasea\Databox\SubdefGroup;
use Alchemy\Phrasea\Media\MediaTypeFactory;
use Assert\Assertion;
use Symfony\Component\Translation\TranslatorInterface;
class databox_subdefsStructure implements IteratorAggregate, Countable
@@ -215,6 +216,25 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
return $this;
}
/**
* @param SubdefGroup[] $groups
*/
public function updateSubdefGroups($groups)
{
Assertion::allIsInstanceOf($groups, SubdefGroup::class);
$dom_xp = $this->databox->get_xpath_structure();
foreach ($groups as $group) {
$nodes = $dom_xp->query('//record/subdefs/subdefgroup[@name="' . $group->getName() . '"]');
/** @var DOMElement $node */
foreach ($nodes as $node) {
$node->setAttribute('document_orderable', ($group->isDocumentOrderable() ? 'true' : 'false'));
}
}
}
/**
* @param string $group
* @param string $name
@@ -222,10 +242,11 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
* @param boolean $downloadable
* @param array $options
* @param array $labels
* @param bool $orderable
* @return databox_subdefsStructure
* @throws Exception
*/
public function set_subdef($group, $name, $class, $downloadable, $options, $labels)
public function set_subdef($group, $name, $class, $downloadable, $options, $labels, $orderable = true)
{
$dom_struct = $this->databox->get_dom_structure();
@@ -233,6 +254,7 @@ class databox_subdefsStructure implements IteratorAggregate, Countable
$subdef->setAttribute('class', $class);
$subdef->setAttribute('name', mb_strtolower($name));
$subdef->setAttribute('downloadable', ($downloadable ? 'true' : 'false'));
$subdef->setAttribute('orderable', ($orderable ? 'true' : 'false'));
foreach ($labels as $code => $label) {
$child = $dom_struct->createElement('label');

View File

@@ -165,7 +165,7 @@ class eventsmanager_broker
}
if (((int) $page + 1) * $n < $total) {
$data['next'] = '<a href="#" onclick="print_notifications(' . ((int) $page + 1) . ');return false;">' . $this->app->trans('charger d\'avantages de notifications') . '</a>';
$data['next'] = '<a href="#" class="notification__print-action" data-page="' . ((int) $page + 1) . '">' . $this->app->trans('charger d\'avantages de notifications') . '</a>';
}
return $data;

View File

@@ -39,8 +39,11 @@ class eventsmanager_notify_push extends eventsmanager_notifyAbstract
$sender = $user->getDisplayName();
$ret = [
'text' => $this->app->trans('%user% vous a envoye un %before_link% panier %after_link%', ['%user%' => $sender, '%before_link%' => '<a href="#" onclick="openPreview(this, \'BASK\',1,\''
. $data['ssel_id'] . '\');return false;">', '%after_link%' => '</a>'])
'text' => $this->app->trans('%user% vous a envoye un %before_link% panier %after_link%', ['%user%' => $sender, '%before_link%' => '<a href="#"
data-kind="BASK"
data-position="1"
data-id="'. $data['ssel_id'] . '"
class="open-preview-action">', '%after_link%' => '</a>'])
, 'class' => ($unread == 1 ? 'reload_baskets' : '')
];

View File

@@ -53,8 +53,7 @@ class eventsmanager_notify_validationreminder extends eventsmanager_notifyAbstra
$basket_name = $this->app->trans('Une selection');
}
$bask_link = '<a href="#" onclick="openPreview(this, \'BASK\',1,\''
. $ssel_id . '\');return false;">'
$bask_link = '<a href="#" data-kind="BASK" data-position="1" data-id="'. $ssel_id . '" class="open-preview-action">'
. $basket_name . '</a>';
$ret = [

View File

@@ -1,9 +1,8 @@
<?php
/*
/**
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
* (c) 2005-2016 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
@@ -11,7 +10,6 @@
class p4field
{
public static function isyes($v)
{
$v = mb_strtolower(trim($v));

View File

@@ -108,7 +108,7 @@ class patch_370alpha6a extends patchAbstract
$options['meta'] = $subdef->isMetadataUpdateRequired() ? 'yes' : 'no';
$options['devices'] = [databox_subdef::DEVICE_SCREEN];
$root->set_subdef($groupname, $subdef->get_name(), $subdef->get_class(), $subdef->is_downloadable(), $options, []);
$root->set_subdef($groupname, $subdef->get_name(), $subdef->get_class(), $subdef->isDownloadable(), $options, []);
}
protected function addMobileSubdefVideo($root, $baseSubdef, $groupname)

View File

@@ -77,7 +77,7 @@ class patch_390alpha13a implements patchInterface
foreach ($rs as $row) {
try {
$user = $em->createQuery('SELECT PARTIAL u.{id} FROM Phraseanet:User s WHERE u.id = :id')
$user = $em->createQuery('SELECT PARTIAL u.{id} FROM Phraseanet:User u WHERE u.id = :id')
->setParameters(['id' => $row['usr_id']])
->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true)
->getSingleResult();

View File

@@ -122,18 +122,18 @@ class patch_390alpha6a extends patchAbstract
$stmt->execute(['export_id' => $row['id']]);
$rs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach ($rs as $element) {
foreach ($rs as $element_row) {
$element = new FtpExportElement();
$element->setBaseId($row['base_id'])
->setRecordId($row['record_id'])
->setBusinessfields($row['businessfields'])
$element->setBaseId($element_row['base_id'])
->setRecordId($element_row['record_id'])
->setBusinessfields($element_row['businessfields'])
->setCreated(new \DateTime($row['date']))
->setUpdated(new \DateTime($row['date']))
->setDone(!!$row['done'])
->setError(!!$row['error'])
->setFilename($row['filename'])
->setFolder($row['folder'])
->setSubdef($row['subdef'])
->setDone(!!$element_row['done'])
->setError(!!$element_row['error'])
->setFilename($element_row['filename'])
->setFolder($element_row['folder'])
->setSubdef($element_row['subdef'])
->setExport($export);
$export->addElement($element);

View File

@@ -1,286 +0,0 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2016 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
use Symfony\Component\Translation\TranslatorInterface;
class queries
{
public static function tree_topics($I18N)
{
$out = '';
$xmlTopics = null;
$sxTopics = null;
if (file_exists(__DIR__ . '/../../config/topics/topics_' . $I18N . '.xml'))
$xmlTopics = __DIR__ . '/../../config/topics/topics_' . $I18N . '.xml';
if (! $xmlTopics) {
if (file_exists(__DIR__ . '/../../config/topics/topics.xml')) {
$xmlTopics = __DIR__ . '/../../config/topics/topics.xml';
}
}
$cssTopics = '';
if ($xmlTopics && false !== $sxTopics = simplexml_load_file($xmlTopics)) {
$cssTopics = (string) $sxTopics->display->css;
}
$out .= '<style type="text/css">
' . $cssTopics . '
</style>';
$out .='<div class="searchZone" >
<div class="linktopics1" >';
if ($sxTopics) {
$defaultview = mb_strtolower($sxTopics->display->defaultview);
if ( ! $defaultview)
$defaultview = 'static';
$out .= ( "<ul id='TOPIC_UL' class='nobox'>\n");
$out .= self::drawTopics($sxTopics->topics, 0, '', $defaultview);
$out .= ( "\n</ul>\n");
}
$out .= '</div>
</div>';
return $out;
}
public static function topics_exists($I18n)
{
if (file_exists(__DIR__ . '/../../config/topics/topics_' . $I18n . '.xml')) {
return true;
}
if (file_exists(__DIR__ . '/../../config/topics/topics.xml')) {
return true;
}
return false;
}
public static function dropdown_topics(TranslatorInterface $translator, $I18n)
{
$out = '';
$xmlTopics = '';
$sxTopics = null;
if (file_exists(__DIR__ . '/../../config/topics/topics_' . $I18n . '.xml'))
$xmlTopics = __DIR__ . '/../../config/topics/topics_' . $I18n . '.xml';
if ($xmlTopics == '') {
if (file_exists(__DIR__ . '/../../config/topics/topics.xml')) {
$xmlTopics = __DIR__ . '/../../config/topics/topics.xml';
}
}
if ($xmlTopics == '') {
return '';
}
$jsTopics = 'null';
$maxdepth = 0;
if (false !== $sxTopics = simplexml_load_file($xmlTopics)) {
$jsTopics = self::topicsAsJS($sxTopics->topics, 0, $maxdepth);
}
$out .= ' <script type="text/javascript">
var maxdepth = ' . ($maxdepth + 1) . ';
var topics = ' . $jsTopics . ';
var current_popqry = "";
function doSearchTopPop(qry)
{
var qft = document.forms["pops"].qry.value;
if(qft != "" && current_popqry != "")
qry = "("+qft+") AND ("+current_popqry+")";
else
qry = qft+current_popqry;
if(qry=="")
qry = "all";
doSpecialSearch(qry,true);
return;
}
function chgPopTopic(ipop)
{
if(ipop > ' . ($maxdepth + 1) . ')
return;
var i,j;
var _topics = topics;
var zpop;
current_popqry = "";
for (i=0; _topics && i<ipop; i++) {
zpop = document.forms["pops"]["popTopic_"+i];
if((j = zpop.selectedIndex) > 0)
current_popqry = zpop.options[j].value;
j--;
if(_topics[j] && _topics[j].topics)
_topics = _topics[j].topics;
else
_topics = null;
}
if(ipop == ' . ($maxdepth + 1) . ')
return;
zpop = document.forms["pops"]["popTopic_"+ipop];
if (_topics) {
while(zpop.options[0])
zpop.options[0] = null;
zpop.options[0] = new Option("All", "");
for(j=0; j<_topics.length; j++)
zpop.options[j+1] = new Option(_topics[j].label, _topics[j].query);
zpop.selectedIndex = 0;
document.getElementById("divTopic_"+ipop).style.display = "";
} else {
document.getElementById("divTopic_"+ipop).style.display = "none";
}
while (++ipop <= ' . $maxdepth . ') {
document.getElementById("divTopic_"+ipop).style.display = "none";
}
}
</script>';
$out .= '<div class="searchZonePop" onload="chgPopTopic(0);">
<div class="linktopics1">
<form name="pops" onsubmit="return(false);" style="margin:0px; margin-left:5px; margin-right:5px">
<table>
<tr>
<td colspan="2">' . $translator->trans('boutton::chercher') . ' :
<input style="width:180px" type="text" name="qry"></td>
</tr>
</table>
' . $translator->trans('client::recherche: dans les categories') . '<br/>';
for ($i = 0; $i <= $maxdepth; $i ++) {
$out .= '<p id="divTopic_' . $i . '" style="margin:0px;margin-bottom:5px;" >
<select style="width:100%;" id="popTopic_' . $i . '" name="popTopic_' . $i . '" onchange="chgPopTopic(' . ($i + 1) . ');">
</select>
</p>';
}
$out .= '<div style="text-align:right;">
<input type="submit" value="' . $translator->trans('boutton::chercher') . '" onclick="doSearchTopPop();" />
</div>
</form>
</div>
</div>
<script>chgPopTopic(0);</script>';
return $out;
}
public static function history(Application $app, $usrId)
{
$history = '<ul>';
$queries = $app['repo.user-queries']->findBy(['user' => $usrId], ['created' => 'ASC'], 25, 0);
foreach ($queries as $query) {
$history .= '<li onclick="doSpecialSearch(\'' . str_replace(["'", '"'], ["\'", '&quot;'], $query->getQuery()) . '\')">' . $query->getQuery() . '</li>';
}
$history .= '<ul>';
return $history;
}
private static function hastopics(&$topics)
{
foreach ($topics->topics as $subtopic) {
return true;
}
return false;
}
private static function topicsAsJS($topics, $depth, &$maxdepth)
{
if ($depth > $maxdepth)
$maxdepth = $depth;
$t = '';
$tab = str_repeat("\t", $depth);
foreach ($topics->topic as $subtopic) {
$t .= $t ? "$tab, " : "$tab ";
$t .= '{ ';
$t .= 'label:"' . p4string::MakeString(utf8_decode($subtopic->label), 'js') . '"';
if ($q = $subtopic->query) {
$q = str_replace(["\\", "'", "\r", "\n"], ["\\\\", "\\'", "\\r", "\\n"], $subtopic->query);
$t .= ", query:'" . $q . "'";
} else {
$t .= ', query:null';
}
if (self::hastopics($subtopic)) {
$t .= ', topics:' . "\n" . self::topicsAsJS($subtopic->topics, $depth + 1, $maxdepth); //, $fullquery) ;
} else {
$t .= ', topics:null';
}
$t .= " }\n";
}
return("$tab" . "[\n" . $t . "\n$tab]");
}
private static function drawTopics($topics, $depth = 0, $triid = '', $defaultview)
{
$n = 0;
$out = '';
foreach ($topics->topic as $subtopic) {
$tid = $triid . '_' . $n;
$s = $subtopic->label;
$l = p4string::MakeString($s, 'html');
$l = '<span class=\'topic_' . $depth . '\'>' . $l . '</span>';
if ($subtopic->query) {
$q = str_replace(["\\", "\"", "'", "\r", "\n"], ["\\\\", "&quot;", "\\'", "\\r", "\\n"], $subtopic->query);
$q = '<a href="javascript:void();" onClick="doSpecialSearch(\'' . $q . '\',true);">' . $l . '</a>';
} else {
$q = $l;
}
if (self::hastopics($subtopic)) {
$view = mb_strtolower($subtopic['view']);
if ( ! $view)
$view = $defaultview;
switch ($view) {
case 'opened':
$out .= ( '<li><a id=\'TOPIC_TRI' . $tid . '\' class="opened" href="javascript:void();" onclick="clktri(\'' . $tid . '\');return(false);"></a>&nbsp;' . $q . '</li>' . "\n");
$out .= ( "<ul id='TOPIC_UL$tid' class='opened'>\n");
$out .= self::drawTopics($subtopic->topics, $depth + 1, $tid, $defaultview);
$out .= ( "</ul>\n<div style='height:1px;'></div>\n");
break;
case 'closed':
$out .= ( '<li><a id=\'TOPIC_TRI' . $tid . '\' class="closed" href="javascript:void();" onclick="clktri(\'' . $tid . '\');return(false);"></a>&nbsp;' . $q . '</li>' . "\n");
$out .= ( "<ul id='TOPIC_UL$tid' class='closed'>\n");
$out .= self::drawTopics($subtopic->topics, $depth + 1, $tid, $defaultview);
$out .= ( "</ul>\n<div style='height:1px;'></div>\n");
break;
case 'static':
default:
$out .= ( '<li><span id=\'TOPIC_TRI' . $tid . '\' class="static">&nbsp</span>&nbsp;' . $q . '</li>' . "\n");
$out .= ( "<ul id='TOPIC_UL$tid' class='static'>\n");
$out .= self::drawTopics($subtopic->topics, $depth + 1, $tid, $defaultview);
$out .= ( "</ul>\n<div style='height:1px;'></div>\n");
break;
}
} else {
$out .= ( '<li><span id=\'TOPIC_TRI' . $tid . '\' class="none">&nbsp</span>&nbsp;' . $q . '</li>' . "\n");
}
$n ++;
}
return $out;
}
}

View File

@@ -1334,9 +1334,10 @@ class record_adapter implements RecordInterface, cache_cacheableInterface
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
@unlink(\p4string::addEndSlash($row['path']) . 'stamp_' . $row['file']);
}
$stmt->closeCursor();
return $this;
$stmt->closeCursor();
return $this;
}
/**

View File

@@ -170,7 +170,7 @@ class record_exportElement extends record_adapter
$downloadable[$name] = false;
$downloadable_settings = $subdef->is_downloadable();
$downloadable_settings = $subdef->isDownloadable();
if (! $downloadable_settings || $go_dl[$class] === false) {
continue;

View File

@@ -212,3 +212,31 @@ embed_bundle:
document:
player: flexpaper
enable-pdfjs: true
geocoding-providers:
-
name: 'mapBox'
enabled: true
public-key: ''
default-position:
- 2.335062
- 48.879162
default-zoom: 2
marker-default-zoom: 11
position-fields:
-
name: GpsCompositePosition
type: latlng
# -
# name: Longitude
# type: lng
# -
# name: Latitude
# type: lat
video-editor:
vttFieldName: VideoTextTrackChapters
seekBackwardStep: 1000 # in ms
seekForwardStep: 1000 # in ms
playbackRates:
- 1
- '1.5'
- 3

View File

@@ -25,11 +25,16 @@
"tmp": "0.0.23",
"wrench": "^1.5.8"
},
"engines" : {
"node" : ">=0.12"
"engines": {
"node": ">=5.8"
},
"scripts": {
"dev": "./node_modules/.bin/gulp sync;",
"build": "./node_modules/.bin/gulp build;",
"postinstall": "./node_modules/.bin/gulp install;"
},
"dependencies": {
"alchemy-embed-medias": "^0.4.4",
"phraseanet-production-client": "~0.19.23"
}
}

View File

@@ -9,6 +9,7 @@
- nginx
- mariadb
- elasticsearch
- rabbitmq
- php
- xdebug
- composer

View File

@@ -7,4 +7,4 @@
- name: Install specific nodejs version
become: yes
become_user: vagrant
shell: export NVM_DIR="$HOME/.nvm" &&. ~/.nvm/nvm.sh && nvm install 0.12.0 && nvm alias default 0.12.0
shell: export NVM_DIR="$HOME/.nvm" &&. ~/.nvm/nvm.sh && nvm install 0.12.16 && nvm alias default 0.12.16

View File

@@ -0,0 +1,27 @@
---
- name: Add package repository
sudo: yes
shell: echo 'deb http://www.rabbitmq.com/debian/ testing main' > /etc/apt/sources.list.d/rabbitmq.list
- name: Install package repository key
sudo: yes
shell: wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc && apt-key add rabbitmq-signing-key-public.asc
- name: Remove signing key
sudo: yes
shell: rm rabbitmq-signing-key-public.asc
- name: Update apt
sudo: yes
apt: update_cache=yes
- name: Install server and libraries
sudo: yes
apt: pkg={{ item }} state=latest allow_unauthenticated=yes
with_items:
- rabbitmq-server
- librabbitmq-dev
- name: Enable management plugin
sudo: yes
shell: rabbitmq-plugins enable rabbitmq_management

View File

@@ -59,6 +59,7 @@ php:
packages: [php5-cli, php5-intl, php5-mcrypt, php5-enchant, php5-gd, php5-imagick, php5-memcache, php5-memcached, php5-curl, php5-mysql, php5-sqlite]
pecl_packages:
- {name: zmq, package: zmq-beta}
- {name: amqp, package: amqp-1.4.0}
xdebug:
install: '1'
composer:

View File

@@ -26,16 +26,15 @@ gulp.task('build', ['build-vendors'], function(){
// standalone vendors used across application
gulp.task('build-vendors', [
'build-alchemy-embed',
'build-alchemy-embed-medias',
'build-phraseanet-production-client',
'build-bootstrap',
'build-colorpicker',
'build-html5shiv',
'build-jquery',
'build-jquery-ui', // will build themes too
'build-jquery-mobile',
'build-jquery-galleria',
'build-jquery-file-upload',
// 'build-jquery-image-enhancer', //bundled in prod only
'build-json2',
'build-modernizr',
'build-zxcvbn',
@@ -50,4 +49,4 @@ gulp.task('build-vendors', [
'build-jquery-treeview',
'build-jquery-lazyload',
'build-jquery-test-paths'
], function() {});
], function() {});

View File

@@ -13,19 +13,6 @@ gulp.task('build-account-css', function(){
], 'account', 'account/css/', debugMode);
});
gulp.task('build-account-js', function(){
var accountGroup = [
config.paths.vendors + 'requirejs/require.js',
config.paths.src + 'account/js/account.js'
];
return utils.buildJsGroup(accountGroup, 'account', 'account/js', debugMode);
});
gulp.task('watch-account-js', function() {
debugMode = true;
return gulp.watch(config.paths.src + 'account/**/*.js', ['build-account-js']);
});
gulp.task('watch-account-css', function() {
debugMode = true;
gulp.watch(config.paths.src + 'account/**/*.scss', ['build-account-css']);
@@ -33,5 +20,5 @@ gulp.task('watch-account-css', function() {
gulp.task('build-account', ['copy-account-images', 'build-account-css'], function(){
debugMode = false;
return gulp.start('build-account-js');
});
return gulp.start('build-account-css');
});

View File

@@ -26,6 +26,8 @@ gulp.task('build-common-css', ['build-common-font-css'],function(){
gulp.task('build-common-js', function(){
var commonGroup = [
config.paths.src + 'common/js/components/utils.js',
config.paths.src + 'common/js/components/user.js',
// config.paths.dist + 'assets/bootstrap/js/bootstrap.js', // should append no conflict
config.paths.src + 'vendors/jquery-mousewheel/js/jquery.mousewheel.js',
// jquery ui date picker langs
@@ -37,9 +39,9 @@ gulp.task('build-common-js', function(){
config.paths.vendors + 'jquery-ui/ui/i18n/jquery.ui.datepicker-en-GB.js',
config.paths.vendors + 'jquery.cookie/jquery.cookie.js',
config.paths.src + 'vendors/jquery-contextmenu/js/jquery.contextmenu_custom.js',
config.paths.src + 'common/js/jquery.common.js',
config.paths.src + 'common/js/jquery.tooltip.js',
config.paths.src + 'common/js/jquery.Dialog.js'
config.paths.src + 'common/js/components/common.js',
config.paths.src + 'common/js/components/tooltip.js',
config.paths.src + 'common/js/components/dialog.js'
];
return utils.buildJsGroup(commonGroup, 'common', 'common/js', debugMode);
});
@@ -57,4 +59,4 @@ gulp.task('watch-common-css', function() {
gulp.task('build-common', ['copy-common-images', 'build-common-css'], function(){
debugMode = false;
return gulp.start('build-common-js');
});
});

View File

@@ -14,48 +14,18 @@ gulp.task('build-lightbox-mobile-css', function(){
], 'lightbox-mobile', 'lightbox/css/', debugMode);
});
gulp.task('build-lightbox-mobile-js', function(){
return utils.buildJsGroup([
config.paths.src + 'lightbox/js/jquery.validator.mobile.js'
], 'lightbox-mobile', 'lightbox/js', debugMode);
});
gulp.task('build-lightbox-ie6-css', function(){
return utils.buildCssGroup([
config.paths.src + 'lightbox/styles/main-ie6.scss'
], 'lightbox-ie6', 'lightbox/css/', debugMode)
});
gulp.task('build-lightbox-css', ['build-lightbox-mobile-css', 'build-lightbox-ie6-css'], function(){
gulp.task('build-lightbox-css', ['build-lightbox-mobile-css'], function(){
return utils.buildCssGroup([
config.paths.src + 'lightbox/styles/main.scss'
], 'lightbox', 'lightbox/css/', debugMode)
});
gulp.task('build-lightbox-js', ['build-lightbox-mobile-js'], function(){
var lightboxGroup = [
config.paths.src + 'lightbox/js/jquery.lightbox.js'
];
var lightboxIE6Group = [
config.paths.src + 'lightbox/js/jquery.lightbox.ie6.js'
];
utils.buildJsGroup(lightboxIE6Group, 'lightboxIe6', 'lightbox/js', debugMode);
return utils.buildJsGroup(lightboxGroup, 'lightbox', 'lightbox/js', debugMode);
});
gulp.task('watch-lightbox-js', function() {
debugMode = true;
return gulp.watch(config.paths.src + 'lightbox/**/*.js', ['build-lightbox-js']);
});
gulp.task('watch-lightbox-css', function() {
debugMode = true;
gulp.watch(config.paths.src + 'lightbox/**/*.scss', ['build-lightbox-css']);
});
gulp.task('build-lightbox', ['copy-lightbox-images', 'build-lightbox-css'], function(){
gulp.task('build-lightbox', ['copy-lightbox-images'], function(){
debugMode = false;
return gulp.start('build-lightbox-js');
});
return gulp.start('build-lightbox-css');
});

View File

@@ -14,107 +14,17 @@ gulp.task('build-uploadFlash', function(){
return utils.buildJsGroup(uploadFlashGroup, 'uploadFlash', 'upload/js');
});
gulp.task('copy-prod-skin-black-images', function(){
return gulp.src([
config.paths.src + 'prod/skins/000000/images/**/*'
])
.pipe(gulp.dest( config.paths.build + 'prod/skins/000000/images'));
});
gulp.task('copy-prod-skin-grey-images', function(){
return gulp.src([
config.paths.src + 'prod/skins/959595/images/**/*'
])
.pipe(gulp.dest( config.paths.build + 'prod/skins/959595/images'));
});
gulp.task('copy-prod-skin-white-images', function(){
return gulp.src([
config.paths.src + 'prod/skins/FFFFFF/images/**/*'
])
.pipe(gulp.dest( config.paths.build + 'prod/skins/FFFFFF/images'));
});
gulp.task('copy-prod-images', function(){
return gulp.src([config.paths.src + 'prod/images/**/*'])
.pipe(gulp.dest( config.paths.build + 'prod/images'));
});
gulp.task('build-prod-skin-black', ['copy-prod-skin-black-images'], function(){
return utils.buildCssGroup([
config.paths.src + 'prod/skins/000000/skin-000000.scss'
], 'skin-000000', 'prod/skins/000000/', debugMode);
});
gulp.task('build-prod-skin-grey', ['copy-prod-skin-grey-images'], function(){
return utils.buildCssGroup([
config.paths.src + 'prod/skins/959595/skin-959595.scss'
], 'skin-959595', 'prod/skins/959595/', debugMode);
});
gulp.task('build-prod-skin-white', ['copy-prod-skin-white-images'], function(){
return utils.buildCssGroup([
config.paths.src + 'prod/skins/FFFFFF/skin-FFFFFF.scss'
], 'skin-FFFFFF', 'prod/skins/FFFFFF/', debugMode);
});
gulp.task('build-prod-css', ['build-prod-skin-black', 'build-prod-skin-grey', 'build-prod-skin-white'], function(){
return utils.buildCssGroup([
config.paths.src + 'prod/styles/main.scss'
], 'prod', 'prod/css/', debugMode);
});
gulp.task('build-prod-js', function(){
var prodGroup = [
config.paths.vendors + 'underscore-amd/underscore.js',
config.paths.src + 'vendors/colorpicker/js/colorpicker.js',
config.paths.vendors + 'jquery.lazyload/jquery.lazyload.js',
config.paths.vendors + 'humane-js/humane.js', // @TODO > extra files
config.paths.vendors + 'blueimp-load-image/js/load-image.js', // @TODO > extra files
config.paths.vendors + 'jquery-file-upload/js/jquery.iframe-transport.js',
config.paths.vendors + 'jquery-file-upload/js/jquery.fileupload.js',
config.paths.vendors + 'geonames-server-jquery-plugin/jquery.geonames.js',
config.paths.src + 'prod/js/components/publication.js',
config.paths.src + 'prod/js/jquery.form.2.49.js',
config.paths.src + 'prod/js/jquery.Selection.js',
config.paths.src + 'prod/js/jquery.Edit.js',
config.paths.src + 'prod/js/jquery.lists.js',
config.paths.src + 'prod/js/jquery.Prod.js',
config.paths.src + 'prod/js/jquery.Feedback.js',
config.paths.src + 'prod/js/jquery.Results.js',
config.paths.src + 'prod/js/jquery.main-prod.js',
config.paths.src + 'prod/js/jquery.WorkZone.js',
config.paths.src + 'prod/js/jquery.Alerts.js',
config.paths.src + 'prod/js/jquery.Upload.js',
config.paths.src + 'prod/js/ThumbExtractor.js',
config.paths.src + 'prod/js/publicator.js',
config.paths.src + 'vendors/jquery-sprintf/js/jquery.sprintf.1.0.3.js',
config.paths.src + 'prod/js/jquery.p4.preview.js',
config.paths.src + 'prod/js/record.editor.js',
config.paths.src + 'prod/js/jquery.color.animation.js',
config.paths.src + 'vendors/jquery-image-enhancer/js/jquery.image_enhancer.js',
config.paths.vendors + 'jquery-treeview/jquery.treeview.js',
config.paths.vendors + 'jquery-treeview/jquery.treeview.async.js',
config.paths.vendors + 'fancytree/dist/jquery.fancytree-all.min.js'
];
return utils.buildJsGroup(prodGroup, 'prod', 'prod/js', debugMode);
});
gulp.task('test-prod', function () {
return gulp.src(config.paths.src + 'prod/js/tests/*.html')
.pipe(qunit());
});
gulp.task('watch-prod-js', function() {
debugMode = true;
return gulp.watch(config.paths.src + 'prod/**/*.js', ['build-prod-js']);
});
gulp.task('watch-prod-css', function() {
debugMode = true;
return gulp.watch(config.paths.src + 'prod/**/*.scss', ['build-prod-css']);
});
gulp.task('build-prod', ['copy-prod-images', 'build-prod-css'], function(){
gulp.task('build-prod', [], function(){
debugMode = false;
return gulp.start('build-prod-js');
return gulp.start('copy-prod-images');
});

View File

@@ -17,9 +17,7 @@ gulp.task('build-thesaurus-js', function(){
var thesaurusGroup = [
config.paths.src + 'vendors/jquery-sprintf/js/jquery.sprintf.1.0.3.js',
config.paths.src + 'thesaurus/js/win.js',
config.paths.src + 'thesaurus/js/xmlhttp.js',
config.paths.src + 'thesaurus/js/thesaurus.js',
config.paths.src + 'thesaurus/js/sprintf.js'
config.paths.src + 'thesaurus/js/xmlhttp.js'
];
return utils.buildJsGroup(thesaurusGroup, 'thesaurus', 'thesaurus/js', debugMode);
});
@@ -36,4 +34,4 @@ gulp.task('watch-thesaurus-css', function() {
gulp.task('build-thesaurus', ['copy-thesaurus-images', 'build-thesaurus-css'], function(){
return gulp.start('build-thesaurus-js');
});
});

View File

@@ -4,25 +4,22 @@ var utils = require('../../utils.js');
var debugMode = false;
// for dev purposes
gulp.task('copy-alchemy-embed-debug', function(){
gulp.task('copy-alchemy-embed-medias-debug', function(){
debugMode = true;
gulp.start('copy-alchemy-embed');
gulp.start('copy-alchemy-embed-medias');
});
gulp.task('copy-alchemy-embed', function(){
gulp.task('copy-alchemy-embed-medias', function(){
// copy all dist folder:
if( debugMode === true) {
return gulp.src('vendor/alchemy/embed-bundle/dist/**/*')
.pipe(gulp.dest( config.paths.build + 'vendors/alchemy-embed-medias'));
}
return gulp.src(config.paths.vendors + 'alchemy-embed-medias/dist/**/*')
return gulp.src('node_modules/alchemy-embed-medias/dist/**/*')
.pipe(gulp.dest( config.paths.build + 'vendors/alchemy-embed-medias'));
});
gulp.task('watch-alchemy-embed-js', function() {
gulp.task('watch-alchemy-embed-medias-js', function() {
debugMode = true;
// in dev mode, watch composer's vendor path:
return gulp.watch('vendor/alchemy/embed-bundle/dist/**/*', ['copy-alchemy-embed']);
return gulp.watch('node_modules/alchemy-embed-medias/dist/**/*', ['copy-alchemy-embed-medias']);
});
gulp.task('build-alchemy-embed-medias', function(){
gulp.start('copy-alchemy-embed-medias');
});
gulp.task('build-alchemy-embed', function(){
gulp.start('copy-alchemy-embed');
});

View File

@@ -0,0 +1,25 @@
var gulp = require('gulp');
var config = require('../../config.js');
var utils = require('../../utils.js');
var debugMode = false;
// for dev purposes
gulp.task('copy-phraseanet-production-client-debug', function(){
debugMode = true;
gulp.start('copy-phraseanet-production-client');
});
gulp.task('copy-phraseanet-production-client', function(){
// copy all dist folder:
return gulp.src('node_modules/phraseanet-production-client/dist/**/*')
.pipe(gulp.dest( config.paths.build + 'production'));
});
gulp.task('watch-phraseanet-production-client-js', function() {
debugMode = true;
// in dev mode, watch composer's vendor path:
return gulp.watch('node_modules/phraseanet-production-client/dist/**/*', ['copy-phraseanet-production-client']);
});
gulp.task('build-phraseanet-production-client', function(){
gulp.start('copy-phraseanet-production-client');
});

View File

@@ -1,20 +0,0 @@
var gulp = require('gulp');
var config = require('../../config.js');
var utils = require('../../utils.js');
gulp.task('copy-colorpicker-images', function(){
return gulp.src([config.paths.src + 'vendors/colorpicker/images/**/*'])
.pipe(gulp.dest( config.paths.build + 'vendors/colorpicker/images'));
});
gulp.task('build-colorpicker-css', function(){
return utils.buildCssGroup([
config.paths.src + 'vendors/colorpicker/styles/colorpicker.scss'
], 'colorpicker', 'vendors/colorpicker');
});
gulp.task('build-colorpicker', ['build-colorpicker-css', 'copy-colorpicker-images'], function(){
return utils.buildJsGroup([
config.paths.src + 'vendors/colorpicker/js/colorpicker.js'
], 'colorpicker', 'vendors/colorpicker');
});

View File

@@ -1,17 +0,0 @@
var gulp = require('gulp');
var config = require('../../config.js');
var utils = require('../../utils.js');
gulp.task('build-jquery-image-enhancer-css', function(){
return utils.buildCssGroup([
config.paths.src + 'vendors/jquery-image-enhancer/styles/jquery.image_enhancer.scss'
], 'jquery-image-enhancer', 'vendors/jquery-image-enhancer');
});
gulp.task('build-jquery-image-enhancer', ['build-jquery-image-enhancer-css'], function(){
return utils.buildJsGroup([
config.paths.src + 'vendors/jquery-image-enhancer/js/jquery.image_enhancer.js'
], 'jquery-image-enhancer', 'vendors/jquery-image-enhancer');
});

View File

@@ -35,7 +35,7 @@ exports.buildJsGroup = function(srcGroup, name, dest, debugMode){
compress: {
drop_console: true
}
}).on('error', config.errorHandler('UGLIFY ERROR'))) //util.log))
}).on('error', config.errorHandler('UGLIFY ERROR')))
.pipe(rename({ extname: '.min.js' }))
.pipe(gulp.dest( config.paths.build + dest))
};
@@ -66,4 +66,4 @@ exports.buildCssGroup = function(srcGroup, name, dest, debugMode){
.pipe(cssmin())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest(config.paths.build + dest));
};
};

View File

@@ -8,7 +8,6 @@ var utils = require('./utils.js');
gulp.task('watch-css', function(){
gulp.start('watch-common-css');
gulp.start('watch-oauth-css');
gulp.start('watch-prod-css');
gulp.start('watch-thesaurus-css');
//gulp.start('watch-uploadFlash');
gulp.start('watch-lightbox-css');
@@ -23,17 +22,15 @@ gulp.task('watch-css', function(){
gulp.task('watch-js', function(){
gulp.start('watch-common-js');
// gulp.start('watch-oauth-js');
gulp.start('watch-prod-js');
gulp.start('watch-thesaurus-js');
//gulp.start('watch-uploadFlash');
gulp.start('watch-lightbox-js');
gulp.start('watch-admin-js');
gulp.start('watch-report-js');
gulp.start('watch-account-js');
// gulp.start('watch-permaview');
gulp.start('watch-setup-js');
gulp.start('watch-authentication-js');
gulp.start('watch-alchemy-embed-js');
gulp.start('watch-alchemy-embed-medias-js');
gulp.start('watch-phraseanet-production-client-js');
});
gulp.task('watch', function(){
@@ -46,7 +43,7 @@ var browserSync = require('browser-sync').create();
gulp.task('sync', ['watch'], function(){
// will open browser in http://localhost:3000/
browserSync.init({
proxy: "phraseanet-php55-nginx"
proxy: "dev.phraseanet.vb"
});
gulp.watch(config.paths.build + '**/*.css').on('change', browserSync.reload);
gulp.watch(config.paths.build + '**/*.js').on('change', browserSync.reload);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More