https://crispy.cat/software/crispage Crispage is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. */ namespace Crispage; defined("ROOT") or die(); require_once ROOT . "/core/app/assets/Asset.php"; require_once ROOT . "/core/app/assets/class/Module.php"; require_once ROOT . "/core/app/assets/class/Plugin.php"; require_once ROOT . "/core/app/assets/class/Role.php"; require_once ROOT . "/core/app/assets/class/User.php"; use \Crispage\Events\Receiver; use \Crispage\Text\ShortcodeRenderer; class AssetManager { public \Crispage $app; private array $registeredClasses = []; public function __construct(\Crispage $app) { $this->app = $app; // Register default assets $this->registerClass("\\Crispage\\Assets\\Module"); $this->registerClass("\\Crispage\\Assets\\Plugin"); $this->registerClass("\\Crispage\\Assets\\Role"); $this->registerClass("\\Crispage\\Assets\\User"); // Automatically update routes $this->app->dispatcher->register( new Receiver( "crispage.assets.update_routes_created", "crispage.assets.asset_created", 0, function (Asset $asset): void { $this->app->router->createContentRoute($asset); $this->app->router->createAdditionalContentRoutes($asset); } ) ); $this->app->dispatcher->register( new Receiver( "crispage.assets.update_routes_set", "crispage.assets.asset_set", 0, function (Asset $asset): void { $this->app->router->deleteContentRoutes($asset); $this->app->router->createContentRoute($asset); $this->app->router->createAdditionalContentRoutes($asset); } ) ); $this->app->dispatcher->register( new Receiver( "crispage.assets.update_routes_deleted", "crispage.assets.asset_deleted", 0, function (Asset $asset): void { $this->app->router->deleteContentRoutes($asset); } ) ); $this->app->shortcodes->mapShortcode(new ShortcodeRenderer( "asset", fn(array $args): string => $this->app->resolveUri("asset://" . ($args["id"] ?? 0)) )); } public function getRegisteredClasses(): array { return $this->registeredClasses; } public function registerClass(string $classname): void { if (!in_array($classname, $this->registeredClasses)) $this->registeredClasses[] = $classname; } public function unregisterClass(string $classname): void { $this->registeredClasses = array_filter( $this->registeredClasses, fn(string $cn): bool => $cn != $classname ); } public function getTable(string $classname): string { return "assets_" . substr(md5($classname), 0, 8) . @end(explode("\\", $classname)); } public function getNewID(): int { // Get id from table $res = $this->app->database->select("system", ["next_asset_id"]); $id = $res->fetchColumn(); // Increment id $this->app->database->increment("system", "next_asset_id"); // Trigger event $this->app->dispatcher->trigger("crispage.assets.id_incremented", $id + 1); // Return id return $id; } public function createTable(string $classname): bool { // Error if class is not an asset if (!is_a($classname, "\\Crispage\\Asset", true)) return false; // Get table name $table = $this->getTable($classname); // Create ReflectionClass $rc = new \ReflectionClass($classname); // Build array of columns $cols = [ "id" => [$this->app->database::TYPE_INT, false], "slug" => [$this->app->database::TYPE_STRING, false] ]; foreach ($rc->getProperties() as $prop) { $name = $prop->getName(); // If name starts with _, or is "id" or "slug", skip if ($name[0] == "_" || $name == "id" || $name == "slug") continue; $type = $prop->getType(); // If type is not \ReflectionNamedType, skip if (!is_a($type, "\\ReflectionNamedType")) continue; // Determine column type from property type switch ($type->getName()) { case "bool": $ctype = $this->app->database::TYPE_BOOL; break; case "int": $ctype = $this->app->database::TYPE_INT; break; case "float": $ctype = $this->app->database::TYPE_FLOAT; break; default: $ctype = $this->app->database::TYPE_STRING; } // Add to columns array $cols[$name] = [$ctype, $type->allowsNull()]; } // Create the table $this->app->database->createTable($table, $cols); $this->app->database->addPrimaryKey($table, "id"); $this->app->database->addForeignKey($table, "id", "assets", "id"); $this->app->database->addUnique($table, "slug"); // Trigger event $this->app->dispatcher->trigger( "crispage.assets.table_created", $classname, $table ); return true; } private function constructAsset(string $table, string $class, int $id): ?Asset { // Get data from table $res = $this->app->database->select($table, null, ["id" => $id]); $row = $res->fetch(); // Check that data exists and class exists if (!$row) return null; if (!class_exists($class)) return null; // Return asset from data $row["_table"] = $table; return new $class($row); } public function get(int $id): ?Asset { // Get asset info from table $res = $this->app->database->select("assets", null, ["id" => $id]); $row = $res->fetch(); // Check that data exists if (!$row) return null; // Return asset return $this->constructAsset( $row["tablename"], $row["classname"], $id ); } public function getBySlug(string $classname, string $slug): ?Asset { // Get asset info from table $res = $this->app->database->select( "assets", null, ["classname" => $classname, "slug" => $slug] ); $row = $res->fetch(); // Check that data exists if (!$row) return null; // Return asset return $this->constructAsset( $row["tablename"], $row["classname"], $row["id"] ); } public function getAll(string $classname = null): \Generator { // Get asset info from table $res = $this->app->database->select( "assets", null, ($classname) ? ["classname" => $classname] : [] ); // Walk through and yield assets while (($row = $res->fetch()) !== false) { yield $this->constructAsset( $row["tablename"], $row["classname"], $row["id"] ); } } public function getAllFiltered( string $classname, array $filters = [], string $sortby = null, bool $desc = false ): \Generator { if (!class_exists($classname)) return; // Get table name $table = $this->getTable($classname); // Get ids from table $res = $this->app->database->select( $table, ["id"], $filters, $sortby, $desc ); // Walk through and yield assets while (($row = $res->fetch()) !== false) yield $this->constructAsset($table, $classname, $row["id"]); } public function countAssets(string $classname = null): int { // Get count $res = $this->app->database->select( "assets", ["COUNT(*)"], ($classname) ? ["classname" => $classname] : [] ); return $res->fetchColumn(); } public function create( string $classname, array $data, bool $temp = false ): Asset { // Error if class is not an asset if ( !class_exists($classname) || !is_a($classname, "\\Crispage\\Asset", true) ) { $this->app->handleException( new CrispageException("$classname is not an Asset", 500) ); } // Create asset $asset = new $classname($data); $asset->id = $this->getNewID(); if (!$temp) { // Get table name $asset->_table = $this->getTable($classname); // Insert into tables $this->app->database->insert("assets", [ "id" => $asset->id, "classname" => "\\" . $asset::class, "tablename" => $asset->_table, "slug" => $asset->slug ]); $this->app->database->insert( $asset->_table, $asset->toArray() ); // Trigger event $this->app->dispatcher->trigger( "crispage.assets.asset_created", $asset ); } else { $this->app->dispatcher->trigger( "crispage.assets.temporary_asset_created", $asset ); } return $asset; } public function set(Asset $asset): void { if ($asset->isTemporary()) return; $asset->mtime = time(); // Update tables $this->app->database->update( $asset->_table, $asset->toArray(), ["id" => $asset->id] ); $this->app->database->update( "assets", ["slug" => $asset->slug], ["id" => $asset->id] ); // Trigger event $this->app->dispatcher->trigger( "crispage.assets.asset_set", $asset ); } public function delete(Asset $asset): void { if ($asset->isTemporary) return; // Delete from tables $this->app->database->delete($asset->_table, ["id" => $asset->id]); $this->app->database->delete("assets", ["id" => $asset->id]); // Trigger event $this->app->dispatcher->trigger( "crispage.assets.asset_deleted", $asset ); } public function makePermanent(Asset $asset): Asset { if (!$asset->isTemporary()) return $asset; // Get table name $asset->_table = $this->getTable("\\" . $asset::class); // Insert into tables $this->app->database->insert("assets", [ "id" => $asset->id, "classname" => "\\" . $asset::class, "tablename" => $asset->_table, "slug" => $asset->slug ]); $this->app->database->insert( $asset->_table, $asset->toArray() ); // Trigger event $this->app->dispatcher->trigger( "crispage.assets.asset_created", $asset ); return $asset; } public function getURI(Asset $asset, int $max = 15): string { $uri = ""; $l = 0; while ($l < $max) { $uri = "/" . $asset->slug . $uri; $asset = $this->get($asset->getParentId()); if (!$asset) return $uri; $l++; } return $uri; } public function assetNameFromID(int $id, string $none = "B_NONE"): string { $asset = $this->get($id); if (!$asset) return $this->app->i18n->getTranslation($none); return $asset->getName(); } } ?>