diff --git a/bin/console b/bin/console
index 552cb672d0..167636b105 100755
--- a/bin/console
+++ b/bin/console
@@ -23,7 +23,9 @@ use Alchemy\Phrasea\Command\SearchEngine\IndexPopulateCommand;
use Alchemy\Phrasea\Command\Thesaurus\FindConceptsCommand;
use Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Command\CreateCollection;
+use Alchemy\Phrasea\Command\Collection\ListCollectionCommand;
use Alchemy\Phrasea\Command\Databox\CreateDataboxCommand;
+use Alchemy\Phrasea\Command\Databox\ListDataboxCommand;
use Alchemy\Phrasea\Command\MailTest;
use Alchemy\Phrasea\Command\Compile\Configuration;
use Alchemy\Phrasea\Command\RecordAdd;
@@ -46,6 +48,8 @@ use Alchemy\Phrasea\Command\Task\TaskRun;
use Alchemy\Phrasea\Command\Task\TaskStart;
use Alchemy\Phrasea\Command\Task\TaskState;
use Alchemy\Phrasea\Command\Task\TaskStop;
+use Alchemy\Phrasea\Command\User\UserSetPasswordCommand;
+use Alchemy\Phrasea\Command\User\UserListCommand;
use Alchemy\Phrasea\Command\UpgradeDBDatas;
require_once __DIR__ . '/../lib/autoload.php';
@@ -108,8 +112,15 @@ $cli->command(new \module_console_fieldsRename('fields:rename'));
$cli->command(new \module_console_fieldsMerge('fields:merge'));
$cli->command(new CreateCollection('collection:create'));
+$cli->command(new ListCollectionCommand('collection:list'));
+
+$cli->command(new ListDataboxCommand('databox:list'));
$cli->command(new CreateDataboxCommand('databox:create'));
+
+$cli->command(new UserSetPasswordCommand('user:set-password'));
+$cli->command(new UserListCommand('user:list'));
+
$cli->command(new RecordAdd('records:add'));
$cli->command(new RescanTechnicalDatas('records:rescan-technical-datas'));
$cli->command(new BuildMissingSubdefs('records:build-missing-subdefs'));
diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml
index b7fef2618a..f3c61ff9b5 100644
--- a/config/configuration.sample.yml
+++ b/config/configuration.sample.yml
@@ -63,12 +63,6 @@ main:
mp4box_timeout: 60
swftools_timeout: 60
unoconv_timeout: 60
- task-manager:
- status: started
- listener:
- protocol: tcp
- host: 127.0.0.1
- port: 6700
storage:
subdefs: null
cache: null
@@ -76,20 +70,7 @@ main:
download: null
lazaret: null
caption: null
- bridge:
- youtube:
- enabled: false
- client_id: null
- client_secret: null
- developer_key: null
- flickr:
- enabled: false
- client_id: null
- client_secret: null
- dailymotion:
- enabled: false
- client_id: null
- client_secret: null
+ tmp_files: null
border-manager:
enabled: true
extension-mapping:
@@ -99,12 +80,17 @@ border-manager:
-
type: Checker\Sha256
enabled: true
+ collections: []
+ compare-ignore-collections: []
-
type: Checker\UUID
enabled: true
+ collections: []
+ compare-ignore-collections: []
-
type: Checker\Colorspace
enabled: false
+ collections: []
options:
colorspaces: [cmyk, grayscale, rgb]
media_types: [Image]
@@ -124,6 +110,8 @@ border-manager:
enabled: false
options:
sensitive: true
+ collections: []
+ compare-ignore-collections: []
-
type: Checker\MediaType
enabled: false
@@ -245,6 +233,14 @@ embed_bundle:
document:
player: flexpaper
enable_pdfjs: true
+video-editor:
+ ChapterVttFieldName: VideoTextTrackChapters
+ seekBackwardStep: 500 # in ms
+ seekForwardStep: 500 # in ms
+ playbackRates:
+ - 1
+ - '1.5'
+ - 3
geocoding-providers:
-
map-provider: mapboxWebGL
diff --git a/lib/Alchemy/Phrasea/Command/Collection/ListCollectionCommand.php b/lib/Alchemy/Phrasea/Command/Collection/ListCollectionCommand.php
new file mode 100644
index 0000000000..e76796e8b7
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Collection/ListCollectionCommand.php
@@ -0,0 +1,76 @@
+setDescription('List all collection in Phraseanet')
+ ->addOption('databox_id', 'd', InputOption::VALUE_REQUIRED, 'The id of the databox to list collection')
+ ->setHelp('');
+ return $this;
+ }
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ try {
+ $databox = $this->container->findDataboxById($input->getOption('databox_id'));
+ $collections = $this->listDataboxCollections($databox);
+
+ $table = $this->getHelperSet()->get('table');
+ $table
+ ->setHeaders(['id local for API', 'id distant', 'name','label','status','total records'])
+ ->setRows($collections)
+ ->render($output);
+ } catch (\Exception $e) {
+ $output->writeln("{$e->getMessage()}");
+ }
+ return 0;
+ }
+
+ private function listDataboxCollections(\databox $databox)
+ {
+ return array_map(function (\collection $collection) {
+ return $this->listCollection($collection);
+ }, array_merge($databox->get_collections(),$this->getUnabledCollection($databox->get_activable_colls())));
+ }
+
+ private function getUnabledCollection($collections)
+ {
+ return array_map(function ($colId){
+ return \collection::getByBaseId($this->container, $colId);
+ },$collections);
+
+ }
+
+ private function listCollection(\collection $collection)
+ {
+ return [
+ $collection->get_base_id(),
+ $collection->get_coll_id(),
+ $collection->get_name(),
+ 'en: ' . $collection->get_label('en') .
+ ', de: ' . $collection->get_label('de') .
+ ', fr: ' . $collection->get_label('fr') .
+ ', nl: ' . $collection->get_label('nl'),
+ ($collection->is_active()) ? 'enabled' : 'disabled',
+ $collection->get_record_amount()
+ ];
+ }
+}
\ No newline at end of file
diff --git a/lib/Alchemy/Phrasea/Command/Databox/ListDataboxCommand.php b/lib/Alchemy/Phrasea/Command/Databox/ListDataboxCommand.php
new file mode 100644
index 0000000000..49775437d5
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/Databox/ListDataboxCommand.php
@@ -0,0 +1,62 @@
+setDescription('List all databox in Phraseanet')
+ ->setHelp('');
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+ try {
+ $databoxes = array_map(function (\databox $databox) {
+ return $this->listDatabox($databox);
+ }, $this->container->getApplicationBox()->get_databoxes());
+
+ $table = $this->getHelperSet()->get('table');
+ $table
+ ->setHeaders(['id', 'name', 'alias'])
+ ->setRows($databoxes)
+ ->render($output);
+
+ } catch (\Exception $e) {
+ $output->writeln('Listing databox failed : '.$e->getMessage().'');
+ }
+
+ return 0;
+ }
+
+ private function listDatabox(\databox $databox)
+ {
+ return [
+ $databox->get_sbas_id(),
+ $databox->get_dbname(),
+ $databox->get_viewname()
+ ];
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/Command/User/UserListCommand.php b/lib/Alchemy/Phrasea/Command/User/UserListCommand.php
new file mode 100644
index 0000000000..1777c017cb
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/User/UserListCommand.php
@@ -0,0 +1,266 @@
+setDescription('List of all user')
+ ->addOption('user_id', null, InputOption::VALUE_OPTIONAL, ' The id of user export only info this user ')
+ ->addOption('user_email', null, InputOption::VALUE_OPTIONAL, 'The mail of user export only info this user .')
+ ->addOption('database_id', null, InputOption::VALUE_OPTIONAL, 'Id of database.')
+ ->addOption('collection_id', null, InputOption::VALUE_OPTIONAL, 'Id of the collection.')
+ ->addOption('mail_lock_status', null, InputOption::VALUE_NONE, 'Status by mail locked')
+ ->addOption('guest', null, InputOption::VALUE_NONE, 'Only guest user')
+ ->addOption('created', null, InputOption::VALUE_OPTIONAL, 'Created at with operator,aaaa-mm-jj hh:mm:ss.')
+ ->addOption('updated', null, InputOption::VALUE_OPTIONAL, 'Update at with operator,aaaa-mm-jj hh:mm:ss.')
+ ->addOption('application', null, InputOption::VALUE_NONE, 'List application of user work only if --user_id is set')
+ ->addOption('right', null, InputOption::VALUE_NONE, 'Show right information')
+ ->addOption('adress', null, InputOption::VALUE_NONE, 'Show adress information')
+ ->setHelp('');
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+
+ $userId = $input->getOption('user_id');
+ $userEmail = $input->getOption('user_email');
+ $databaseId = $input->getOption('database_id');
+ $collectionId = $input->getOption('collection_id');
+ $lockStatus = $input->getOption('mail_lock_status');
+ $guest = $input->getOption('guest');
+ $application = $input->getOption('application');
+ $withAdress = $input->getOption('adress');
+ $created = $input->getOption('created');
+ $updated = $input->getOption('updated');
+ $withRight = $input->getOption('right');
+
+ $query = $this->container['phraseanet.user-query'];
+
+ if($databaseId) $query->on_base_ids([$databaseId]);
+ if($collectionId) $query->on_sbas_ids([$collectionId]);
+ if($created) $this->addFilterDate($created,'created',$query);
+ if($updated) $this->addFilterDate($updated,'updated',$query);
+ if($userId) $query->addSqlFilter('Users.id = ?' ,[$userId]);
+ if($userEmail) $query->addSqlFilter('Users.email = ?' ,[$userEmail]);
+ if($lockStatus) $query->addSqlFilter('Users.mail_locked = 1');
+ if($guest) $query->include_invite(true)->addSqlFilter('Users.guest = 1');
+
+ if ($application and !$userId) {
+ $output->writeln('You must provide --user_id when using --application option');
+ return 0;
+ }
+
+ $users = $query
+ ->execute()->get_results();
+
+ $userList = [];
+ $showApplication = false;
+ foreach ($users as $key => $user) {
+ if ($userId and $application) {
+ $showApplication = true;
+ }
+ $userList[] = $this->listUser($user,$withAdress,$withRight);
+ }
+
+ $table = $this->getHelperSet()->get('table');
+ $table
+ ->setHeaders($this->headerTable($withAdress,$withRight))
+ ->setRows($userList)
+ ->render($output);
+ ;
+
+ if ($showApplication) {
+ $applicationTable = $this->getHelperSet()->get('table');
+ $applicationTable->setHeaders(array(
+ array(new TableCell('Applications', array('colspan' => 5))),
+ ['name','callback','client_secret','client_id','token'],
+ ))->setRows($this->getApplicationOfUser($users[0]))->render($output);
+ }
+
+ return 0;
+ }
+
+ /**
+ * @param $withAdress
+ * @param $withRight
+ * @return array
+ */
+ private function headerTable($withAdress,$withRight)
+ {
+ $defaultHeader = ['id', 'login', 'email','last_model','first_name','last_name','gender','created','updated','status','locale'];
+ $adressHeader = [ 'address', 'zip_code', 'city', 'country', 'phone', 'fax', 'job','position', 'company', 'geoname_id'];
+ $rightHeader = [ 'admin', 'guest', 'mail_notification', 'ldap_created', 'mail_locked'];
+
+ return $this->createInformation($withAdress,$withRight,$defaultHeader,['adress' => $adressHeader,'right' =>$rightHeader]);
+ }
+
+ /**
+ * @param User $user
+ * @param $withAdress
+ * @param $withRight
+ * @return array
+ */
+ private function listUser(User $user,$withAdress,$withRight)
+ {
+ switch ($user->getGender()) {
+ case User::GENDER_MRS:
+ $gender = 'Mrs';
+ break;
+ case User::GENDER_MISS:
+ $gender = 'Miss';
+ break;
+ case User::GENDER_MR:
+ default:
+ $gender = 'Mr';
+ }
+
+ $defaultInfo = [
+ $user->getId(),
+ $user->getLogin() ?: '-',
+ $user->getEmail() ?: '-',
+ $user->getLastAppliedTemplate() ? $user->getLastAppliedTemplate()->getLogin() : '-',
+ $user->getFirstName() ?: '-',
+ $user->getLastName() ?: '-',
+ $gender,
+ NullableDateTime::format($user->getCreated(),'Y-m-d H:i:s'),
+ NullableDateTime::format($user->getUpdated(),'Y-m-d H:i:s'),
+ 'status',
+ $user->getLocale() ?: '-',
+ ];
+
+ return $this->createInformation($withAdress,$withRight,$defaultInfo,['adress' => $this->userAdress($user),'right' => $this->userRight($user)]);
+ }
+
+ /**
+ * @param User $user
+ * @return array
+ */
+ private function userAdress(User $user)
+ {
+ return [
+ $user->getAddress() ?: '-',
+ $user->getZipCode() ?: '-',
+ $user->getCity() ?: '-',
+ $user->getCountry() ?: '-',
+ $user->getPhone() ?: '-',
+ $user->getFax() ?: '-',
+ $user->getJob() ?: '-',
+ $user->getActivity() ?: '-',
+ $user->getCompany() ?: '-',
+ $user->getGeonameId() ?: '-',
+ ];
+ }
+
+ /**
+ * @param User $user
+ * @return array
+ */
+ private function userRight(User $user)
+ {
+ return [
+ $user->isAdmin() ?: false,
+ $user->isGuest() ?: false,
+ $user->hasMailNotificationsActivated() ?: false,
+ $user->hasLdapCreated() ?: false,
+ $user->isMailLocked() ?: false,
+ ];
+ }
+
+ /**
+ * @param User $user
+ * @return array
+ */
+ private function getApplicationOfUser(User $user)
+ {
+ $apiRepository = $this->container['repo.api-applications'];
+ $applications = $apiRepository->findByUser($user);
+
+ if (empty($applications)) {
+ return [];
+ }
+
+ $accountRepository = $this->container['repo.api-accounts'];
+ $apiOauthRepository = $this->container['repo.api-oauth-tokens'];
+ $usersApplication = [];
+ foreach ($applications as $application) {
+ $account = $accountRepository->findByUserAndApplication($user, $application);
+ $token = $account ? $apiOauthRepository->findDeveloperToken($account) : null;
+ $usersApplication[] = [
+ $application->getName(),
+ $application->getRedirectUri(),
+ $application->getClientSecret(),
+ $application->getClientId(),
+ ($token) ? $token->getOauthToken() : '-'
+ ];
+ }
+
+ return $usersApplication;
+ }
+
+ /**
+ * @param $withAdress
+ * @param $withRight
+ * @param $default
+ * @param $infoToMerge
+ * @return array
+ */
+ private function createInformation($withAdress,$withRight,$default,$infoToMerge)
+ {
+ if ($withAdress && $withRight) {
+ $information = array_merge($default, $infoToMerge['adress'],$infoToMerge['right']);
+ } elseif ($withAdress && !$withRight) {
+ $information = array_merge($default, $infoToMerge['adress']);
+ } elseif(!$withAdress && $withRight) {
+ $information = array_merge($default, $infoToMerge['right']);
+ } else {
+ $information = $default;
+ }
+
+ return $information;
+ }
+
+ /**
+ * @param $date
+ * @param $type
+ * @param $query
+ */
+ private function addFilterDate($date,$type,$query){
+
+ list($operator,$dateAt) = explode(',', $date);
+
+ if (!in_array($operator,['=','>=','<=','>','<'])) {
+ throw new \InvalidArgumentException(" '=' or '<=' or '>=' or '>' or '<'");
+ }
+
+ $query->addSqlFilter($type.$operator.' ?' ,[$dateAt]);
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/Command/User/UserSetPasswordCommand.php b/lib/Alchemy/Phrasea/Command/User/UserSetPasswordCommand.php
new file mode 100644
index 0000000000..963910db02
--- /dev/null
+++ b/lib/Alchemy/Phrasea/Command/User/UserSetPasswordCommand.php
@@ -0,0 +1,79 @@
+setDescription('Set user password in Phraseanet')
+ ->addOption('user_id', null, InputOption::VALUE_REQUIRED, 'The id of user.')
+ ->addOption('generate', null, InputOption::VALUE_NONE, 'Generate the password')
+ ->addOption('password', null, InputOption::VALUE_OPTIONAL, 'The password')
+ ->setHelp('');
+
+ return $this;
+ }
+
+ protected function doExecute(InputInterface $input, OutputInterface $output)
+ {
+
+ $dialog = $this->getHelperSet()->get('dialog');
+ $userRepository = $this->container['repo.users'];
+ $userManipulator = $this->container['manipulator.user'];
+ $user = $userRepository->find($input->getOption('user_id'));
+ $password = $input->getOption('password');
+ $generate = $input->getOption('generate');
+
+ if ($user === null) {
+ $output->writeln('Not found User.');
+ return 0;
+ }
+
+ if ($generate) {
+ $password = $this->container['random.medium']->generateString(64);
+ } else {
+ if (!$password) {
+ $output->writeln('--password option not specified');
+ return 0;
+ }
+ }
+
+ do {
+ $continue = mb_strtolower($dialog->ask($output, 'Do you want really set password to this user? (y/N)', 'N'));
+ } while (!in_array($continue, ['y', 'n']));
+
+ if ($continue !== 'y') {
+ $output->writeln('Aborting !');
+
+ return;
+ }
+
+ $userManipulator->setPassword($user,$password);
+ $output->writeln('New password: ' . $password . '');
+
+ return 0;
+ }
+
+}
diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php
index d17062de4c..c029f2be01 100644
--- a/lib/Alchemy/Phrasea/Core/Version.php
+++ b/lib/Alchemy/Phrasea/Core/Version.php
@@ -16,7 +16,8 @@ class Version
/**
* @var string
*/
- private $number = '4.1.0-alpha.22a';
+
+ private $number = '4.1.0-alpha.23a';
/**
* @var string
diff --git a/lib/classes/patch/410alpha23a.php b/lib/classes/patch/410alpha23a.php
new file mode 100644
index 0000000000..eae272bc8e
--- /dev/null
+++ b/lib/classes/patch/410alpha23a.php
@@ -0,0 +1,111 @@
+release;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function concern()
+ {
+ return $this->concern;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function require_all_upgrades()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDoctrineMigrations()
+ {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function apply(base $appbox, Application $app)
+ {
+ // fix embed-bundle keys
+ if ($app['conf']->has(['embed_bundle', 'video', 'available-speeds'])) {
+ $availableSpeed = $app['conf']->get(['embed_bundle', 'video', 'available-speeds']);
+ $app['conf']->remove(['embed_bundle', 'video', 'available-speeds']);
+ $app['conf']->set(['embed_bundle', 'video', 'available_speeds'], $availableSpeed);
+ }
+
+ if ($app['conf']->has(['embed_bundle', 'audio', 'available-speeds'])) {
+ $availableSpeed = $app['conf']->get(['embed_bundle', 'audio', 'available-speeds']);
+ $app['conf']->remove(['embed_bundle', 'audio', 'available-speeds']);
+ $app['conf']->set(['embed_bundle', 'audio', 'available_speeds'], $availableSpeed);
+ }
+
+ if ($app['conf']->has(['embed_bundle', 'document', 'enable-pdfjs'])) {
+ $enablePdfjs = $app['conf']->get(['embed_bundle', 'document', 'enable-pdfjs']);
+ $app['conf']->remove(['embed_bundle', 'document', 'enable-pdfjs']);
+ $app['conf']->set(['embed_bundle', 'document', 'enable_pdfjs'], $enablePdfjs);
+ }
+
+ // geoloc section change replace 'name' to 'map-provider'
+ if ($app['conf']->has(['geocoding-providers', 0, 'name'])) {
+ $geocodingName = $app['conf']->get(['geocoding-providers', 0, 'name']);
+ $app['conf']->remove(['geocoding-providers', 0, 'name']);
+ $app['conf']->set(['geocoding-providers', 0, 'map-provider'], $geocodingName);
+ }
+
+ // video-editor section change, replace 'vttFieldName' to 'ChapterVttFieldName'
+ if ($app['conf']->has(['video-editor', 'vttFieldName'])) {
+ $chapterVttFieldName = $app['conf']->get(['video-editor', 'vttFieldName']);
+ $app['conf']->remove(['video-editor', 'vttFieldName']);
+ $app['conf']->set(['video-editor', 'ChapterVttFieldName'], $chapterVttFieldName);
+ }
+
+ // remove registry classic section if exist
+ if ($app['conf']->has(['registry', 'classic'])) {
+ $app['conf']->remove(['registry', 'classic']);
+ }
+
+ // remove bridge section if exist
+ if ($app['conf']->has(['main', 'bridge'])) {
+ $app['conf']->remove(['main', 'bridge']);
+ }
+
+ // insert RGPD bloc if not exist
+ if (!$app['conf']->has(['user_account', 'deleting_policies', 'email_confirmation'])) {
+ $app['conf']->set(['user_account', 'deleting_policies', 'email_confirmation'], true);
+ }
+
+ return true;
+ }
+}
diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml
index 0e13eba50c..de1eab7f0a 100644
--- a/lib/conf.d/configuration.yml
+++ b/lib/conf.d/configuration.yml
@@ -94,6 +94,7 @@ main:
download: null
lazaret: null
caption: null
+ tmp_files: null
trusted-proxies: []
debugger:
@@ -241,7 +242,7 @@ embed_bundle:
geocoding-providers:
-
map-provider: 'mapboxWebGL'
- enabled: true
+ enabled: false
public-key: ''
map-layers:
-
@@ -284,7 +285,7 @@ geocoding-providers:
provincefields: Province
countryfields: 'Country, Pays'
video-editor:
- vttFieldName: VideoTextTrackChapters
+ ChapterVttFieldName: VideoTextTrackChapters
seekBackwardStep: 500 # in ms
seekForwardStep: 500 # in ms
playbackRates: