initial commit

This commit is contained in:
crispycat 2024-05-27 00:44:14 -04:00
commit b6387dfc0f
19 changed files with 1454 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
_license/**
_build/**
cache/**
core/**
lib/**
media/**
packages/**
temp/**
user/**
userlib/**
.htaccess
index.php
package.php
_crispage/**
!_crispage/version
_scripts/build_package.sh
_scripts/pull_corelibs.sh
LICENSE
README.md

1
_crispage/version Normal file
View File

@ -0,0 +1 @@
0.25.1a

27
_scripts/pull_crispage.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
PWD="$(pwd)";
WORK_DIR="${1:-$PWD}";
BUILD_DIR="$WORK_DIR/_build";
GIT_URL="https://git.calitabby.com/crispage/crispage.git";
REPO_DIR="$BUILD_DIR/repo";
VERSION="$(cat _crispage/version)";
echo "Pulling crispage";
echo "Cleaning build directory";
rm -rf "$BUILD_DIR";
mkdir -p "$REPO_DIR";
echo "Cloning repository";
git clone "$GIT_URL" -b "$VERSION" "$REPO_DIR";
rm -rf "$REPO_DIR/.git";
echo "Cleaning destinations";
rm -rf "$WORK_DIR/core";
rm -rf "$WORK_DIR/lib";
echo "Copying files";
cp -rv "$REPO_DIR"/* "$WORK_DIR";
echo "Copied";

BIN
cds.sqlite Normal file

Binary file not shown.

170
cds/CDSPackager.php Normal file
View File

@ -0,0 +1,170 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\ApplicationConfig;
use \Crispage\Utils\FileUtils;
class CDSPackager {
public DevSuite $cds;
public \Crispage $app;
public readonly string $pkgpath;
public function __construct(DevSuite $cds) {
$this->cds = $cds;
$this->app = $cds->app;
$this->pkgpath = ROOT . ApplicationConfig::get("cds.package_path");
if (!file_exists($this->pkgpath)) {
mkdir($this->pkgpath);
mkdir("$this->pkgpath/build");
mkdir("$this->pkgpath/archive");
}
}
public function getPath(string $package): string {
return "$this->pkgpath/build/$package";
}
public function initDirectory(string $package): void {
$path = $this->getPath($package);
if (file_exists($path)) FileUtils::emptyDirectory($path);
else mkdir($path);
mkdir("$path/_crispage");
mkdir("$path/user");
}
public function createInfoFile(
string $package, array $info, array $pdata, array $exts, array $files, bool $dplugins = true
): void {
$path = $this->getPath($package) . "/_crispage/package.ini";
$info["type"] = "package";
$info["package"] = $info["id"];
$info["classname"] = $info["id"];
$einfo = [];
$edata = [];
$pdata["contents"] = $files;
if ($dplugins) $pdata["plugins"] = [];
foreach ($exts as $ext) {
$einfo[$ext->id] = [
"id" => $ext->id,
"version" => $ext->version,
"package" => $ext->package,
"classname" => $ext->classname,
"type" => $ext->type
];
$edata[$ext->id] = $ext->data;
if ($dplugins && $ext->type == "plugin")
$pdata["plugins"][] = $ext->id;
}
IniWriter::write($path, [
"ExtensionInfo" => $info,
"PackageData" => $pdata,
"Extensions" => $einfo,
"ExtensionData" => $edata
]);
}
public function copyDefaultScripts(string $package): int {
$path = $this->getPath($package) . "/_crispage/scripts";
mkdir($path);
return FileUtils::copyDirectory(ROOT . "/cds/static/defaultscripts", $path);
}
public function copyCodeDirectory(string $package, string $dir): int {
$from = ROOT . $dir;
if (!file_exists($from)) return 0;
$to = $this->getPath($package) . "/user/" . basename($dir);
return FileUtils::copyDirectory($from, $to);
}
public function copyFiles(string $package, string $path): int {
$from = ROOT . $path;
$to = $this->getPath($package) . $path;
if (!file_exists($from)) return 0;
if (is_dir($from)) {
@mkdir(dirname($to), 0777, true);
return FileUtils::copyDirectory($from, $to);
}
else {
copy($from, $to);
return 1;
}
}
public function buildPackage(
string $package, array $info, array $pdata, array $exts,
array $cdirs, array $files, bool $dscripts = true, bool $dplugins = true
): string {
$info["id"] = $package;
$this->cds->log("Building package $package", "Packager");
$this->cds->log(
sprintf(
"Including %d extensions, %d code directories, %d additional files",
count($exts), count($cdirs), count($files)
)
);
$this->cds->log("Initializing package directory...");
$this->initDirectory($package);
$this->cds->log("Creating info file...");
$this->createInfoFile($package, $info, $pdata, $exts, $files, $dplugins);
if ($dscripts) {
$this->cds->log("Copying default scripts...");
$this->copyDefaultScripts($package);
}
$this->cds->log("Copying code directories...");
$count = 0;
foreach ($cdirs as $dir) {
$this->cds->log(">>> $dir");
$count += $this->copyCodeDirectory($package, $dir);
}
$this->cds->log("Copied $count files");
$this->cds->log("Copying additional files...");
$count = 0;
foreach ($files as $path) {
$this->cds->log(">>> $path");
$count += $this->copyFiles($package, $path);
}
$this->cds->log("Copied $count files");
return $this->getPath($package);
}
public function targzPackage(string $package): ?string {
$this->cds->log("Creating package archive for $package", "Packager");
$path = $this->getPath($package);
$info = @parse_ini_file("$path/_crispage/package.ini", true, INI_SCANNER_TYPED);
$id = ($info) ? ($info["Package"]["id"] ?? $package) : $package;
$version = ($info) ? ($info["Package"]["version"] ?? "0.0.0") : "0.0.0";
$arcname = "{$id}_$version.tar.gz";
$arcpath = "$this->pkgpath/archive/$arcname";
try {
$arc = new \PharData($arcpath);
$arc->buildFromDirectory($path);
$this->cds->log("Archive $arcname created");
return $arcpath;
}
catch (\Exception $e) {
$this->cds->log("Failed to create archive:\n$e");
return null;
}
}
}
?>

268
cds/DevSuite.php Normal file
View File

@ -0,0 +1,268 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
require_once ROOT . "/cds/IniWriter.php";
require_once ROOT . "/cds/CDSPackager.php";
use \Crispage\ApplicationConfig;
use \Crispage\Request\Route;
use \Crispage\Signaling\Receiver;
use \Crispage\Extensions\Extension;
use \Crispage\Assets\Plugin;
class DevSuite {
public const EXT_CLASS_MAP = [
"asset" => "\\Crispage\\Framework\\Asset",
"action" => "\\Crispage\\Framework\\Action",
"component" => "\\Crispage\\Framework\\Component",
"plugin" => "\\Crispage\\Framework\\PluginClass",
"modulecomponent" => "\\Crispage\\Framework\\ModuleComponent"
];
public static function nicePrint(mixed $obj): string {
if (is_object($obj))
return "{" . $obj::class . "}";
if (is_array($obj))
return "[" . count($obj) . "]";
if (is_string($obj)) return "\"$obj\"";
return @strval($obj);
}
public \Crispage $app;
public CDSPackager $packager;
private array $logs;
private string $logprefix = "";
private bool $print_enabled = true;
public function __construct(\Crispage $app) {
$this->app = $app;
@$this->app->cds = $this;
$this->packager = new CDSPackager($this);
}
public function getLogPrefix(): ?string {
return $this->logprefix;
}
public function setLogPrefix(string $prefix) {
$this->logprefix = $prefix;
}
public function clearLogPrefix(): void {
$this->logprefix = "";
}
public function disableLogPrint(): void {
$this->print_enabled = false;
}
public function log(string $msg, ?string $tag = null): float {
$time = microtime(true);
if ($tag) $this->setLogPrefix("[$tag] ");
$fmsg = $this->logprefix . preg_replace("/\\n/", "\n\t", $msg);
$this->logs[] = [$fmsg, $time];
return $time;
}
public function logEvent(...$args): float {
$event = $this->app->dispatcher->event();
$ev = $this->app->dispatcher->getValue()->current();
if ($ev) $this->app->dispatcher->pushValue($ev);
$args = array_map([$this, "nicePrint"], $args);
return $this->log("$event(" . implode(", ", $args) . "): " . self::nicePrint($ev), "Event");
}
public function printLogs(): void {
if (!$this->print_enabled) return;
$this->app->dispatcher->suppress();
@ob_end_flush();
echo "<pre class=\"cds_log\">";
foreach ($this->logs as $log)
printf("[%.6f] %s\n", $log[1] - START_TIME, $log[0]);
echo "</pre>";
}
public function init(): void {
$this->app->dispatcher->register(new Receiver(
"crispage.devsuite.log_receiver",
"", -127, [$this, "logEvent"]
));
register_shutdown_function([$this, "printLogs"]);
$this->app->router->registerHardRoute(new Route(
"cds_console", "\\Crispage\\DevSuite\\ConsoleAction",
[]
), "backend");
$this->app->router->registerHardRoute(new Route(
"cds_classmanager", "\\Crispage\\DevSuite\\ClassManagerAction",
[]
), "backend");
$this->app->router->registerHardRoute(new Route(
"cds_packager", "\\Crispage\\DevSuite\\PackagerAction",
[]
), "backend");
$this->app->page->data["styles"]["cds"] = [
"_inner" => file_get_contents(ROOT . "/cds/static/assets/cds.css")
];
$this->log("CDS Initialized");
}
public function runScript(string $script): string {
$this->log("Running script", "Console");
$output = "";
ob_start();
try {
$start = microtime(true);
$res = eval($script);
$end = microtime(true);
}
catch (\Throwable $e) {
$err = $e;
}
$output .= ob_get_clean();
if (isset($err)) {
$this->log("ERROR: $err");
$output .= "\n== Script error ==\n$err";
}
else {
$ms = sprintf("%.3f", ($end - $start) * 1000);
$rval = print_r($res ?? "[nothing returned]", true);
$this->log("Executed in $ms ms");
$this->log("Return value: $rval");
$output .= "\n== Script executed in $ms ms ==\nReturn value:\n$rval";
}
return $output;
}
public function getExtensions(): array {
$res = $this->app->database->select(
"extensions", ["id"]
);
$exts = [];
foreach ($res->fetchAll(\PDO::FETCH_COLUMN) as $id) {
$ext = $this->app->extensions->get($id);
if ($ext) $exts[] = $ext;
}
return $exts;
}
public function registerExtension(string $classname, string $type): ?Extension {
$info = ApplicationConfig::get("cds.default_info");
$this->log("Registering $classname as $type...", "Extension");
switch ($type) {
case "asset":
case "action":
case "component":
case "plugin": {
try {
$this->app->loadClass($classname, self::EXT_CLASS_MAP[$type]);
}
catch (\Exception $e) {
return null;
}
$info = $classname::getExtensionInfo();
if (is_a($classname, self::EXT_CLASS_MAP["modulecomponent"], true))
$info["is_module"] = true;
break;
}
case "template": {
foreach (Config::TEMPLATE_PATH as $tp) {
$path = ROOT . "$tp/$classname/template.ini";
if (file_exists($path)) break;
}
$ini = parse_ini_file($path, true, INI_SCANNER_TYPED);
$info = array_merge($info, $ini["ExtensionInfo"] ?? []);
break;
}
case "translation": {
foreach (Config::TRANSLATION_PATH as $tp) {
$path = ROOT . "$tp/$classname/translations.ini";
if (file_exists($path)) break;
}
$ini = parse_ini_file($path, true, INI_SCANNER_TYPED);
$info = array_merge($info, $ini["ExtensionInfo"] ?? []);
break;
}
default:
return null;
}
$id = $info["id"];
$version = $info["version"] ?? VERSION;
$package = $info["package"] ?? explode(".", $id)[0] ?? "";
$data = array_diff_key($info, ApplicationConfig::get("cds.default_info"));
$ext = new Extension($id, $version, $package, $classname, $type, $data);
$this->app->extensions->register($ext);
$this->log("Success");
return $ext;
}
public function unregisterExtensions(array $ids): int {
$count = 0;
foreach ($ids as $id) {
$this->log("Unregistering $id...", "Extension");
$ext = $this->app->extensions->get($id);
if ($ext) {
$this->app->extensions->unregister($ext);
$count++;
}
}
$this->log("Unregistered $count extensions");
return $count;
}
public function createPlugin(string $extid, int $priority = 0): ?Plugin {
$ext = $this->app->extensions->get($extid);
if (!$ext || $ext->type != "plugin") return null;
$plugin = $this->app->assets->create(
"\\Crispage\\Assets\\Plugin", [
"slug" => preg_replace("/\\./", "__", $ext->id),
"classname" => $ext->classname,
"priority" => $priority
]
);
$this->log("Created plugin $extid", "Extension");
return $plugin;
}
public function deletePlugin(string $extid): void {
$ext = $this->app->extensions->get($extid);
if (!$ext || $ext->type != "plugin") return;
$plugins = $this->app->assets->getAllFiltered(
"\\Crispage\\Assets\\Plugin",
["classname" => $ext->classname]
);
foreach ($plugins as $plugin) {
if ($plugin->slug == "devsuite") continue;
$this->app->assets->delete($plugin);
}
$this->log("Deleted plugin $extid", "Extension");
}
}
?>

56
cds/IniWriter.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Config;
use \Crispage\Utils\FileUtils;
class IniWriter {
public const INI_FALSE = "False";
public const INI_TRUE = "True";
public static function inival(mixed $value): string {
if (is_bool($value))
return ($value) ? self::INI_TRUE : self::INI_FALSE;
if (is_int($value) || is_float($value))
return strval($value);
if (is_string($value)) return "\"$value\"";
return "";
}
public static function section(array $data, ?string $section = null): string {
$str = "";
if ($section) $str .= "[$section]\n";
foreach ($data as $key => $value) {
if (is_array($value)) {
foreach ($value as $k => $v)
$str .= "{$key}[$k] = " . self::inival($v) . "\n";
}
else $str .= "$key = " . self::inival($value) . "\n";
}
$str .= "\n";
return $str;
}
public static function ini(array $data, bool $sections = true): string {
$ini = "";
if ($sections) {
foreach ($data as $section => $sdata)
$ini .= self::section($sdata, $section);
}
else $ini .= self::section($data);
return $ini;
}
public static function write(string $path, array $data, bool $sections = true): int|false {
$ini = self::ini($data, $sections);
return file_put_contents($path, $ini);
}
}
?>

View File

@ -0,0 +1,98 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class ClassManagerAction extends \Crispage\Framework\Action {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.actions.classmanager",
"version" => VERSION,
"package" => "crispage.devsuite"
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
$this->app->page->data["title"] = $this->app->i18n->translate("{%CDS_CLASS_MANAGER}");
if (!$this->app->auth->userHasPermission(
CorePermissions::MANAGE_EXTENSIONS
)) {
$this->app->page->setPersistentMessage(
"unauthorized", "Unauthorized", "danger"
);
$this->app->page->actionFinished();
};
$action = strval($this->app->request->params["action"] ?? "");
switch ($action) {
case "":
break;
case "register": {
$classname = strval($this->app->request->params["classname"] ?? "");
$type = strval($this->app->request->params["type"] ?? "");
if (!$this->app->cds->registerExtension($classname, $type))
$err = "Could not register $classname as $type";
break;
}
case "unregister": {
$exts = $this->app->request->params["exts"] ?? [];
if (empty($exts) || !is_array($exts))
break;
$this->app->cds->unregisterExtensions($exts);
break;
}
case "plugin_create": {
$plugin = strval(
$this->app->request->params["ext"] ?? ""
);
$priority = intval(
$this->app->request->params["priority"] ?? 0
);
$this->app->cds->createPlugin($plugin);
break;
}
case "plugin_delete": {
$plugin = strval(
$this->app->request->params["ext"] ?? ""
);
$this->app->cds->deletePlugin($plugin);
break;
}
default:
$err = "Invalid action";
}
if (isset($err)) {
$this->app->page->data["messages"]["error"] = [
"content" => $err,
"color" => "danger"
];
}
elseif (strlen($action)) {
$this->app->page->data["messages"]["success"] = [
"content" => "Success",
"color" => "success"
];
}
$com_main = $this->app->page->createComponent(
"\\Crispage\\DevSuite\\ClassManagerComponent",
["extensions" => $this->app->cds->getExtensions()]
);
$this->app->page->setMainComponent($com_main);
$this->app->page->actionFinished();
}
}
?>

View File

@ -0,0 +1,46 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class ConsoleAction extends \Crispage\Framework\Action {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.actions.console",
"version" => VERSION,
"package" => "crispage.devsuite"
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
$this->app->page->data["title"] = $this->app->i18n->translate("{%CDS_CONSOLE}");
if (!$this->app->auth->userHasPermission(
CorePermissions::MANAGE_EXTENSIONS
)) {
$this->app->page->setPersistentMessage(
"unauthorized", "Unauthorized", "danger"
);
$this->app->page->actionFinished();
}
$script = strval($this->app->request->params["script"] ?? "");
if (strlen($script))
$output = $this->app->cds->runScript($script);
$com_main = $this->app->page->createComponent(
"\\Crispage\\DevSuite\\ConsoleComponent",
["script" => $script, "output" => $output ?? ""]
);
$this->app->page->setMainComponent($com_main);
$this->app->page->actionFinished();
}
}
?>

View File

@ -0,0 +1,133 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
use \Crispage\ApplicationConfig;
use \Crispage\Utils\URIUtils;
class PackagerAction extends \Crispage\Framework\Action {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.actions.packager",
"version" => VERSION,
"package" => "crispage.devsuite"
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
$this->app->page->data["title"] = $this->app->i18n->translate("{%CDS_PACKAGER}");
if (!$this->app->auth->userHasPermission(
CorePermissions::MANAGE_EXTENSIONS
)) {
$this->app->page->setPersistentMessage(
"unauthorized", "Unauthorized", "danger"
);
$this->app->page->actionFinished();
};
$mode = strval($this->app->request->params["mode"] ?? "");
$package = strval(
$this->app->request->params["package"]
?? ApplicationConfig::get("cds.default_info")["id"]
);
switch ($mode) {
case "build": {
$version = strval(
$this->app->request->params["version"]
?? ApplicationConfig::get("cds.default_info")["version"]
);
$pdata = parse_ini_string(
strval($this->app->request->params["pdata"] ?? ""),
false, \INI_SCANNER_TYPED
);
$exts = $this->app->request->params["exts"] ?? [];
if (!is_array($exts)) $exts = [];
$exts = array_filter(
array_map([$this->app->extensions, "get"], $exts)
);
$cdirs = $this->app->request->params["cdirs"] ?? [];
if (!is_array($cdirs)) $cdirs = [];
$files = explode("\n", strval(
$this->app->request->params["files"] ?? ""
));
$dscripts = boolval($this->app->request->params["dscripts"] ?? "0");
$dplugins = boolval($this->app->request->params["dplugins"] ?? "0");
$ppath = $this->app->cds->packager->buildPackage(
$package, ["version" => $version],
$pdata, $exts, $cdirs, $files, $dscripts, $dplugins
);
$arclink = URIUtils::iuri("/backend", [
"route" => "cds_packager",
"mode" => "archive",
"package" => $package
]);
$this->app->page->data["messages"]["success"] = [
"color" => "success",
"content" => $this->app->i18n->translate("{%CDS_PACKAGE_SUCCESS}", $ppath, $arclink)
];
break;
}
case "archive": {
$path = $this->app->cds->packager->targzPackage($package);
if (!file_exists($path)) {
$this->app->data->messages["nofile"] = [
"color" => "danger",
"content" => $this->app->translate("{%FILE_DOES_NOT_EXIST}")
];
break;
}
$this->app->page->stopBuffering(false);
$this->app->page->template->setLayout("api", "default");
header("Content-Type: application/x-compressed-tar");
header("Content-Length: " . filesize($path));
header("Content-Disposition: attachment; filename=\"" . basename($path) . "\"");
header("Cache-Control: must-revalidate");
header("Expires: 0");
readfile($path);
$this->app->page->actionFinished(false);
$this->app->cds->disableLogPrint();
return;
}
}
$dirs = [["/user/etc", "etc"]];
foreach (ApplicationConfig::get("crispage.paths.asset") as $path)
$dirs[] = [$path, "asset"];
foreach (ApplicationConfig::get("crispage.paths.action") as $path)
$dirs[] = [$path, "action"];
foreach (ApplicationConfig::get("crispage.paths.component") as $path)
$dirs[] = [$path, "component"];
foreach (ApplicationConfig::get("crispage.paths.plugin") as $path)
$dirs[] = [$path, "plugin"];
foreach (ApplicationConfig::get("crispage.paths.template") as $path)
$dirs[] = [$path, "template"];
foreach (ApplicationConfig::get("crispage.paths.translation") as $path)
$dirs[] = [$path, "translation"];
$com_main = $this->app->page->createComponent(
"\\Crispage\\DevSuite\\PackagerComponent",
[
"extensions" => $this->app->cds->getExtensions(),
"codedirs" => $dirs
]
);
$this->app->page->setMainComponent($com_main);
$this->app->page->actionFinished();
}
}
?>

View File

@ -0,0 +1,114 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class ClassManagerComponent extends \Crispage\Framework\Component {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.components.classmanager",
"version" => VERSION,
"package" => "crispage.devsuite",
"cache_policy" => \Crispage\Caching\CacheEngine::POL_NOCACHE
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
?>
<form method="post" action="<?php echo WROOT; ?>/backend" class="cds_form">
<input type="hidden" name="route" value="cds_classmanager" />
<h1><?php ($this->app->i18n)("{%CDS_CLASS_MANAGER}"); ?></h1>
<h2><?php ($this->app->i18n)("{%REGISTER_CLASS}"); ?></h2>
<label for="type"><?php ($this->app->i18n)("{%TYPE}:"); ?></label>
<select name="type">
<option value="action">
<?php ($this->app->i18n)("{%ACTION}"); ?>
</option>
<option value="component">
<?php ($this->app->i18n)("{%COMPONENT}"); ?>
</option>
<option value="plugin">
<?php ($this->app->i18n)("{%PLUGIN}"); ?>
</option>
<option value="template">
<?php ($this->app->i18n)("{%TEMPLATE}"); ?>
</option>
<option value="translation">
<?php ($this->app->i18n)("{%TRANSLATION}"); ?>
</option>
</select>
<label for="classname"><?php ($this->app->i18n)("{%CLASS_NAME}:"); ?></label>
<input type="text" name="classname" />
<button type="submit" name="action" value="register">
<?php ($this->app->i18n)("{%REGISTER}"); ?>
</button>
<hr />
<h2><?php ($this->app->i18n)("{%REGISTERED_EXTENSIONS}"); ?></h2>
<table>
<thead>
<tr>
<th></th>
<th><?php ($this->app->i18n)("{%PACKAGE}") ?></th>
<th><?php ($this->app->i18n)("{%ID}") ?></th>
<th><?php ($this->app->i18n)("{%VERSION}") ?></th>
<th><?php ($this->app->i18n)("{%TYPE}") ?></th>
<th><?php ($this->app->i18n)("{%CLASS_NAME}") ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($this->data["extensions"] as $ext) { ?>
<tr>
<td><input type="checkbox" name="exts[]" value="<?php echo $ext->id; ?>" /></td>
<td><?php echo $ext->package; ?></td>
<td><?php echo $ext->id; ?></td>
<td><?php echo $ext->version; ?></td>
<td><?php echo $ext->type; ?></td>
<td><?php echo $ext->classname; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<button type="submit" name="action" value="unregister">
<?php ($this->app->i18n)("{%UNREGISTER}"); ?>
</button>
<hr />
<h2><?php ($this->app->i18n)("{%PLUGIN_MANAGEMENT}"); ?></h2>
<label for="ext">
<?php ($this->app->i18n)("{%ID}:"); ?>
</label>
<select name="ext">
<?php
foreach ($this->data["extensions"] as $ext) {
if ($ext->type != "plugin") continue;
echo "<option value=\"$ext->id\">$ext->id</option>";
}
?>
</select>
<label for="priority">
<?php ($this->app->i18n)("{%PRIORITY}:"); ?>
</label>
<input type="number" name="priority" min="-127" max="127" />
<button type="submit" name="action" value="plugin_create">
<?php ($this->app->i18n)("{%CREATE}"); ?>
</button>
<button type="submit" name="action" value="plugin_delete">
<?php ($this->app->i18n)("{%DELETE}"); ?>
</button>
<hr />
</form>
<?php
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class ConsoleComponent extends \Crispage\Framework\Component {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.components.console",
"version" => VERSION,
"package" => "crispage.devsuite",
"cache_policy" => \Crispage\Caching\CacheEngine::POL_NOCACHE
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
?>
<form method="post" action="<?php echo WROOT; ?>/backend" class="cds_form">
<h1><?php ($this->app->i18n)("{%CDS_CONSOLE}"); ?></h1>
<input type="hidden" name="route" value="cds_console" />
<label for="script"><?php ($this->app->i18n)("{%SCRIPT}:"); ?></label>
<textarea name="script" required rows="22" cols="80" ><?php echo $this->data["script"] ?? ""; ?></textarea>
<br />
<input type="submit" value="<?php ($this->app->i18n)("{%RUN}"); ?>" />
<hr />
<p><?php ($this->app->i18n)("{%OUTPUT}:"); ?></p>
<pre class="output"><?php echo $this->data["output"] ?? ""; ?></pre>
</form>
<?php
}
}
?>

View File

@ -0,0 +1,122 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
use \Crispage\Auth\CorePermissions;
class PackagerComponent extends \Crispage\Framework\Component {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.components.packager",
"version" => VERSION,
"package" => "crispage.devsuite",
"cache_policy" => \Crispage\Caching\CacheEngine::POL_NOCACHE
];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function render(): void {
?>
<form method="post" action="<?php echo WROOT; ?>/backend" class="cds_form">
<h1><?php ($this->app->i18n)("{%CDS_PACKAGER}"); ?></h1>
<input type="hidden" name="route" value="cds_packager" />
<input type="hidden" name="mode" value="build" />
<hr />
<h2><?php ($this->app->i18n)("{%PACKAGE_INFO}"); ?></h2>
<label for="package">
<?php ($this->app->i18n)("{%PACKAGE_ID}"); ?>
</label>
<input type="text" name="package" required />
<label for="version">
<?php ($this->app->i18n)("{%PACKAGE_VERSION}"); ?>
</label>
<input type="text" name="version" required />
<label for="pdata">
<?php ($this->app->i18n)("{%PACKAGE_DATA}"); ?>
</label>
<textarea name="pdata" rows="8" cols="52" required>generator = CDS <?php echo VERSION; ?></textarea>
<label for="dscripts">
<?php ($this->app->i18n)("{%COPY_DEFAULT_SCRIPTS}"); ?>
</label>
<input type="checkbox" name="dscripts" value="1" checked />
<br />
<label for="dplugins">
<?php ($this->app->i18n)("{%INSTALL_PLUGINS}"); ?>
</label>
<input type="checkbox" name="dplugins" value="1" checked />
<hr />
<h2><?php ($this->app->i18n)("{%REGISTERED_EXTENSIONS}"); ?></h2>
<table>
<thead>
<tr>
<th></th>
<th><?php ($this->app->i18n)("{%PACKAGE}") ?></th>
<th><?php ($this->app->i18n)("{%ID}") ?></th>
<th><?php ($this->app->i18n)("{%VERSION}") ?></th>
<th><?php ($this->app->i18n)("{%TYPE}") ?></th>
<th><?php ($this->app->i18n)("{%CLASS_NAME}") ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($this->data["extensions"] as $ext) { ?>
<tr>
<td><input type="checkbox" name="exts[]" value="<?php echo $ext->id; ?>" <?php if ($ext->package != "crispage.devsuite") echo "checked"; ?> /></td>
<td><?php echo $ext->package; ?></td>
<td><?php echo $ext->id; ?></td>
<td><?php echo $ext->version; ?></td>
<td><?php echo $ext->type; ?></td>
<td><?php echo $ext->classname; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<hr />
<h2><?php ($this->app->i18n)("{%CODE_DIRECTORIES}"); ?></h2>
<table>
<thead>
<tr>
<th></th>
<th><?php ($this->app->i18n)("{%PATH}") ?></th>
<th><?php ($this->app->i18n)("{%TYPE}") ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($this->data["codedirs"] as $dir) { ?>
<tr>
<td><input type="checkbox" name="cdirs[]" value="<?php echo $dir[0]; ?>" <?php if (str_starts_with($dir[0], "/user")) echo "checked"; ?> /></td>
<td><?php echo $dir[0]; ?></td>
<td><?php echo $dir[1]; ?></td>
</tr>
<?php } ?>
<tr>
<td>
<label for="files">
<?php ($this->app->i18n)("{%ADDITIONAL_FILES}"); ?>
</label>
</td>
<td>
<textarea name="files" rows="8" cols="52"></textarea>
</td>
</tr>
</tbody>
</table>
<hr />
<input type="submit" value="<?php ($this->app->i18n)("{%BUILD_PACKAGE}"); ?>" />
</form>
<?php
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace Crispage\DevSuite;
defined("ROOT") or die();
require_once ROOT . "/cds/DevSuite.php";
class CDSPlugin extends \Crispage\Framework\PluginClass {
public static function getExtensionInfo(): array {
return [
"id" => "crispage.devsuite.plugins.cdsplugin",
"version" => VERSION,
"package" => "crispage.devsuite"
];
}
public static function getPluginFields(): array {
return [];
}
public function __construct(\Crispage $app, array $data) {
parent::__construct($app, $data);
}
public function run(): void {
global $DevSuite;
$DevSuite = new DevSuite($this->app);
$DevSuite->init();
}
}
?>

81
cds/static/assets/cds.css Normal file
View File

@ -0,0 +1,81 @@
.cds_log {
background: #fec;
border: 1px inset #fd0;
width: 100%;
margin: 2px 0;
padding: 4px;
}
.cds_form {
background: #eee;
border: 1px inset #ccc;
padding: 8px;
max-width: 100%;
}
.cds_form label {
display: block;
margin-top: 4px;
}
.cds_form input, .cds_form textarea {
border: 1px inset #aaa;
margin: 4px 0;
display: block;
}
.cds_form textarea {
font-family: monospace;
}
.cds_form input[type=submit], .cds_form button {
background: #fd6;
border: 1px outset #f60;
font-size: x-large;
padding: 4px 16px;
}
.cds_form input[type=checkbox] {
display: inline;
margin-top: 4px;
}
.cds_form input[type=checkbox] + label {
display: block;
margin: -24px 16px 4px;
}
.cds_form input[type=submit]:hover, .cds_form button:hover {
background: #f60;
border: 1px inset #f60;
}
.cds_form h1 {
font-size: xx-large;
font-weight: bold;
}
.cds_form h2 {
font-size: x-large;
font-weight: medium;
}
.cds_form pre {
overflow: scroll;
max-width: 100%;
}
.cds_form input[type=text] {
min-width: 300px;
height: 24px;
}
.cds_form table {
border: 2px outset #fff;
margin: 8px 2px;
}
.cds_form th, td {
border: 2px inset #fff;
padding: 4px;
}

View File

@ -0,0 +1,64 @@
<?php
// CDS Installer
use \Crispage\Utils\FileUtils;
use \Crispage\Extensions\Extension;
$pdata = $this->getPackageInfo(PACKAGE);
$pid = $pdata["ExtensionInfo"]["id"] ?? "unknown";
$pversion = $pdata["ExtensionInfo"]["version"] ?? "0.0";
$this->log("Installing $pid v$pversion", $this::L_INFO);
$this->log("Copying files", $this::L_NORMAL);
$files = array_merge(
["/user"],
$pdata["PackageData"]["contents"] ?? []
);
foreach ($files as $path) {
$this->log("Copying $path", $this::L_DEBUG);
$from = PACKAGE_PATH . $path;
$to = ROOT . $path;
if (!file_exists($from)) {
$this->log("$path does not exist", $this::L_WARNING);
continue;
}
if (is_dir($from)) FileUtils::copyDirectory($from, $to);
else copy($from, $to);
}
$this->log("Installing extensions", $this::L_NORMAL);
$extensions = $pdata["Extensions"] ?? [];
$plugins = $pdata["PackageData"]["plugins"] ?? [];
foreach ($extensions as $extid => $data) {
$this->log("Installing $extid", $this::L_DEBUG);
$ext = new Extension(
$data["id"] ?? $extid,
$data["version"] ?? $pversion,
$data["package"] ?? $pid,
$data["classname"] ?? "unknown",
$data["type"] ?? "unknown",
$pdata["ExtensionData"][$extid] ?? []
);
$this->app->extensions->register($ext);
if (in_array($extid, $plugins)) {
$this->log("Installing $extid as plugin", $this::L_DEBUG);
$this->app->assets->create(
"\\Crispage\\Assets\\Plugin", [
"slug" => preg_replace("/[^a-z0-9]+/", "_", $extid),
"classname" => $data["classname"] ?? "\\Crispage\\Plugins\\DefaultPluginClass"
]
);
}
}
$this->log("Installation Successful!", $this::L_SUCCESS);
?>

View File

@ -0,0 +1,51 @@
<?php
// CDS Uninstaller
use \Crispage\Utils\FileUtils;
use \Crispage\Extensions\Extension;
$pdata = $this->getPackageInfo(PACKAGE);
$pid = $pdata["ExtensionInfo"]["id"] ?? "unknown";
$pversion = $pdata["ExtensionInfo"]["version"] ?? "0.0";
$this->log("Uninstalling $pid v$pversion", $this::L_INFO);
$this->log("Uninstalling extensions", $this::L_NORMAL);
$extensions = $pdata["Extensions"] ?? [];
$plugins = $pdata["PackageData"]["plugins"] ?? [];
foreach ($extensions as $extid => $data) {
$this->log("Uninstalling $extid", $this::L_DEBUG);
if (in_array($extid, $plugins)) {
$this->log("Uninstalling $extid as plugin", $this::L_DEBUG);
$asset = $this->app->assets->getBySlug(
"\\Crispage\\Assets\\Plugin",
preg_replace("/[^a-z0-9]+/", "_", $extid)
);
if ($asset) $this->app->assets->delete($asset);
}
$ext = $this->app->extensions->get($extid);
if ($ext) $this->app->extensions->unregister($ext);
}
$this->log("Uninstalling package", $this::L_DEBUG);
$this->app->extensions->unregister(PACKAGE);
$this->log("Deleting files", $this::L_NORMAL);
$files = $pdata["PackageData"]["contents"] ?? [];
foreach ($files as $path) {
$this->log("Deleting $path", $this::L_DEBUG);
$rpath = ROOT . $path;
if (is_dir($rpath)) {
FileUtils::emptyDirectory($rpath);
rmdir($rpath);
}
else unlink($rpath);
}
$this->log("Uninstallation Successful!", $this::L_SUCCESS);
?>

View File

@ -0,0 +1,34 @@
[TranslationSet]
language = en-US
set = cds
version = VERSION
package = crispage.devsuite
[Translations]
ADDITIONAL_FILES= Additional Files
ADDITIONAL_FILES_HELP=Separate with newlines, use : to specify package path
BUILD_PACKAGE = Build Package
CDS_CLASS_MANAGER=CDS Class Manager
CDS_CONSOLE = CDS Console
CDS_PACKAGER = CDS Packager
CDS_PACKAGE_SUCCESS = "Package built sucessfully: <a href='%2$s'>%1$s</a>"
CLASS_MANAGER = Class Manager
CLASS_NAME = Class name
CODE_DIRECTORIES= Code Directories
CONSOLE = Console
COPY_DEFAULT_SCRIPTS=Copy default [un]installation scripts
INSTALL_PLUGINS = Install plugins
OUTPUT = Output
PACKAGE = Package
PACKAGE_DATA = "Package data (INI)"
PACKAGE_ID = Package ID
PACKAGE_INFO = Package Info
PACKAGE_VERSION = Package version
PACKAGER = Packager
PATH = Path
REGISTER_CLASS = Register Class
REGISTERED_EXTENSIONS=Registered Extensions
RUN = Run
SCRIPT = Script
UNREGISTER = Unregister
VERSION = Version

100
config.php Normal file
View File

@ -0,0 +1,100 @@
; <?php http_response_code(403); die(); ?>
_version = "0.25"
;===============================
; General configuration
;===============================
; Your public facing domain
crispage.site_domain = "localhost"
; Superusers (these users always have all permissions)
crispage.superusers[] = "devsuite"
;===============================
; Security settings
;===============================
; Enable the API (required for some editor functions)
crispage.security.api_enabled = true
; Password hashing algorithm (PASSWORD_BCRYPT recommended)
crispage.security.password_algo = PASSWORD_BCRYPT
; Password and session token hashing costs (recommended: >=12, >=10)
crispage.security.password_opts[cost] = 13
crispage.security.token_opts[cost] = 10
; Number of bytes for tokens (recommended: 16)
crispage.security.token_bytes = 16
; Enable public registration
crispage.security.enable_registration = true
; Writable media paths (relative to the media directory)
crispage.security.media_paths[] = "/uploads"
crispage.security.media_paths[] = "/assets"
; Disallowed mime types
crispage.security.media_banned_mimes = "text/x-php"
;===============================
; Database settings
;===============================
; Database type (currently only MySQL is officially supported)
crispage.database.type = "\Crispage\Database\SQLiteDatabase"
; Database location/host (usually localhost)
crispage.database.location = "./cds.sqlite"
; Database port (usually 3306)
crispage.database.port = 3306
; Database name
crispage.database.name = ""
; Database credentials
crispage.database.user = ""
crispage.database.password = ""
; Database table prefix (should not be changed)
crispage.database.prefix = "cds_"
; Extra PDO options
;crispage.database.options[] =
;===============================
; Development settings
;===============================
crispage.dev.force_errmsgs = true
crispage.dev.disable_buffering = true
crispage.dev.modules_unsafe = true
crispage.dev.disable_caching = true
; Shown error level (recommended: E_ALL & ~E_DEPRECATED & ~E_STRICT)
crispage.dev.error_level = E_ALL
crispage.dev.smtp_debug = 0
crispage.dev.disable_exception_handling = false
crispage.dev.disable_redirect = false
crispage.dev.db_errors = true
;===============================
; Paths
;===============================
; Paths are relative to the ROOT
crispage.paths.asset[] = "/user/assets"
crispage.paths.asset[] = "/core/app/assets/class"
crispage.paths.action[] = "/cds/actions"
crispage.paths.action[] = "/user/actions"
crispage.paths.action[] = "/core/actions"
crispage.paths.component[] = "/cds/components"
crispage.paths.component[] = "/user/components"
crispage.paths.component[] = "/core/components"
crispage.paths.plugin[] = "/cds/plugins"
crispage.paths.plugin[] = "/user/plugins"
crispage.paths.plugin[] = "/core/plugins"
crispage.paths.template[] = "/user/templates"
crispage.paths.template[] = "/core/templates"
crispage.paths.translation[]= "/cds/translations"
crispage.paths.translation[]= "/user/translations"
crispage.paths.translation[]= "/core/translations"
crispage.paths.packages = "/packages"
crispage.paths.media = "/media"
crispage.paths.temp = "/temp"
; If not null, the public facing media url
crispage.paths.media_url = null
cds.package_path = "/temp/pkgbuild"
cds.default_info[id] = "cds.unknown"
cds.default_info[version] = VERSION