initial commit

This commit is contained in:
crispycat 2023-12-07 14:07:15 -05:00
parent 11283b22dd
commit c1a57c64f2
20 changed files with 1619 additions and 2 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build
cache
lite
patch/config.php

2
README Normal file
View File

@ -0,0 +1,2 @@
# Crispage Lite
Crispage Lite is a version of the [Crispage CMS](https://crispy.cat/software/crispage) with many features removed, intended for the development of other applications based on the framework.

View File

@ -1,2 +0,0 @@
# crispage-lite

23
build.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/bash
rm -rf build;
if [[ -d cache ]]; then
cp -r cache build;
else
git clone https://git.calitabby.com/crispage/crispage.git build;
cp -r build cache;
fi;
while IFS= read -r line; do
if [[ "$line" != "" ]]; then
rm -rf build/"$line";
fi;
done < delete.list;
cp -r patch/* build;
read -p "Copy build folder? [y/n]" copy
if [[ "$copy" = "y" ]]; then
rm -rf lite;
cp -r build lite;
fi;

8
delete.list Normal file
View File

@ -0,0 +1,8 @@
.git
_crispage
core/app/assets
core/actions
core/components
core/templates
media/assets
media/uploads

View File

@ -0,0 +1,33 @@
<?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\Actions;
defined("ROOT") or die();
class DefaultAction extends \Crispage\Response\Action {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.actions.default",
"version" => VERSION
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
// do nothing
}
}
?>

View File

@ -0,0 +1,187 @@
<?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();
use \Crispage\Auth\CorePermissions;
class Asset {
public static function getNames(): array {
return ["Asset", "Assets"];
}
public static function getPermissions(): array {
return [
"create" => CorePermissions::NEVER,
"modify" => CorePermissions::NEVER,
"modify_own"=> CorePermissions::NEVER,
"delete" => CorePermissions::NEVER,
"delete_own"=> CorePermissions::NEVER
];
}
public static function getRoutingInfo(): array {
return [
"action" => null,
"data" => []
];
}
public static function getSortingInfo(): array {
return [
"field" => "mtime",
"desc" => true
];
}
public static function getListColumns(): array {
global $Crispage;
return [
"id" => ["label" => "{%ID}"],
"slug" => [
"label" => "{%SLUG}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"ctime" => [
"label" => "{%CREATED}",
"filter" => [$Crispage->i18n, "datetime"]
],
"mtime" => [
"label" => "{%MODIFIED}",
"filter" => [$Crispage->i18n, "datetime"]
]
];
}
public static function getListActions(): array {
return [
[
"route" => "assets/editor",
"label" => "{%CREATE_NEW}",
"color" => "success"
]
];
}
public static function getListItemActions(): array {
return [
[
"route" => "assets/editor",
"label" => "{%EDIT}",
"color" => "primary",
"fields" => [
"asset_id" => "id"
]
],
[
"route" => "assets/delete",
"label" => "{%DELETE}",
"color" => "danger",
"fields" => [
"asset_id" => "id"
]
]
];
}
public static function getEditorFields(): array {
return [
"slug" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%SLUG}:"
],
"filter" => "strval"
]
];
}
public static function getOptionFields(): array {
return [];
}
public static function slug(string $text = null): string {
if (empty($text)) return strval(time());
$slug = strtolower($text);
$slug = preg_replace("/[^a-z\\d\\-_]+/", "-", $slug);
$slug = preg_replace("/-{2,}/", "-", $slug);
$slug = trim($slug, "-");
if (empty($slug)) return strval(time());
return $slug;
}
public ?string $_table;
public int $id;
public string $slug;
public int $ctime;
public int $mtime;
public string $options;
protected array $_options_array;
protected function __construct(array $props) {
$this->_table = $props["_table"] ?? null;
$this->id = $props["id"] ?? 0;
$this->slug = $props["slug"] ?? "";
$this->ctime = $props["ctime"] ?? time();
$this->mtime = $props["mtime"] ?? time();
$this->options = $props["options"] ?? "[]";
$this->_options_array = json_decode($this->options, true);
}
public function toArray(): array {
$arr = [];
foreach ($this as $k => $v) {
if ($k[0] == "_") continue;
if (is_bool($v)) $v = intval($v);
$arr[$k] = $v;
}
return $arr;
}
public function getOption(string $key, $default = null) {
return $this->_options_array[$key] ?? $default;
}
public function getOptions(): array {
return $this->_options_array;
}
public function setOption(string $key, $value): void {
$this->_options_array[$key] = $value;
$this->options = json_encode($this->_options_array);
}
public function setOptions(array $data): void {
$this->_options_array = array_merge($this->_options_array, $data);
$this->options = json_encode($this->_options_array);
}
public function isTemporary(): bool {
return $this->_table === null;
}
public function getOwnerId(): int {
return 0;
}
public function getName(): string {
return $this->slug;
}
public function getParentId(): int {
return 0;
}
}
?>

View File

@ -0,0 +1,359 @@
<?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();
}
}
?>

View File

@ -0,0 +1,167 @@
<?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\Assets;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class Module extends \Crispage\Asset {
public static function getNames(): array {
return ["{%MODULE}", "{%MODULES}"];
}
public static function getPermissions(): array {
return [
"create" => CorePermissions::MODIFY_LAYOUT,
"modify" => CorePermissions::MODIFY_LAYOUT,
"modify_own"=> CorePermissions::MODIFY_LAYOUT,
"delete" => CorePermissions::MODIFY_LAYOUT,
"delete_own"=> CorePermissions::MODIFY_LAYOUT
];
}
public static function getSortingInfo(): array {
return [
"field" => "location",
"desc" => false
];
}
public static function getListColumns(): array {
global $Crispage;
return [
"id" => ["label" => "{%ID}"],
"slug" => [
"label" => "{%SLUG}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"component" => [
"label" => "{%TYPE}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"location" => [
"label" => "{%LOCATION}",
"filter" => "htmlentities"
],
"position" => [
"label" => "{%POSITION}",
"filter" => null
],
"enabled" => [
"label" => "{%STATE}",
"filter" => function(bool $s): string {
global $Crispage;
return $Crispage->i18n->translate(
($s) ? "{%ENABLED}" : "{%DISABLED}"
);
}
],
"mtime" => [
"label" => "{%MODIFIED}",
"filter" => [$Crispage->i18n, "datetime"]
]
];
}
public static function getListActions(): array {
return [
[
"route" => "module_create",
"label" => "{%CREATE_NEW}",
"color" => "success"
]
];
}
public static function getEditorFields(): array {
return [
"enabled" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\BooleanFieldComponent",
"com_data" => [
"label" => "{%ENABLED}:"
],
"filter" => "boolval"
],
"slug" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%SLUG}:",
"attrs" => ["placeholder" => "automatic"]
],
"filter" => "strval"
],
"location" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%LOCATION}:",
"attrs" => ["required"]
],
"filter" => "strval"
],
"position" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\NumberFieldComponent",
"com_data" => [
"label" => "{%POSITION}:",
"attrs" => ["required"]
],
"filter" => "intval"
]
];
}
public static function getOptionFields(): array {
return [
"config" => [
"component" => "\\Crispage\\Components\\Fields\\ModuleConfigFieldComponent",
"com_data" => [],
"filter" => function($data): array {
global $Crispage;
if (!is_array($data)) return [];
$asset = $Crispage->assets->get(
intval($Crispage->request->params["asset_id"]) ?? 0
);
$Crispage->loadClass($asset->component);
$ndata = [];
foreach ($asset->component::getModuleFields() as $name => $field)
$ndata[$name] = $field["filter"]($data[$name] ?? null);
return $ndata;
}
]
];
}
public string $component;
public string $location;
public int $position;
public bool $enabled;
public function __construct(array $props) {
parent::__construct($props);
$this->component = $props["component"] ?? "\\Crispage\\Components\\DefaultComponent";
$this->location = $props["location"] ?? "default";
$this->position = $props["position"] ?? 0;
$this->enabled = $props["enabled"] ?? false;
}
public function config(string $key, $default = null) {
return $this->_options_array["config"][$key] ?? $default;
}
}
?>

View File

@ -0,0 +1,146 @@
<?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\Assets;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class Plugin extends \Crispage\Asset {
public static function getNames(): array {
return ["{%PLUGIN}", "{%PLUGINS}"];
}
public static function getPermissions(): array {
return [
"create" => CorePermissions::NEVER,
"modify" => CorePermissions::MODIFY_SETTINGS,
"modify_own"=> CorePermissions::MODIFY_SETTINGS,
"delete" => CorePermissions::MODIFY_SETTINGS,
"delete_own"=> CorePermissions::MODIFY_SETTINGS
];
}
public static function getSortingInfo(): array {
return [
"field" => "priority",
"desc" => false
];
}
public static function getListColumns(): array {
global $Crispage;
return [
"id" => ["label" => "{%ID}"],
"runnable" => [
"label" => "{%TYPE}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"priority" => [
"label" => "{%PRIORITY}",
"filter" => null
],
"enabled" => [
"label" => "{%STATE}",
"filter" => function(bool $s): string {
global $Crispage;
return $Crispage->i18n->translate(
($s) ? "{%ENABLED}" : "{%DISABLED}"
);
}
],
"mtime" => [
"label" => "{%MODIFIED}",
"filter" => [$Crispage->i18n, "datetime"]
]
];
}
public static function getListActions(): array {
return [];
}
public static function getListItemActions(): array {
return [
[
"route" => "assets/editor",
"label" => "{%EDIT}",
"color" => "primary",
"fields" => [
"asset_id" => "id"
]
]
];
}
public static function getEditorFields(): array {
return [
"enabled" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\BooleanFieldComponent",
"com_data" => [
"label" => "{%ENABLED}:"
],
"filter" => "boolval"
],
"priority" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\NumberFieldComponent",
"com_data" => [
"label" => "{%PRIORITY}:",
"attrs" => ["required"]
],
"filter" => "intval"
]
];
}
public static function getOptionFields(): array {
return [
"config" => [
"component" => "\\Crispage\\Components\\Fields\\PluginConfigFieldComponent",
"com_data" => [],
"filter" => function($data): array {
global $Crispage;
if (!is_array($data)) return [];
$asset = $Crispage->assets->get(
intval($Crispage->request->params["asset_id"]) ?? 0
);
$Crispage->loadClass($asset->component);
$ndata = [];
foreach ($asset->component::getPluginFields() as $name => $field)
$ndata[$name] = $field["filter"]($data[$name] ?? null);
return $ndata;
}
]
];
}
public string $runnable;
public int $priority;
public bool $enabled;
public function __construct(array $props) {
parent::__construct($props);
$this->runnable = $props["runnable"] ?? "\\Crispage\\Plugins\\DefaultPluginRunnable";
$this->priority = $props["priority"] ?? 0;
$this->enabled = $props["enabled"] ?? false;
}
public function config(string $key, $default = null) {
return $this->_options_array["config"][$key] ?? $default;
}
}
?>

View File

@ -0,0 +1,122 @@
<?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\Assets;
defined("ROOT") or die();
require_once ROOT . "/core/app/assets/Asset.php";
use \Crispage\Auth\CorePermissions;
class Role extends \Crispage\Asset {
public static function getNames(): array {
return ["{%ROLE}", "{%ROLES}"];
}
public static function getPermissions(): array {
return [
"create" => CorePermissions::MODIFY_ROLES,
"modify" => CorePermissions::MODIFY_ROLES,
"modify_own"=> CorePermissions::MODIFY_ROLES,
"delete" => CorePermissions::MODIFY_ROLES,
"delete_own"=> CorePermissions::MODIFY_ROLES
];
}
public static function getSortingInfo(): array {
return [
"field" => "rank",
"desc" => false
];
}
public static function getListColumns(): array {
global $Crispage;
return [
"id" => ["label" => "{%ID}"],
"slug" => [
"label" => "{%SLUG}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"name" => [
"label" => "{%NAME}",
"filter" => "htmlentities"
],
"rank" => [
"label" => "{%RANK}",
"filter" => null
],
"mtime" => [
"label" => "{%MODIFIED}",
"filter" => [$Crispage->i18n, "datetime"]
]
];
}
public static function getEditorFields(): array {
return [
"name" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%NAME}:",
"attrs" => ["required"]
],
"filter" => "strval"
],
"permissions" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\PermissionsFieldComponent",
"com_data" => [
"label" => "{%PERMISSIONS}:",
"attrs" => ["required"]
],
"filter" => "intval"
],
"slug" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%SLUG}:",
"attrs" => ["placeholder" => "automatic"]
],
"filter" => "strval"
],
"rank" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\NumberFieldComponent",
"com_data" => [
"label" => "{%RANK}:",
"attrs" => ["required"]
],
"filter" => "intval"
]
];
}
public string $name;
public int $permissions;
public int $rank;
public function __construct(array $props) {
parent::__construct($props);
$this->name = $props["name"] ?? "";
$this->permissions = $props["permissions"] ?? 0;
$this->rank = $props["rank"] ?? 0;
}
public function getName(): string {
return $this->name;
}
}
?>

View File

@ -0,0 +1,189 @@
<?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\Assets;
defined("ROOT") or die();
require_once ROOT . "/core/app/assets/Asset.php";
use \Crispage\Auth\CorePermissions;
class User extends \Crispage\Asset {
public const UNCONFIRMED = 0;
public const CONFIRMED = 1;
public const ACTIVATED = 2;
public static function getNames(): array {
return ["{%USER}", "{%USERS}"];
}
public static function getPermissions(): array {
return [
"create" => CorePermissions::NONE,
"modify" => CorePermissions::MODIFY_OTHERS,
"modify_own"=> CorePermissions::MODIFY_SELF,
"delete" => CorePermissions::NEVER,
"delete_own"=> CorePermissions::NEVER
];
}
public static function getSortingInfo(): array {
return [
"field" => "mtime",
"desc" => true
];
}
public static function getListColumns(): array {
global $Crispage;
return [
"id" => ["label" => "{%ID}"],
"slug" => [
"label" => "{%USERNAME}",
"filter" => "\\Crispage\\Utils\\TextUtils::codeify"
],
"displayname" => [
"label" => "{%DISPLAY_NAME}",
"filter" => "htmlentities"
],
"last_login" => [
"label" => "{%LAST_LOGIN}",
"filter" => [$Crispage->i18n, "datetime"]
],
"state" => [
"label" => "{%STATE}",
"filter" => function(int $s): string {
global $Crispage;
switch ($s) {
case self::CONFIRMED:
$str = "CONFIRMED";
break;
case self::ACTIVATED:
$str = "ACTIVATED";
break;
default:
$str = "UNCONFIRMED";
}
return $Crispage->i18n->translate("{%$str}");
}
],
"mtime" => [
"label" => "{%MODIFIED}",
"filter" => [$Crispage->i18n, "datetime"]
]
];
}
public static function getEditorFields(): array {
return [
"displayname" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%DISPLAY_NAME}:",
"attrs" => ["required"]
],
"filter" => "strval"
],
"email" => [
"column" => 0,
"component" => "\\Crispage\\Components\\Fields\\TextFieldComponent",
"com_data" => [
"label" => "{%EMAIL}:",
"attrs" => ["required"]
],
"filter" => "strval"
],
"role_ids" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\RoleSelectFieldComponent",
"com_data" => [
"label" => "{%ROLES}:"
],
"filter" => function($data): string {
if (!is_array($data)) return "[]";
$data = array_filter($data, "ctype_digit");
return json_encode(array_values($data));
}
],
"state" => [
"column" => 1,
"component" => "\\Crispage\\Components\\Fields\\SelectFieldComponent",
"com_data" => [
"label" => "{%STATE}:",
"options" => [
0 => "{%DEACTIVATED}",
2 => "{%ACTIVATED}"
]
],
"filter" => "intval"
]
];
}
public string $displayname;
public string $email;
public int $last_login;
public string $role_ids;
private array $_role_ids_array;
public int $state;
public function __construct(array $props) {
parent::__construct($props);
$this->displayname = $props["displayname"] ?? $props["slug"] ?? "";
$this->email = $props["email"] ?? "";
$this->last_login = $props["last_login"] ?? 0;
$this->role_ids = $props["role_ids"] ?? "[]";
$this->_role_ids_array = json_decode($this->role_ids, true) ?? [];
$this->state = $props["state"] ?? 0;
}
public function getRoles(): array {
return $this->_role_ids_array;
}
public function hasRole(int $id): bool {
return in_array($id, $this->_role_ids_array);
}
public function setRoles(int ...$ids): array {
$this->_role_ids_array = $ids;
$this->role_ids = json_encode($this->_role_ids_array);
return $this->_role_ids_array;
}
public function addRoles(int ...$ids): array {
$this->_role_ids_array = array_unique(array_merge(
$this->_role_ids_array, $ids
));
$this->role_ids = json_encode($this->_role_ids_array);
return $this->_role_ids_array;
}
public function removeRoles(int ...$ids): array {
$this->_role_ids_array = array_filter(
$this->_role_ids_array, fn(int $id): bool => !in_array($id, $ids)
);
$this->role_ids = json_encode($this->_role_ids_array);
return $this->_role_ids_array;
}
public function getName(): string {
return $this->displayname;
}
public function getOwnerId(): int {
return $this->id;
}
}
?>

View File

@ -0,0 +1,71 @@
<?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\Extensions;
defined("ROOT") or die();
class CoreExtensions {
public static function get(): array {
return [
/*
new Extension(
id, version, package,
classname, type, data
),
*/
new Extension(
"crispage.core", VERSION, "crispage.core",
"crispage.core", "package", []
),
// Actions
new Extension(
"crispage.actions.default", VERSION, "crispage.core",
"\\Crispage\\Actions\\DefaultAction",
"action", []
),
// Components
new Extension(
"crispage.components.default", VERSION, "crispage.core",
"\\Crispage\\Components\\DefaultComponent",
"component", ["is_module" => true]
),
// Plugins
new Extension(
"crispage.plugins.default", VERSION, "crispage.core",
"\\Crispage\\Plugins\\DefaultPluginRunnable",
"component", []
),
// Templates
new Extension(
"crispage.templates.default", VERSION, "crispage.core",
"default", "template", []
),
// Translations
new Extension(
"crispage.translations.en_us.default", VERSION, "crispage.core",
"en-US_default", "translation", []
)
];
}
}
?>

View File

@ -0,0 +1,41 @@
<?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\Components;
defined("ROOT") or die();
class DefaultComponent extends \Crispage\Response\Component
implements \Crispage\Response\ModuleComponent {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.components.default",
"version" => VERSION,
"name" => "default",
"author" => "crispycat",
"description" => "The Crispage default module."
];
}
public static function getModuleFields(): array {
return [];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
// do nothing
}
}
?>

View File

@ -0,0 +1,64 @@
<?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\Components;
defined("ROOT") or die();
class ErrorInfoComponent extends \Crispage\Response\Component {
public const TITLES = [
400 => "BAD_REQUEST",
401 => "UNAUTHORIZED",
403 => "FORBIDDEN",
404 => "PAGE_NOT_FOUND",
500 => "INTERNAL_SERVER_ERROR"
];
public static function getExtensionInfo(): array {
return [
"id" => "crispage.components.errorinfo",
"version" => VERSION
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
$exceptions = $this->data["exceptions"] ?? [];
if (empty($exceptions)) return;
$code = $exceptions[0]->getCode();
$message = $exceptions[0]->getMessage();
$title = $this->app->i18n->getTranslation(self::TITLES[$code] ?? "ERROR");
echo "<h1 class=\"title is-1\"><span class=\"tag is-danger is-large\">"
. "$code</span>&nbsp;$title</h1><p class=\"subtitle is-3\">$message</p>";
($this->app->i18n)("<p>{%AN_ERROR_HAS_OCCURRED_}</p>");
if (!$exceptions[0]->showDebug) return;
echo "<hr/>";
($this->app->i18n)("<h2 class=\"title is-2\">{%DEBUG_INFO}</h2>\n<pre>");
foreach ($exceptions as $ind => $exception) {
if (!$exception->showDebug ?? true) continue;
echo "=======[Exception $ind]=======\n$exception\n\n";
}
echo "</pre>";
}
}
?>

View File

@ -0,0 +1,91 @@
<?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\Components;
defined("ROOT") or die();
class HeadTagsComponent extends \Crispage\Response\Component {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.components.headtags",
"version" => VERSION
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
$blocks = $this->data["render"] ?? ["title", "metas", "links", "scripts", "styles"];
foreach ($blocks as $block) {
switch ($block) {
case "title": {
$title = $this->app->page->data["title"] ?? "";
$asset = $this->app->assets->get($this->app->route->data["asset_id"] ?? 0);
if ($asset && !$asset->getOption("show_title_suffix", true)) {
$prefix = "";
$suffix = "";
}
else {
$prefix = $this->app->settings->get("crispage.page.title_prefix", "");
$suffix = $this->app->settings->get("crispage.page.title_suffix", "");
}
echo "<title>$prefix$title$suffix</title>";
break;
}
case "metas": {
foreach ($this->app->page->data["metas"] as $meta) {
if (!$meta) continue;
echo "<meta";
foreach ($meta as $attr => $val) echo " $attr=\"$val\"";
echo ">";
}
break;
}
case "links": {
foreach ($this->app->page->data["links"] as $link) {
if (!$link) continue;
echo "<link";
foreach ($link as $attr => $val) echo " $attr=\"$val\"";
echo ">";
}
break;
}
case "scripts": {
foreach ($this->app->page->data["scripts"] as $script) {
if (!$script) continue;
echo "<script";
foreach ($script as $attr => $val)
if ($attr != "_inner") echo " $attr=\"$val\"";
echo ">" . ($script["_inner"] ?? "") . "</script>";
}
break;
}
case "styles": {
foreach ($this->app->page->data["styles"] as $style) {
if (!$style) continue;
echo "<style";
foreach ($style as $attr => $val)
if ($attr != "_inner") echo " $attr=\"$val\"";
echo ">" . ($style["_inner"] ?? "") . "</style>";
}
break;
}
}
}
}
}
?>

View File

@ -0,0 +1,41 @@
<?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\Components;
defined("ROOT") or die();
class MessagesComponent extends \Crispage\Response\Component {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.components.messages",
"version" => VERSION
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
?>
<div id="messages">
<?php foreach ($this->app->page->data["messages"] as $id => $msg) { ?>
<div id="msg_<?php echo $id; ?>" class="notification is-<?php echo $msg["color"]; ?> mb-2">
<?php echo $msg["content"]; ?>
</div>
<?php } ?>
</div>
<?php
}
}
?>

View File

@ -0,0 +1 @@
<?php $this->app->page->renderMainComponent(); ?>

View File

@ -0,0 +1,36 @@
<?php
$com_head = $this->app->page->createComponent(
"\\Crispage\\Components\\HeadTagsComponent",
["render" => ["title", "metas", "links", "scripts", "styles"]]
);
?>
<!DOCTYPE html>
<html lang="<?php echo $this->app->i18n->getLanguage(); ?>" dir="<?php echo $this->app->i18n->getDirection(); ?>">
<head>
<?php $this->app->page->renderComponent($com_head); ?>
<style>
html {
background: #7f7f7f;
}
body {
background: #ffffff;
font-family: monospace;
width: 100%;
max-width: 960px;
margin: 20px auto;
padding: 10px;
}
</style>
</head>
<body>
<?php
$com_msgs = $this->app->page->createComponent(
"\\Crispage\\Components\\MessagesComponent", []
);
$this->app->page->renderComponent($com_msgs);
$this->app->page->renderMainComponent();
?>
</body>
</html>

View File

@ -0,0 +1,34 @@
<?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 CrispageLite;
defined("ROOT") or die();
class DefaultAction extends \Crispage\Response\Action {
public static function getExtensionInfo(): array {
return [
"id" => "crispagelite.defaultaction",
"version" => VERSION
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
$this->app->page->setPersistentMessage("lite_installed", "Crispage Lite has been installed successfully.");
$this->app->page->actionFinished();
}
}
?>