@@ -98,6 +98,7 @@ final public function __construct(Filesystem $filesystem, Response $response, Gr
9898 $ this ->filesystem = $ filesystem ;
9999 $ this ->graph = $ graph ?? new Graph ();
100100 $ this ->response = $ response ;
101+ // @TODO: Mention \EasyRdf_Namespace::set('lm', 'https://purl.org/pdsinterop/link-metadata#');
101102 }
102103
103104 final public function respondToRequest (Request $ request ): Response
@@ -280,6 +281,7 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
280281
281282 try {
282283 // Assuming this is in our native format, turtle
284+ // @CHECKME: Does the Graph Parse here also need an URI?
283285 $ graph ->parse ($ data , "turtle " );
284286 // FIXME: Adding this base will allow us to parse <> entries; , $this->baseUrl . $this->basePath . $path), but that breaks the build.
285287 // FIXME: Use enums from namespace Pdsinterop\Rdf\Enum\Format instead of 'turtle'?
@@ -294,12 +296,14 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
294296 switch ($ command ) {
295297 case "INSERT " :
296298 // insert $triple(s) into $graph
299+ // @CHECKME: Does the Graph Parse here also need an URI?
297300 $ graph ->parse ($ triples , "turtle " ); // FIXME: The triples here are in sparql format, not in turtle;
298301
299302 break ;
300303 case "DELETE " :
301304 // delete $triples from $graph
302305 $ deleteGraph = $ this ->getGraph ();
306+ // @CHECKME: Does the Graph Parse here also need an URI?
303307 $ deleteGraph ->parse ($ triples , "turtle " ); // FIXME: The triples here are in sparql format, not in turtle;
304308 $ resources = $ deleteGraph ->resources ();
305309 foreach ($ resources as $ resource ) {
@@ -339,6 +343,7 @@ private function handleSparqlUpdate(Response $response, string $path, $contents)
339343 $ response = $ response ->withStatus ($ success ? 201 : 500 );
340344
341345 if ($ success ) {
346+ $ this ->removeLinkFromMetaFileFor ($ path );
342347 $ this ->sendWebsocketUpdate ($ path );
343348 }
344349 } catch (EasyRdf_Exception $ exception ) {
@@ -385,6 +390,7 @@ private function handleCreateRequest(Response $response, string $path, $contents
385390 }
386391
387392 if ($ success ) {
393+ $ this ->removeLinkFromMetaFileFor ($ path );
388394 $ response = $ response ->withHeader ("Location " , $ this ->baseUrl . $ path );
389395 $ response = $ response ->withStatus (201 );
390396 $ this ->sendWebsocketUpdate ($ path );
@@ -420,6 +426,7 @@ private function handleCreateDirectoryRequest(Response $response, string $path):
420426 $ success = $ filesystem ->createDir ($ path );
421427 $ response = $ response ->withStatus ($ success ? 201 : 500 );
422428 if ($ success ) {
429+ $ this ->removeLinkFromMetaFileFor ($ path );
423430 $ this ->sendWebsocketUpdate ($ path );
424431 }
425432 }
@@ -507,6 +514,7 @@ private function handleUpdateRequest(Response $response, string $path, string $c
507514 $ success = $ filesystem ->update ($ path , $ contents );
508515 $ response = $ response ->withStatus ($ success ? 201 : 500 );
509516 if ($ success ) {
517+ $ this ->removeLinkFromMetaFileFor ($ path );
510518 $ this ->sendWebsocketUpdate ($ path );
511519 }
512520 }
@@ -559,10 +567,8 @@ private function handleReadRequest(Response $response, string $path, $contents,
559567 /*/ The file does exist and no link-metadata is present /*/
560568 $ response = $ this ->addLinkRelationHeaders ($ response , $ path , $ mime );
561569
562- if (preg_match ('/.ttl$/ ' , $ path )) {
563- $ mimetype = "text/turtle " ; // FIXME: teach flysystem that .ttl means text/turtle
564- } elseif (preg_match ('/.acl$/ ' , $ path )) {
565- $ mimetype = "text/turtle " ; // FIXME: teach flysystem that .acl files also want text/turtle
570+ if (preg_match ('/\.(acl|meta|ttl)$/ ' , $ path )) {
571+ $ mimetype = "text/turtle " ; // FIXME: teach flysystem that .acl/.meta/.ttl means text/turtle
566572 } else {
567573 $ mimetype = $ filesystem ->asMime ($ mime )->getMimetype ($ path );
568574 }
@@ -572,6 +578,11 @@ private function handleReadRequest(Response $response, string $path, $contents,
572578 if ($ contents !== false ) {
573579 $ response ->getBody ()->write ($ contents );
574580 $ response = $ response ->withHeader ("Content-type " , $ mimetype )->withStatus (200 );
581+ } else {
582+ /*/ The file does exist in another format and no link-metadata is present /*/
583+ $ message = vsprintf (self ::ERROR_PATH_DOES_NOT_EXIST , [$ path ]);
584+ $ response ->getBody ()->write ($ message );
585+ $ response = $ response ->withStatus (404 );
575586 }
576587 } else {
577588 /*/ The file does exist in another format and no link-metadata is present /*/
@@ -694,6 +705,7 @@ private function addLinkRelationHeaders(Response $response, string $path, $mime=
694705 {
695706 // @FIXME: If a `.meta` file is requested, it must have header `Link: </path/to/resource>; rel="describes"`
696707
708+ //@CHECKME: Should the ACL link header be added here or in/by the Auth server?
697709 if ($ this ->hasAcl ($ path , $ mime )) {
698710 $ value = sprintf ('<%s>; rel="acl" ' , $ this ->getAclPath ($ path , $ mime ));
699711 $ response = $ response ->withAddedHeader ('Link ' , $ value );
@@ -815,23 +827,34 @@ private function parseLinkedMetadata(string $path)
815827 $ describedByPath = $ this ->filesystem ->getMetadata ($ path )['describedby ' ] ?? '' ;
816828 $ describedByContents = $ this ->filesystem ->read ($ describedByPath );
817829 } catch (FileNotFoundException $ e ) {
818- // @CHECKME: If, for whatever reason, the file is not present after all... Do we care here?
830+ // If, for whatever reason, the file is not present after all, the resource should still be returned (or a 404)
831+ // @CHECKME: Should the upstream add a message to the header or something?
819832 return $ linkMeta ;
820833 }
821834
822835 $ graph = $ this ->getGraph ();
823836
824837 try {
825- $ graph ->parse ($ describedByContents );
838+ $ graph ->parse ($ describedByContents, null , ' / ' . $ describedByPath );
826839 } catch (EasyRdf_Exception $ exception ) {
827- throw Exception::create (self ::ERROR_CAN_NOT_PARSE_METADATA , [$ path ]);
840+ // If the metadata can not be parsed, the resource should still be returned (or a 404)
841+ // @CHECKME: Should the upstream add a message to the header or something?
842+ return $ linkMeta ;
828843 }
829844
830845 $ toRdfPhp = $ graph ->toRdfPhp ();
831846
832847 $ rdfPaths = array_keys ($ toRdfPhp );
833848 $ foundPath = $ this ->findPath ($ rdfPaths , $ path );
834849
850+ // If the requested path is a sub folder or file, it also needs te be handled
851+ foreach ($ rdfPaths as $ rdfPath ) {
852+ if (strpos ($ rdfPath , $ path ) !== false ) {
853+ $ foundPath = $ rdfPath ;
854+ break ;
855+ }
856+ }
857+
835858 if (isset ($ toRdfPhp [$ foundPath ])) {
836859 $ filteredRdfData = array_filter ($ toRdfPhp [$ foundPath ], static function ($ key ) {
837860 $ uris = implode ('| ' , [
@@ -855,12 +878,18 @@ private function parseLinkedMetadata(string $path)
855878 $ value = array_pop ($ linkMetaValue );
856879 $ url = $ value ['value ' ] ?? null ;
857880
881+ if (strpos ($ foundPath , './ ' ) === 0 ) {
882+ // Filepath is relative to the meta file
883+ $ path = $ foundPath ;
884+ }
885+
858886 if ($ path !== $ foundPath ) {
859887 // Change the path from the request to the redirect (or not found) path
860888 $ url = substr_replace ($ path ,
861889 $ url ,
862890 strpos ($ path , $ foundPath ),
863- strlen ($ foundPath ));
891+ strlen ($ foundPath ))
892+ ;
864893 }
865894
866895 $ linkMeta = [
@@ -885,12 +914,69 @@ private function findPath(array $rdfPaths, string $path)
885914 // @FIXME: We have no way of knowing if the file is a directory or a file.
886915 // This means that, unless we make a trialing slash `/` required,
887916 // (using the example for `forget.ttl`) forget.ttl/foo.txt will
888- // also work although semantically is should not
917+ // also work although semantically it should not
889918 $ path = $ rdfPath ;
890919 break ;
891920 }
892921 }
893922
894923 return $ path ;
895924 }
925+
926+ private function removeLinkFromMetaFileFor ($ path ): bool
927+ {
928+ $ result = false ;
929+
930+ if ($ this ->hasDescribedBy ($ path )) {
931+ $ describedByPath = $ this ->getDescribedByPath ($ path );
932+
933+ $ graph = $ this ->getGraph ();
934+
935+ try {
936+ $ contents = $ this ->filesystem ->read ($ describedByPath );
937+ $ graph ->parse ($ contents , 'turtle ' , '/ ' .$ describedByPath );
938+ } catch (\Throwable $ e ) {
939+ return false ;
940+ }
941+
942+ // A resource might be added for a folder but written to a file,
943+ // or vice-versa. In both cases, the _other_ entry also needs to be
944+ // removed. And depending on the RDF entry, the resource might have
945+ // a leading slash or not, so that also needs to be checked.
946+ $ normalizedPath = trim ($ path , '/ ' );
947+ $ resourcePaths = array_unique ([
948+ $ normalizedPath ,
949+ $ normalizedPath . '/ ' ,
950+ '/ ' . $ normalizedPath ,
951+ '/ ' . $ normalizedPath . '/ ' ,
952+ ]);
953+
954+ // @CHECKME: If an entry for a sub-folder is present but then a file is written,
955+ // removing the folder, should the sub-folder entry also be removed?
956+
957+ $ changed = false ;
958+ foreach ($ resourcePaths as $ resourcePath ) {
959+ $ resource = $ graph ->resource ($ resourcePath );
960+
961+ $ predicates = $ resource ->propertyUris ();
962+ foreach ($ predicates as $ predicate ) {
963+ if (strpos ($ predicate , 'https://purl.org/pdsinterop/link-metadata# ' ) === 0 ) {
964+ $ changed = true ;
965+ $ graph ->deleteSingleProperty ($ resource , $ predicate );
966+ }
967+ }
968+ }
969+
970+ if ($ changed ) {
971+ $ changedContents = $ graph ->serialise ('turtle ' );
972+ try {
973+ $ result = $ this ->filesystem ->update ($ describedByPath , $ changedContents );
974+ } catch (FileNotFoundException $ exception ) {
975+ // $result is already false;
976+ }
977+ }
978+ }
979+
980+ return $ result ;
981+ }
896982}
0 commit comments