You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
14 KiB
472 lines
14 KiB
<?php
|
|
|
|
namespace App\Wrappers;
|
|
|
|
use Auxilium\DatabaseInteractions\Deegraph\Nodes\User;
|
|
use Darksparrow\DeegraphInteractions\DataStructures\DataURL;
|
|
use Darksparrow\DeegraphInteractions\DataStructures\UUID;
|
|
use Darksparrow\DeegraphInteractions\Exceptions\InvalidUUIDFormatException;
|
|
use Darksparrow\DeegraphInteractions\QueryBuilder\QueryBuilder;
|
|
|
|
class DeegraphNode
|
|
{
|
|
/**
|
|
* Static cache for storing already instantiated nodes.
|
|
* @var array
|
|
*/
|
|
private static $cached_nodes = [];
|
|
|
|
|
|
private $RawContent = null;
|
|
private $Metadata = null;
|
|
|
|
private ?array $CachedProperties = null;
|
|
private ?array $CachedReferences = null;
|
|
private ?array $CachedPermissions = null;
|
|
|
|
private bool $HasRawContentBeenFetchedYet = false;
|
|
private bool $HasMetadataBeenFetchedYet = false;
|
|
|
|
private UUID $NodeID;
|
|
|
|
/**
|
|
* Constructor to initialise a DeegraphNode with a given ID.
|
|
* @param string $id The unique identifier for the node.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function __construct(string $id)
|
|
{
|
|
$this->NodeID = new UUID($id);
|
|
}
|
|
|
|
|
|
/**
|
|
* Fetches a Node from the database by its path.
|
|
* @param string $path The path to the Node.
|
|
* @return DeegraphNode|null The Node if found, otherwise null.
|
|
*/
|
|
public static function from_path(string $path): ?DeegraphNode
|
|
{
|
|
return GraphDatabaseConnection::node_from_path($path);
|
|
}
|
|
|
|
/**
|
|
* Converts the Node to a string by fetching its data.
|
|
* If the data is a string, it returns that string; otherwise, it returns an empty string.
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
if(is_string($this->getData()))
|
|
{
|
|
return $this->getData();
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves raw content data associated with the node.
|
|
* @return mixed Raw content or null if not fetched.
|
|
*/
|
|
public function getData(User $actor = null)
|
|
{
|
|
$content = $this->getContent($actor);
|
|
return ($content == null) ? null : $content->getData();
|
|
}
|
|
|
|
public function getContent(User $actor = null): CeilidhKitLFSObject|DataURL|null
|
|
{
|
|
if($this->getRawContent($actor) != null)
|
|
{
|
|
if(str_starts_with($this->getRawContent($actor), "data:"))
|
|
{
|
|
return new DataURL($this->getRawContent($actor));
|
|
}
|
|
elseif(str_starts_with($this->getRawContent($actor), "auxlfs:"))
|
|
{
|
|
return new CeilidhKitLFSObject($this->getRawContent($actor));
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function getRawContent(User $actor = null)
|
|
{
|
|
if($this->HasRawContentBeenFetchedYet)
|
|
{
|
|
return $this->RawContent;
|
|
}
|
|
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
|
|
$result = GraphDatabaseConnection::raw_request($actor, "/api/v1/" . $this->NodeID, "GET");
|
|
|
|
if(is_array($result))
|
|
{
|
|
if(isset($result["@data"]))
|
|
{
|
|
$this->RawContent = $result["@data"];
|
|
}
|
|
}
|
|
|
|
$this->HasRawContentBeenFetchedYet = true;
|
|
return $this->RawContent;
|
|
}
|
|
|
|
public function getUuid(): string
|
|
{
|
|
return $this->NodeID->GetPlainUUID();
|
|
}
|
|
|
|
/**
|
|
* Adds a property (link) between this Node and another Node.
|
|
* @param string $key The key representing the property.
|
|
* @param DeegraphNode $node The Node to link as a property.
|
|
* @param User|null $actor The User performing the operation.
|
|
* @param bool $force Whether to force the link creation.
|
|
* @return mixed The result of the graph database query.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function addProperty(string $key, DeegraphNode $node, User $actor = null, bool $force = false)
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
if(preg_match('/^[a-z_][a-z0-9_]*$/', $key) || preg_match('/^[0-9]+$/', $key) || $key == "#")
|
|
{ // Let's not allow injections! (Even though DDS handles permissions and damage will be limited to this user anyway, there's not really a benefit to *not* preventing injections)
|
|
// $query = "LINK {".$node->NodeID."} AS ".$key." OF {".$this->NodeID."}".($force ? " FORCE" : "");
|
|
$query = QueryBuilder::Link()
|
|
->linkOfRelativePath($node->NodeID, $this->NodeID)
|
|
->as($key);
|
|
if($force) $query = $query->force();
|
|
$query = $query->build();
|
|
return GraphDatabaseConnection::query($actor, $query);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes a property link from this Node.
|
|
* @param string $key The key of the property to unlink.
|
|
* @param User|null $actor The user performing the operation.
|
|
* @return mixed The result of the graph database query.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function unlinkProperty(string $key, User $actor = null)
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
if(preg_match('/^[a-z_][a-z0-9_]*$/', $key) || preg_match('/^[0-9]+$/', $key))
|
|
{ // Let's not allow injections! (Even though DDS handles permissions and damage will be limited to this user anyway, there's not really a benefit to *not* preventing injections)
|
|
// $query = "UNLINK ".$key." FROM {".$this->GetNodeID()."}";
|
|
$query = QueryBuilder::Unlink()
|
|
->unlinkWhat($key)
|
|
->from($this->NodeID)
|
|
->build();
|
|
if($this->CachedProperties != null)
|
|
{
|
|
if(isset($this->CachedProperties[$key]))
|
|
{
|
|
unset($this->CachedProperties[$key]); // Get rid of any dangling references to this
|
|
}
|
|
}
|
|
return GraphDatabaseConnection::query($actor, $query);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes this Node from the graph database.
|
|
* @param User|null $actor The user performing the operation.
|
|
* @return mixed The result of the graph database query.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function delete(User $actor = null)
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
|
|
// $query = "DELETE {".$this->GetNodeID()."}";
|
|
$query = QueryBuilder::Delete()
|
|
->relativePath($this->NodeID)
|
|
->build();
|
|
return GraphDatabaseConnection::query($actor, $query);
|
|
}
|
|
|
|
/**
|
|
* Checks if this Node is the same as another.
|
|
* @param DeegraphNode $n Another Node to compare.
|
|
* @return bool True if they are the same, false otherwise.
|
|
*/
|
|
public function is(DeegraphNode $n): bool
|
|
{
|
|
if($this->getId() == $n->getId())
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the UUID of the Node as a string.
|
|
* @return string Node UUID.
|
|
*/
|
|
public function getId(): string
|
|
{
|
|
return $this->NodeID->GetPlainUUID();
|
|
}
|
|
|
|
/**
|
|
* Fetches the permissions of the Node from the database.
|
|
* @param User|null $actor The user performing the operation.
|
|
* @return array|null Permissions data or null if not fetched.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function getPermissions(User $actor = null): ?array
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
if($this->CachedPermissions != null)
|
|
{
|
|
return $this->CachedPermissions;
|
|
}
|
|
$outputMap = [];
|
|
// $query = "PERMS ON {".$this->GetNodeID()."}";
|
|
$query = QueryBuilder::Permission()
|
|
->on($this->NodeID)
|
|
->build();
|
|
$response = GraphDatabaseConnection::query($actor, $query);
|
|
if(isset($response["@permissions"]))
|
|
{
|
|
foreach($response["@permissions"] as $value)
|
|
{
|
|
$outputMap[] = $value;
|
|
}
|
|
$this->CachedPermissions = $outputMap;
|
|
return $this->CachedPermissions;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches references to other Nodes.
|
|
* @param User|null $actor The user performing the operation.
|
|
* @return array|null References data or null if not fetched.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public function getReferences(User $actor = null): ?array
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
if($this->CachedReferences != null)
|
|
{
|
|
return $this->CachedReferences;
|
|
}
|
|
$outputMap = [];
|
|
// $query = "REFERENCES {".$this->GetNodeID()."}";
|
|
$query = QueryBuilder::References()
|
|
->relativePath($this->NodeID)
|
|
->build();
|
|
$response = GraphDatabaseConnection::query($actor, $query);
|
|
if(isset($response["@map"]))
|
|
{
|
|
foreach($response["@map"] as $key => $value)
|
|
{
|
|
$arr = [];
|
|
foreach($value as $refNodeId)
|
|
{
|
|
$arr[] = DeegraphNode::from_id($refNodeId);
|
|
}
|
|
$outputMap[$key] = $arr;
|
|
}
|
|
$this->CachedReferences = $outputMap;
|
|
return $outputMap;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fetches a Node by its ID, using a static cache for optimisation.
|
|
* @param string|null $id The ID of the Node.
|
|
* @return DeegraphNode|null The Node if found or created, null otherwise.
|
|
* @throws InvalidUUIDFormatException
|
|
*/
|
|
public static function from_id(string $id = null): ?DeegraphNode
|
|
{
|
|
if($id == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if(isset(self::$cached_nodes[$id]))
|
|
{
|
|
if(self::$cached_nodes[$id] instanceof DeegraphNode)
|
|
{
|
|
return self::$cached_nodes[$id]; // Skip creating the node representation - we've already loaded it!
|
|
}
|
|
}
|
|
|
|
// Do stuff
|
|
|
|
self::$cached_nodes[$id] = new DeegraphNode($id);
|
|
|
|
if(isset(self::$cached_nodes[$id]))
|
|
{
|
|
return self::$cached_nodes[$id];
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves a property of the Node by its key.
|
|
* @param string $property Property name to fetch.
|
|
* @param User|null $actor The user performing the operation.
|
|
* @return mixed|null Property value or null if not found.
|
|
*/
|
|
public function getProperty(string $property, User $actor = null): mixed
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
$temp = $this->getProperties($actor);
|
|
if(isset($temp[$property]))
|
|
return $temp[$property];
|
|
return null;
|
|
}
|
|
|
|
public function getProperties(User $actor = null): ?array
|
|
{
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
if($this->CachedProperties != null)
|
|
{
|
|
return $this->CachedProperties;
|
|
}
|
|
$outputMap = [];
|
|
// $query = "DIRECTORY {".$this->GetNodeID()."}";
|
|
$query = QueryBuilder::Directory()
|
|
->relativePath($this->NodeID)
|
|
->build();
|
|
|
|
$response = GraphDatabaseConnection::query($actor, $query);
|
|
if(isset($response["@map"]))
|
|
{
|
|
foreach($response["@map"] as $key => $value)
|
|
{
|
|
$outputMap[$key] = DeegraphNode::from_id($value);
|
|
}
|
|
$this->CachedProperties = $outputMap;
|
|
return $outputMap;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function getObjectSize()
|
|
{
|
|
$content = $this->getContent($actor);
|
|
return ($content == null) ? 0 : $content->getSize();
|
|
}
|
|
|
|
public function getMimeType()
|
|
{
|
|
$content = $this->getContent($actor);
|
|
return ($content == null) ? null : $content->getMimeType();
|
|
}
|
|
|
|
public function getTimestamp(): string
|
|
{
|
|
return date("c", strtotime($this->getNodeMetadata()["@created"]));
|
|
}
|
|
|
|
/**
|
|
* Retrieves metadata associated with the node.
|
|
* @return mixed Metadata or null if not fetched.
|
|
*/
|
|
public function getNodeMetadata(User $actor = null): ?array
|
|
{
|
|
if($this->HasMetadataBeenFetchedYet)
|
|
{
|
|
return $this->Metadata;
|
|
}
|
|
|
|
if($actor == null)
|
|
{
|
|
$actor = Session::get_current()->getUser();
|
|
}
|
|
|
|
$result = GraphDatabaseConnection::raw_request($actor, "/api/v1/{" . $this->getId() . "}", "GET");
|
|
|
|
if(is_array($result))
|
|
{
|
|
$this->Metadata = $result;
|
|
}
|
|
|
|
$this->HasMetadataBeenFetchedYet = true;
|
|
return $this->Metadata;
|
|
}
|
|
|
|
public function getTimestampInt(): false|int
|
|
{
|
|
return strtotime($this->getNodeMetadata()["@created"]);
|
|
}
|
|
|
|
public function getSchema()
|
|
{
|
|
return Schema::from_url($this->getSchemaUrl());
|
|
}
|
|
|
|
public function getSchemaUrl()
|
|
{
|
|
return isset($this->getNodeMetadata()["@schema"]) ? $this->getNodeMetadata()["@schema"] : null;
|
|
}
|
|
|
|
public function getCreator(): ?DeegraphNode
|
|
{
|
|
return DeegraphNode::from_id($this->getNodeMetadata()["@creator"]);
|
|
}
|
|
|
|
public function extendsOrInstanceOf(string $schema): bool
|
|
{
|
|
if(!isset($this->getNodeMetadata()["@schema"]))
|
|
{
|
|
return false;
|
|
}
|
|
return $this->getNodeMetadata()["@schema"] == $schema;
|
|
}
|
|
}
|
|
|