diff --git a/app/resto/core/RestoCollection.php b/app/resto/core/RestoCollection.php index 98adb1f4..8d76f59d 100755 --- a/app/resto/core/RestoCollection.php +++ b/app/resto/core/RestoCollection.php @@ -1114,9 +1114,14 @@ private function checkCreationMandatoryProperties($object, $modelName) } /* - * Set DefaultModel if not set + * Set DefaultModel if not set - preseance to input $modelName */ - $object['model'] = isset($modelName) ? $modelName : ($object['model'] ?? 'DefaultModel'); + if ( isset($modelName) ) { + $object['model'] = $modelName; + } + if ( !isset($object['model']) ) { + $object['model'] = $this->context->core['defaultModel']; + } if (!class_exists($object['model']) || !is_subclass_of($object['model'], 'RestoModel')) { RestoLogUtil::httpError(400, 'Model "' . $object['model'] . '" is not a valid model name'); diff --git a/app/resto/core/RestoConstants.php b/app/resto/core/RestoConstants.php index 1efbc9a0..6f9e078b 100644 --- a/app/resto/core/RestoConstants.php +++ b/app/resto/core/RestoConstants.php @@ -20,7 +20,7 @@ class RestoConstants // [IMPORTANT] Starting resto 7.x, default routes are defined in RestoRouter class // resto version - const VERSION = '9.0.4'; + const VERSION = '9.0.5'; /* ============================================================ * NEVER EVER TOUCH THESE VALUES diff --git a/app/resto/core/RestoContext.php b/app/resto/core/RestoContext.php index 60af588b..186a7d4f 100755 --- a/app/resto/core/RestoContext.php +++ b/app/resto/core/RestoContext.php @@ -53,6 +53,9 @@ class RestoContext // Display collection that have at least 'collectionMinMatch' object 'collectionMinMatch' => 0, + // Default model applied to POST collection + 'defaultModel' => 'DefaultModel', + // Use cache 'useCache' => false, diff --git a/app/resto/core/api/CollectionsAPI.php b/app/resto/core/api/CollectionsAPI.php index 415f53e4..f37042c5 100755 --- a/app/resto/core/api/CollectionsAPI.php +++ b/app/resto/core/api/CollectionsAPI.php @@ -491,7 +491,7 @@ public function deleteCollection($params) RestoLogUtil::httpError(403); } - (new CollectionsFunctions($this->context->dbDriver))->removeCollection($collection); + (new CollectionsFunctions($this->context->dbDriver))->removeCollection($collection, $this->context->core['baseUrl']); return RestoLogUtil::success('Collection ' . $collection->id . ' deleted'); } diff --git a/app/resto/core/api/STACAPI.php b/app/resto/core/api/STACAPI.php index c2904b12..9dc31c73 100644 --- a/app/resto/core/api/STACAPI.php +++ b/app/resto/core/api/STACAPI.php @@ -405,8 +405,10 @@ public function addCatalog($params, $body) * [IMPORTANT] Special case - post a collection under a catalog is in fact an update of 'links' property of this catalog */ if ( $body['type'] === 'Collection' ) { + + // Collection does not exist - created first if ( !(new CollectionsFunctions($this->context->dbDriver))->collectionExists($body['id']) ) { - return RestoLogUtil::httpError(400, 'Collection ' . $body['id'] . ' does not exist. Should be added first before pushing under a catalog'); + $this->context->keeper->getRestoCollections($this->user)->create($body, $params['model'] ?? null); } return $this->catalogsFunctions->storeCollectionUnderCatalog($parentId, $body['id'], $this->user->profile['id'], $this->context) ? RestoLogUtil::success('Collection added under catalog ' . $parentId) : RestoLogUtil::error('Cannot add collection under catalog ' . $parentId); } diff --git a/app/resto/core/dbfunctions/CatalogsFunctions.php b/app/resto/core/dbfunctions/CatalogsFunctions.php index b5c1a1de..6f41ab17 100755 --- a/app/resto/core/dbfunctions/CatalogsFunctions.php +++ b/app/resto/core/dbfunctions/CatalogsFunctions.php @@ -282,6 +282,7 @@ public function storeCollectionUnderCatalog($catalogId, $collectionId, $userid, $link = array( 'rel' => 'child', + 'type' => RestoUtil::$contentTypes['json'], 'href' => $context->core['baseUrl'] . RestoRouter::ROUTE_TO_COLLECTIONS . '/' . $collectionId ); diff --git a/app/resto/core/dbfunctions/CollectionsFunctions.php b/app/resto/core/dbfunctions/CollectionsFunctions.php index 0b85b9bb..26d310f8 100755 --- a/app/resto/core/dbfunctions/CollectionsFunctions.php +++ b/app/resto/core/dbfunctions/CollectionsFunctions.php @@ -130,10 +130,10 @@ public function collectionExists($collectionId) * Remove collection from RESTo database * * @param RestoCollection $collection - * @return array + * @param string $baseUrl * @throws Exception */ - public function removeCollection($collection) + public function removeCollection($collection, $baseUrl) { /* * Never remove a non empty collection @@ -146,8 +146,14 @@ public function removeCollection($collection) * Delete (within transaction) */ try { + $this->dbDriver->query('BEGIN'); + /* + * First remove collection referenced within catalogs + */ + $this->removeCollectionFromCatalogs($baseUrl . RestoRouter::ROUTE_TO_COLLECTIONS . '/' . $collection->id); + $this->dbDriver->pQuery('DELETE FROM ' . $this->dbDriver->targetSchema . '.collection WHERE id=$1', array( $collection->id )); @@ -630,4 +636,55 @@ private function format($rawDescription, $osDescription) return $collection; } + + /** + * + * Remove all collectionUrl entries in catalog links property + * + * @param string $collectionurl + * @return void + */ + private function removeCollectionFromCatalogs($collectionurl) + { + + $this->dbDriver->query("WITH tmp AS ( + WITH target_links as ( + SELECT id, links + FROM " . $this->dbDriver->targetSchema . ".catalog + WHERE EXISTS ( + SELECT 1 + FROM json_array_elements(links) AS link + WHERE link->>'href' = '" . $collectionurl . "' + ) + ), + expanded_links AS ( + SELECT + id, + json_array_elements(links) AS link + FROM target_links + WHERE json_typeof(links) = 'array' + ), + filtered_links AS ( + SELECT + id, + json_agg(link) AS new_links + FROM expanded_links + WHERE link->>'href' != '" . $collectionurl . "' -- Exclude the link with the specific ID + GROUP BY id + ) + SELECT " . $this->dbDriver->targetSchema . ".catalog.id, f.new_links AS modified_links + FROM " . $this->dbDriver->targetSchema . ".catalog + LEFT JOIN filtered_links f ON " . $this->dbDriver->targetSchema . ".catalog.id = f.id + ) + UPDATE " . $this->dbDriver->targetSchema . ".catalog + SET links = tmp.modified_links + FROM tmp + WHERE " . $this->dbDriver->targetSchema . ".catalog.id = tmp.id + AND EXISTS ( + SELECT 1 + FROM json_array_elements(links) AS link + WHERE link->>'href' = '" . $collectionurl . "' + );"); + + } } diff --git a/build/resto/config.php.template b/build/resto/config.php.template index 2323b9fb..ad3053ac 100755 --- a/build/resto/config.php.template +++ b/build/resto/config.php.template @@ -12,6 +12,7 @@ return array( ), 'storeQuery' => ${STORE_QUERY:-false}, 'collectionMinMatch' => ${COLLECTION_MINMATCH:-0}, + 'defaultModel' => '${DEFAULT_COLLECTION_MODEL:-DefaultModel}', 'timezone' => '${TIMEZONE:-Europe/Paris}', 'tokenDuration' => ${JWT_DURATION:-8640000}, 'userAutoValidation' => ${USER_AUTOVALIDATION:-true}, diff --git a/config.env b/config.env index 89409fdd..124117db 100644 --- a/config.env +++ b/config.env @@ -27,6 +27,9 @@ SUPPORTED_LANGUAGES=en,fr ### Data related "planet" #PLANET=earth +### Default Model applied to collection +#DEFAULT_COLLECTION_MODEL=DefaultModel + ### Permanent storage directory to store/retrieve files (e.g. user's avatar picture) ### Relative to PUBLIC_ENDPOINT if not starting with http #STORAGE_PUBLIC_ENDPOINT=/static diff --git a/examples/collections/DummyCollection.json b/examples/collections/DummyCollection.json index 24d66adb..f6be1cd3 100644 --- a/examples/collections/DummyCollection.json +++ b/examples/collections/DummyCollection.json @@ -5,7 +5,6 @@ "DummyCollectionAlias" ], "version": "1.0", - "model": "OpticalModel", "license": "PDDL-1.0", "osDescription": { "en": { diff --git a/examples/collections/DummyCollection2.json b/examples/collections/DummyCollection2.json new file mode 100644 index 00000000..adc69d05 --- /dev/null +++ b/examples/collections/DummyCollection2.json @@ -0,0 +1,27 @@ +{ + "id": "DummyCollection2", + "type": "Collection", + "version": "1.0", + "license": "PDDL-1.0", + "osDescription": { + "en": { + "ShortName": "DummyCollection2", + "LongName": "DummyCollection2", + "Description": "This is a Dummy Collection2", + "Tags": "dummy", + "Developer": "Jérôme Gasperi", + "Contact": "jrom@snapplanet.io", + "Query": "dummy", + "Attribution": "USGS/NASA Landsat" + } + }, + "providers": [ + { + "name": "jeobrowser", + "roles": [ + "processor" + ], + "url": "https://github.com/jjrom/resto" + } + ] +} \ No newline at end of file