This repository has been archived on 2024-04-19. You can view files and clone it, but cannot push or open issues or pull requests.
crispage-lite/patch/core/app/assets/AssetManager.php

360 lines
9.9 KiB
PHP
Raw Permalink Normal View History

2023-12-07 14:07:15 -05:00
<?php
/*
Crispage CMS
crispycat <the@crispy.cat>
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();
}
}
?>