From 5de988299df22c65abc34081f3fedc1a8409b43f Mon Sep 17 00:00:00 2001 From: Sandeep Date: Fri, 16 Mar 2018 15:01:20 +0400 Subject: [PATCH] PHRAS-1911-add route return list documentary fields --- .../Phrasea/Controller/Api/V1Controller.php | 145 ++++++++++++++++++ .../Phrasea/ControllerProvider/Api/V1.php | 3 + .../Elastic/Structure/MetadataHelper.php | 35 +---- lib/classes/media/subdef.php | 76 +++++---- .../Phrasea/Controller/Api/ApiTestCase.php | 30 ++++ 5 files changed, 231 insertions(+), 58 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php index 3859676bbc..48a90fa927 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1Controller.php @@ -79,6 +79,7 @@ use Alchemy\Phrasea\Search\TechnicalDataView; use Alchemy\Phrasea\Search\V1SearchCompositeResultTransformer; use Alchemy\Phrasea\Search\V1SearchRecordsResultTransformer; use Alchemy\Phrasea\Search\V1SearchResultTransformer; +use Alchemy\Phrasea\SearchEngine\Elastic\ElasticsearchOptions; use Alchemy\Phrasea\SearchEngine\SearchEngineInterface; use Alchemy\Phrasea\SearchEngine\SearchEngineLogger; use Alchemy\Phrasea\SearchEngine\SearchEngineOptions; @@ -89,6 +90,7 @@ use Alchemy\Phrasea\Utilities\NullableDateTime; use Doctrine\ORM\EntityManager; use JMS\TranslationBundle\Annotation\Ignore; use League\Fractal\Resource\Item; +use media_subdef; use Symfony\Component\Form\Form; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\Request; @@ -2678,6 +2680,137 @@ class V1Controller extends Controller return Result::create($request, $ret)->createResponse(); } + /** + * Returns all documentary fields available for user + * @param Request $request + * @return Response + */ + public function getCurrentUserStructureAction(Request $request) + { + $ret = [ + "meta_fields" => $this->listUserAuthorizedMetadataFields($this->getAuthenticatedUser()), + "aggregable_fields" => $this->listUserAggregableFields(), + "technical_fields" => media_subdef::getTechnicalFieldsList(), + ]; + + return Result::create($request, $ret)->createResponse(); + } + + /** + * Returns all sub-definitions available for the user + * @param Request $request + * @return Response + */ + public function getCurrentUserSubdefsAction(Request $request) + { + $ret = [ + "subdefs" => $this->listUserAuthorizedSubdefs($this->getAuthenticatedUser()), + ]; + + return Result::create($request, $ret)->createResponse(); + } + + /** + * Returns list of Metadata Fields from the databoxes on which the user has rights + * @param User $user + * @return array + */ + private function listUserAuthorizedMetadataFields(User $user) + { + $acl = $this->getAclForUser($user); + $ret = []; + + foreach ($acl->get_granted_sbas() as $databox) { + $databoxId = $databox->get_sbas_id(); + foreach ($databox->get_meta_structure() as $databox_field) { + $data = [ + 'name' => $databox_field->get_name(), + 'id' => $databox_field->get_id(), + 'databox_id' => $databoxId, + 'multivalue' => $databox_field->is_multi(), + 'indexable' => $databox_field->is_indexable(), + 'readonly' => $databox_field->is_readonly(), + 'business' => $databox_field->isBusiness(), + 'source' => $databox_field->get_tag()->getTagname(), + 'labels' => [ + 'fr' => $databox_field->get_label('fr'), + 'en' => $databox_field->get_label('en'), + 'de' => $databox_field->get_label('de'), + 'nl' => $databox_field->get_label('nl'), + ], + ]; + $ret[] = $data; + } + + if ($acl->can_see_business_fields($databox) === false) { + $ret = array_values($this->removeBusinessFields($ret)); + } + } + + return $ret; + } + + /** + * Build the aggregable fields array + * @return array + */ + private function listUserAggregableFields() + { + $ret = []; + $aggregFields = ElasticsearchOptions::getAggregableTechnicalFields(); + foreach ($aggregFields as $key => $field) { + $data['name'] = $key; + foreach ($field as $k => $i) { + $data[$k] = $i; + } + $ret[] = $data; + } + + return $ret; + } + + /** + * Returns list of sub-definitions from the databoxes on which the user has rights + * @param User $user + * @return array + */ + private function listUserAuthorizedSubdefs(User $user) + { + $acl = $this->getAclForUser($user); + $ret = []; + + foreach ($acl->get_granted_sbas() as $databox) { + $databoxId = $databox->get_sbas_id(); + $subdefs = $databox->get_subdef_structure(); + foreach ($subdefs as $subGroup) { + foreach ($subGroup->getIterator() as $sub) { + $opt = []; + $data = [ + 'name' => $sub->get_name(), + 'class' => $sub->get_class(), + 'preset' => $sub->get_preset(), + 'downloadable' => $sub->isDownloadable(), + 'devices' => $sub->getDevices(), + 'labels' => [ + 'fr' => $sub->get_label('fr'), + 'en' => $sub->get_label('en'), + 'de' => $sub->get_label('de'), + 'nl' => $sub->get_label('nl'), + ], + ]; + $options = $sub->getOptions(); + foreach ($options as $option) { + $opt[$option->getName()] = $option->getValue(); + } + $data['options'] = $opt; + $ret[$databoxId][$subGroup->getName()][$sub->get_name()] = $data; + } + } + } + + return $ret; + } + public function deleteCurrentUserAction(Request $request) { try { @@ -3127,4 +3260,16 @@ class V1Controller extends Controller $recordView->setCaption($captionView); } } + + /** + * Remove business metadata fields + * @param array $fields + * @return array + */ + private function removeBusinessFields(array $fields) + { + return array_filter($fields, function ($field) { + return $field['business'] !== true; + }); + } } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php index fdf999557f..f3ca635347 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Api/V1.php @@ -264,6 +264,9 @@ class V1 extends Api implements ControllerProviderInterface, ServiceProviderInte $controllers->get('/me/', 'controller.api.v1:getCurrentUserAction'); $controllers->delete('/me/', 'controller.api.v1:deleteCurrentUserAction'); + $controllers->get('/me/structure/', 'controller.api.v1:getCurrentUserStructureAction'); + $controllers->get('/me/subdefs/', 'controller.api.v1:getCurrentUserSubdefsAction'); + $controllers->post('/me/request-collections/', 'controller.api.v1:createCollectionRequests'); $controllers->post('/me/update-account/', 'controller.api.v1:updateCurrentUserAction'); diff --git a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/MetadataHelper.php b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/MetadataHelper.php index 0e6f481c6f..a31ac3accd 100644 --- a/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/MetadataHelper.php +++ b/lib/Alchemy/Phrasea/SearchEngine/Elastic/Structure/MetadataHelper.php @@ -8,40 +8,19 @@ use media_subdef; class MetadataHelper { + private static $tag_descriptors = []; + private function __construct() {} public static function createTags() { - static $tag_descriptors = [ - [media_subdef::TC_DATA_WIDTH , 'integer', false], - [media_subdef::TC_DATA_HEIGHT , 'integer', false], - [media_subdef::TC_DATA_COLORSPACE , 'string' , false], - [media_subdef::TC_DATA_CHANNELS , 'integer', false], - [media_subdef::TC_DATA_ORIENTATION , 'integer', false], - [media_subdef::TC_DATA_COLORDEPTH , 'integer', false], - [media_subdef::TC_DATA_DURATION , 'float' , false], - [media_subdef::TC_DATA_AUDIOCODEC , 'string' , false], - [media_subdef::TC_DATA_AUDIOSAMPLERATE , 'float' , false], - [media_subdef::TC_DATA_VIDEOCODEC , 'string' , false], - [media_subdef::TC_DATA_FRAMERATE , 'float' , false], - [media_subdef::TC_DATA_MIMETYPE , 'string' , false], - [media_subdef::TC_DATA_FILESIZE , 'long' , false], - // TODO use geo point type for lat/long - [media_subdef::TC_DATA_LONGITUDE , 'float' , false], - [media_subdef::TC_DATA_LATITUDE , 'float' , false], - [media_subdef::TC_DATA_FOCALLENGTH , 'float' , false], - [media_subdef::TC_DATA_CAMERAMODEL , 'string' , true ], - [media_subdef::TC_DATA_FLASHFIRED , 'boolean', false], - [media_subdef::TC_DATA_APERTURE , 'float' , false], - [media_subdef::TC_DATA_SHUTTERSPEED , 'float' , false], - [media_subdef::TC_DATA_HYPERFOCALDISTANCE, 'float' , false], - [media_subdef::TC_DATA_ISO , 'integer', false], - [media_subdef::TC_DATA_LIGHTVALUE , 'float' , false] - ]; + self::$tag_descriptors = media_subdef::getTechnicalFieldsList(); $tags = []; - foreach ($tag_descriptors as $descriptor) { - $tags[] = new Tag($descriptor[0], $descriptor[1], $descriptor[2]); + foreach (self::$tag_descriptors as $descriptor) { + if (array_key_exists('type', $descriptor) && array_key_exists('analyzable', $descriptor) && array_key_exists('name', $descriptor)) { + $tags[] = new Tag($descriptor['name'], $descriptor['type'], $descriptor['analyzable']); + } } return $tags; diff --git a/lib/classes/media/subdef.php b/lib/classes/media/subdef.php index 339efac9d1..9e75d500ae 100644 --- a/lib/classes/media/subdef.php +++ b/lib/classes/media/subdef.php @@ -587,38 +587,16 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $datas = []; - $methods = [ - self::TC_DATA_WIDTH => 'getWidth', - self::TC_DATA_HEIGHT => 'getHeight', - self::TC_DATA_FOCALLENGTH => 'getFocalLength', - self::TC_DATA_CHANNELS => 'getChannels', - self::TC_DATA_COLORDEPTH => 'getColorDepth', - self::TC_DATA_CAMERAMODEL => 'getCameraModel', - self::TC_DATA_FLASHFIRED => 'getFlashFired', - self::TC_DATA_APERTURE => 'getAperture', - self::TC_DATA_SHUTTERSPEED => 'getShutterSpeed', - self::TC_DATA_HYPERFOCALDISTANCE => 'getHyperfocalDistance', - self::TC_DATA_ISO => 'getISO', - self::TC_DATA_LIGHTVALUE => 'getLightValue', - self::TC_DATA_COLORSPACE => 'getColorSpace', - self::TC_DATA_DURATION => 'getDuration', - self::TC_DATA_FRAMERATE => 'getFrameRate', - self::TC_DATA_AUDIOSAMPLERATE => 'getAudioSampleRate', - self::TC_DATA_VIDEOCODEC => 'getVideoCodec', - self::TC_DATA_AUDIOCODEC => 'getAudioCodec', - self::TC_DATA_ORIENTATION => 'getOrientation', - self::TC_DATA_LONGITUDE => 'getLongitude', - self::TC_DATA_LONGITUDE_REF => 'getLongitudeRef', - self::TC_DATA_LATITUDE => 'getLatitude', - self::TC_DATA_LATITUDE_REF => 'getLatitudeRef', - ]; + $techDatas = self::getTechnicalFieldsList(); - foreach ($methods as $tc_name => $method) { - if (method_exists($media, $method)) { - $result = call_user_func([$media, $method]); + foreach ($techDatas as $techData) { + if (array_key_exists('name', $techData) && array_key_exists('method', $techData)) { + if (method_exists($media, $techData['method'])) { + $result = call_user_func([$media, $techData['method']]); - if (null !== $result) { - $datas[$tc_name] = $result; + if (null !== $result) { + $datas[$techData['name']] = $result; + } } } } @@ -825,4 +803,42 @@ class media_subdef extends media_abstract implements cache_cacheableInterface return $this; } + + /** + * Return list of technical data and there attributes + * + * @return array + */ + public static function getTechnicalFieldsList(){ + + $datas = [ + ['name' => self::TC_DATA_WIDTH, 'method' => 'getWidth', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_HEIGHT, 'method' => 'getHeight', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_FOCALLENGTH, 'method' => 'getFocalLength', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_CHANNELS, 'method' => 'getChannels', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_COLORDEPTH, 'method' => 'getColorDepth', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_CAMERAMODEL, 'method' => 'getCameraModel', 'type' => 'string', 'analyzable' => false], + ['name' => self::TC_DATA_FLASHFIRED, 'method' => 'getFlashFired', 'type' => 'boolean', 'analyzable' => false], + ['name' => self::TC_DATA_APERTURE, 'method' => 'getAperture', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_SHUTTERSPEED, 'method' => 'getShutterSpeed', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_HYPERFOCALDISTANCE, 'method' => 'getHyperfocalDistance', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_ISO, 'method' => 'getISO', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_LIGHTVALUE, 'method' => 'getLightValue', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_COLORSPACE, 'method' => 'getColorSpace', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_DURATION, 'method' => 'getDuration', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_FRAMERATE, 'method' => 'getFrameRate', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_AUDIOSAMPLERATE, 'method' => 'getAudioSampleRate', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_VIDEOCODEC, 'method' => 'getVideoCodec', 'type' => 'string', 'analyzable' => false], + ['name' => self::TC_DATA_AUDIOCODEC, 'method' => 'getAudioCodec', 'type' => 'string', 'analyzable' => false], + ['name' => self::TC_DATA_ORIENTATION, 'method' => 'getOrientation', 'type' => 'integer', 'analyzable' => false], + ['name' => self::TC_DATA_LONGITUDE, 'method' => 'getLongitude', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_LONGITUDE_REF, 'method' => 'getLongitudeRef'], + ['name' => self::TC_DATA_LATITUDE, 'method' => 'getLatitude', 'type' => 'float', 'analyzable' => false], + ['name' => self::TC_DATA_LATITUDE_REF, 'method' => 'getLatitudeRef'], + ['name' => self::TC_DATA_MIMETYPE, 'type' => 'string', 'analyzable' => false], + ['name' => self::TC_DATA_FILESIZE, 'type' => 'long', 'analyzable' => false], + ]; + + return $datas; + } } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php index 054f779076..963a5cb1ad 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Api/ApiTestCase.php @@ -97,6 +97,36 @@ abstract class ApiTestCase extends \PhraseanetWebTestCase $this->evaluateGoodUserItem($content['response']['user'], self::$DI['user_notAdmin']); } + public function testRouteMeStructure() + { + $this->setToken($this->userAccessToken); + + $route = '/api/v1/me/structure/'; + + $this->evaluateMethodNotAllowedRoute($route, [ 'POST', 'PUT' ]); + + self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); + $content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); + + $this->assertArrayHasKey('meta_fields', $content['response']); + $this->assertArrayHasKey('aggregable_fields', $content['response']); + $this->assertArrayHasKey('technical_fields', $content['response']); + } + + public function testRouteMeSubdefs() + { + $this->setToken($this->userAccessToken); + + $route = '/api/v1/me/subdefs/'; + + $this->evaluateMethodNotAllowedRoute($route, [ 'POST', 'PUT' ]); + + self::$DI['client']->request('GET', $route, $this->getParameters(), [], ['HTTP_Accept' => $this->getAcceptMimeType()]); + $content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); + + $this->assertArrayHasKey('subdefs', $content['response']); + } + protected function evaluateGoodUserItem($data, User $user) { foreach ([