From 18f7a658ed64fd6160e4bcd7a01d3ee5a8c575c1 Mon Sep 17 00:00:00 2001 From: jygaulier Date: Mon, 22 Jun 2020 14:40:27 +0200 Subject: [PATCH 1/3] change : removed "/setmetadatas" from route, the http verb "PATCH" on "record" entity is ok fix : unknown field now throws exception (no more erase all...) add : set "base_id":x to change collection cleanup : removed unused parm to recordadapter::move_to_collection() --- .../Phrasea/Controller/Api/V1Controller.php | 2 +- ...sController.php => V3RecordController.php} | 29 +++++++++++++------ .../Prod/MoveCollectionController.php | 4 +-- .../Controller/Prod/RecordController.php | 2 +- .../Phrasea/ControllerProvider/Api/V3.php | 8 ++--- .../TaskManager/Job/RecordMoverJob.php | 3 +- lib/classes/record/adapter.php | 5 ++-- .../Phrasea/Application/OverviewTest.php | 4 +-- 8 files changed, 34 insertions(+), 23 deletions(-) rename lib/Alchemy/Phrasea/Controller/Api/V3/{V3MetadatasController.php => V3RecordController.php} (94%) diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php index cd445c9e8f..d93474e72a 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php @@ -2093,7 +2093,7 @@ class V1Controller extends Controller try { $collection = \collection::getByBaseId($this->app, $request->get('base_id')); - $record->move_to_collection($collection, $this->getApplicationBox()); + $record->move_to_collection($collection); return Result::create($request, ["record" => $this->listRecord($request, $record)])->createResponse(); } catch (\Exception $e) { diff --git a/lib/Alchemy/Phrasea/Controller/Api/V3/V3MetadatasController.php b/lib/Alchemy/Phrasea/Controller/Api/V3/V3RecordController.php similarity index 94% rename from lib/Alchemy/Phrasea/Controller/Api/V3/V3MetadatasController.php rename to lib/Alchemy/Phrasea/Controller/Api/V3/V3RecordController.php index a187ad4d47..d6b8ecd163 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V3/V3MetadatasController.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V3/V3RecordController.php @@ -17,7 +17,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -class V3MetadatasController extends Controller +class V3RecordController extends Controller { use JsonBodyAware; use DispatcherAware; @@ -32,7 +32,7 @@ class V3MetadatasController extends Controller * * @return Response */ - public function setmetadatasAction(Request $request, $databox_id, $record_id) + public function indexAction_patch(Request $request, $databox_id, $record_id) { $struct = $this->findDataboxById($databox_id)->get_meta_structure(); $record = $this->findDataboxById($databox_id)->get_record($record_id); @@ -59,7 +59,10 @@ class V3MetadatasController extends Controller } // do sb ops if (is_array($b->status)) { - $debug['sb_ops'] = $this->do_status($struct, $record, $b->status); + $debug['sb_ops'] = $this->do_status($record, $b->status); + } + if(!is_null($b->base_id)) { + $debug['coll_ops'] = $this->do_collection($record, $b->base_id); } } catch (Exception $e) { @@ -77,13 +80,21 @@ class V3MetadatasController extends Controller return Result::create($request, $ret)->createResponse(); } + /** + * @param record_adapter $record + * @param $base_id + */ + private function do_collection(record_adapter $record, $base_id) + { + $record->move_to_collection($this->getApplicationBox()->get_collection($base_id)); + } + ////////////////////////////////// /// TODO : keep multi-values uniques ! /// it should be done in record_adapter ////////////////////////////////// - /** * @param databox_field[] $struct * @param record_adapter $record @@ -94,12 +105,10 @@ class V3MetadatasController extends Controller private function do_metadatas($struct, record_adapter $record, $metadatas) { $structByKey = []; - $nameToStrucId = []; $allStructFields = []; foreach ($struct as $f) { - $nameToStrucId[$f->get_name()] = $f->get_id(); $allStructFields[$f->get_id()] = $f; - $structByKey[$f->get_id()] = &$allStructFields[$f->get_id()];; + $structByKey[$f->get_id()] = &$allStructFields[$f->get_id()]; $structByKey[$f->get_name()] = &$allStructFields[$f->get_id()]; } @@ -123,6 +132,9 @@ class V3MetadatasController extends Controller $fields_list[] = $structByKey[$k]->get_name(); $struct_fields[$structByKey[$k]->get_id()] = $structByKey[$k]; } + else { + throw new Exception(sprintf("unknown field (%s).", $k)); + } } } else { @@ -187,13 +199,12 @@ class V3MetadatasController extends Controller } /** - * @param $struct * @param $record * @param $statuses * @return array * @throws Exception */ - private function do_status($struct, $record, $statuses) + private function do_status(record_adapter $record, $statuses) { $datas = strrev($record->getStatus()); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php index 50e2219e0d..274ab2ac17 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/MoveCollectionController.php @@ -115,13 +115,13 @@ class MoveCollectionController extends Controller foreach ($records as $record) { $oldCollectionId = $record->getCollection()->get_coll_id(); - $record->move_to_collection($collection, $this->getApplicationBox()); + $record->move_to_collection($collection); if ($request->request->get("chg_coll_son") == "1") { /** @var \record_adapter $child */ foreach ($record->getChildren() as $child) { if ($this->getAclForUser()->has_right_on_base($child->getBaseId(), \ACL::CANDELETERECORD)) { - $child->move_to_collection($collection, $this->getApplicationBox()); + $child->move_to_collection($collection); } } } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php index da612c13aa..1c1e6ee250 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/RecordController.php @@ -242,7 +242,7 @@ class RecordController extends Controller $this->getEventDispatcher()->dispatch(RecordEvents::DELETE, new DeleteEvent($record)); } else { // move to trash collection - $record->move_to_collection($trashCollectionsBySbasId[$sbasId], $this->getApplicationBox()); + $record->move_to_collection($trashCollectionsBySbasId[$sbasId]); // disable permalinks foreach($record->get_subdefs() as $subdef) { if( ($pl = $subdef->get_permalink()) ) { diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php index 64373dedff..22244bb1a4 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V3.php @@ -3,7 +3,7 @@ namespace Alchemy\Phrasea\ControllerProvider\Api; use Alchemy\Phrasea\Application as PhraseaApplication; -use Alchemy\Phrasea\Controller\Api\V3\V3MetadatasController; +use Alchemy\Phrasea\Controller\Api\V3\V3RecordController; use Alchemy\Phrasea\Controller\Api\V3\V3ResultHelpers; use Alchemy\Phrasea\Controller\Api\V3\V3SearchController; use Alchemy\Phrasea\Controller\Api\V3\V3StoriesController; @@ -28,7 +28,7 @@ class V3 extends Api implements ControllerProviderInterface, ServiceProviderInte )); }); $app['controller.api.v3.metadatas'] = $app->share(function (PhraseaApplication $app) { - return (new V3MetadatasController($app)) + return (new V3RecordController($app)) ->setJsonBodyHelper($app['json.body_helper']) ->setDispatcher($app['dispatcher']) ; @@ -70,9 +70,9 @@ class V3 extends Api implements ControllerProviderInterface, ServiceProviderInte $controllers->match('/search/', 'controller.api.v3.search:searchAction'); /** - * @uses V3MetadatasController::setmetadatasAction() + * @uses V3RecordController::indexAction_patch() */ - $controllers->patch('/records/{databox_id}/{record_id}/setmetadatas/', 'controller.api.v3.metadatas:setmetadatasAction') + $controllers->patch('/records/{databox_id}/{record_id}/', 'controller.api.v3.metadatas:indexAction_patch') ->before('controller.api.v1:ensureCanAccessToRecord') ->before('controller.api.v1:ensureCanModifyRecord') ->assert('databox_id', '\d+') diff --git a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php index 9e2878cdd0..d007dc5f29 100644 --- a/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php +++ b/lib/Alchemy/Phrasea/TaskManager/Job/RecordMoverJob.php @@ -74,7 +74,6 @@ class RecordMoverJob extends AbstractJob private function processData(Application $app, $row, $logsql) { - /** @var databox $databox */ $databox = $app->findDataboxById($row['sbas_id']); $rec = $databox->get_record($row['record_id']); @@ -83,7 +82,7 @@ class RecordMoverJob extends AbstractJob // change collection ? if (array_key_exists('coll', $row)) { $coll = \collection::getByCollectionId($app, $databox, $row['coll']); - $rec->move_to_collection($coll, $app['phraseanet.appbox']); + $rec->move_to_collection($coll); if ($logsql) { $this->log('debug', sprintf("on sbas %s move rid %s to coll %s \n", $row['sbas_id'], $row['record_id'], $coll->get_coll_id())); } diff --git a/lib/classes/record/adapter.php b/lib/classes/record/adapter.php index e572b13a34..a1d79111a3 100644 --- a/lib/classes/record/adapter.php +++ b/lib/classes/record/adapter.php @@ -522,10 +522,11 @@ class record_adapter implements RecordInterface, cache_cacheableInterface /** * * @param collection $collection - * @param appbox $appbox + * @param appbox $appbox WTF this parm is useless * @return record_adapter + * */ - public function move_to_collection(collection $collection, appbox $appbox) + public function move_to_collection(collection $collection, appbox $appbox = null) { if ($this->getCollection()->get_base_id() === $collection->get_base_id()) { return $this; diff --git a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php index d98c9d7c61..78b643cce6 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php +++ b/tests/Alchemy/Tests/Phrasea/Application/OverviewTest.php @@ -118,7 +118,7 @@ class OverviewTest extends \PhraseanetAuthenticatedWebTestCase public function testDatafilesRouteNotAuthenticatedIsOkInPublicFeed() { self::$DI['app']['phraseanet.SE'] = $this->createSearchEngineMock(); - self::$DI['record_5']->move_to_collection(self::$DI['collection_no_access'], self::$DI['app']['phraseanet.appbox']); + self::$DI['record_5']->move_to_collection(self::$DI['collection_no_access']); $path = self::$DI['app']['url_generator']->generate('datafile', [ 'sbas_id' => self::$DI['record_5']->get_sbas_id(), 'record_id' => self::$DI['record_5']->get_record_id(), @@ -127,7 +127,7 @@ class OverviewTest extends \PhraseanetAuthenticatedWebTestCase self::$DI['client']->request('GET', $path); $this->assertTrue(self::$DI['client']->getResponse()->isOk()); - self::$DI['record_5']->move_to_collection(self::$DI['collection'], self::$DI['app']['phraseanet.appbox']); + self::$DI['record_5']->move_to_collection(self::$DI['collection']); } public function testDatafilesRouteNotAuthenticatedUnknownSubdef() From df22b24b1933dd5e07f507cb4ee29f22b1acb57f Mon Sep 17 00:00:00 2001 From: aynsix Date: Tue, 23 Jun 2020 11:06:51 +0300 Subject: [PATCH 2/3] add populate controller in thesaurus --- .../Thesaurus/ThesaurusController.php | 31 +++++++++++++++++++ .../Thesaurus/Thesaurus.php | 1 + 2 files changed, 32 insertions(+) diff --git a/lib/Alchemy/Phrasea/Controller/Thesaurus/ThesaurusController.php b/lib/Alchemy/Phrasea/Controller/Thesaurus/ThesaurusController.php index 1bd06d83b2..b31ad7e8c2 100644 --- a/lib/Alchemy/Phrasea/Controller/Thesaurus/ThesaurusController.php +++ b/lib/Alchemy/Phrasea/Controller/Thesaurus/ThesaurusController.php @@ -13,6 +13,9 @@ use Alchemy\Phrasea\Application\Helper\DispatcherAware; use Alchemy\Phrasea\Controller\Controller; use Alchemy\Phrasea\Core\Event\Thesaurus as ThesaurusEvent; use Alchemy\Phrasea\Core\Event\Thesaurus\ThesaurusEvents; +use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions; +use Alchemy\Phrasea\WorkerManager\Event\PopulateIndexEvent; +use Alchemy\Phrasea\WorkerManager\Event\WorkerEvents; use Doctrine\DBAL\Driver\Connection; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -1222,6 +1225,26 @@ class ThesaurusController extends Controller ]); } + /** + * Order to populate databox + * + * @param Request $request + * @return \Symfony\Component\HttpFoundation\JsonResponse + */ + public function populate(Request $request) + { + $options = $this->getElasticsearchOptions(); + + $data['host'] = $options->getHost(); + $data['port'] = $options->getPort(); + $data['indexName'] = $options->getIndexName(); + $data['databoxIds'] = [$request->get('databox_id')]; + + $this->getDispatcher()->dispatch(WorkerEvents::POPULATE_INDEX, new PopulateIndexEvent($data)); + + return $this->app->json($data); + } + /** * @param Request $request * @return Response @@ -3031,4 +3054,12 @@ class ThesaurusController extends Controller { return $this->app['locales.available']; } + + /** + * @return ElasticsearchOptions + */ + private function getElasticsearchOptions() + { + return $this->app['elasticsearch.options']; + } } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Thesaurus/Thesaurus.php b/lib/Alchemy/Phrasea/ControllerProvider/Thesaurus/Thesaurus.php index 039cb4b96f..d5119eede9 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Thesaurus/Thesaurus.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Thesaurus/Thesaurus.php @@ -60,6 +60,7 @@ class Thesaurus implements ControllerProviderInterface, ServiceProviderInterface $controllers->match('newterm.php', 'controller.thesaurus:newTerm'); $controllers->match('properties.php', 'controller.thesaurus:properties'); $controllers->match('thesaurus.php', 'controller.thesaurus:thesaurus')->bind('thesaurus_thesaurus'); + $controllers->match('populate', 'controller.thesaurus:populate')->bind('thesaurus_populate'); $controllers->match('xmlhttp/accept.x.php', 'controller.thesaurus:acceptXml'); $controllers->match('xmlhttp/acceptcandidates.x.php', 'controller.thesaurus:acceptCandidatesXml'); From 2a379093415e9082ac66f8e8dfb1d4ce1b7d681e Mon Sep 17 00:00:00 2001 From: Arthur de Moulins Date: Wed, 24 Jun 2020 17:20:34 +0200 Subject: [PATCH 3/3] change plugin delimiter --- README.md | 9 ++++++--- docker/phraseanet/plugins/InstallCommand.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c311851856..8bc90f4d11 100644 --- a/README.md +++ b/README.md @@ -193,17 +193,20 @@ XDEBUG_REMOTE_HOST=host.docker.internal Plugins can be installed during build if you set the `PHRASEANET_PLUGINS` env var as follows: ```bash -PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git" +PHRASEANET_PLUGINS="https://github.com/alchemy-fr/Phraseanet-plugin-expose.git" # You can optionally precise the branch to install # If not precised, the main branch will be pulled PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git(custom-branch)" -# Plugins are separated by spaces -PHRASEANET_PLUGINS="git@github.com:foo/bar.git(branch-1) git@github.com:baz/42.git" +# Plugins are separated by semicolons +PHRASEANET_PLUGINS="git@github.com:foo/bar.git(branch-1);git@github.com:baz/42.git" ``` +> Prefer the HTTPS URL for public repositories, you will not be required to provide your SSH key. + If you install private plugins, make sure you export your SSH private key content in order to allow docker build to access the GIT repository: +Also ensure you're using the SSH URL form (i.e: `git@github.com:alchemy-fr/repo.git`). ```bash export PHRASEANET_SSH_PRIVATE_KEY=$(cat ~/.ssh/id_rsa) # or if your private key is protected by a passphrase: diff --git a/docker/phraseanet/plugins/InstallCommand.php b/docker/phraseanet/plugins/InstallCommand.php index 7b8a28c4cd..3a0c4bc7e1 100644 --- a/docker/phraseanet/plugins/InstallCommand.php +++ b/docker/phraseanet/plugins/InstallCommand.php @@ -29,7 +29,7 @@ class InstallCommand extends Command mkdir($pluginsDir); } - foreach (explode(' ', $plugins) as $key => $plugin) { + foreach (explode(';', $plugins) as $key => $plugin) { $plugin = trim($plugin); $repo = $plugin; $branch = 'master';