commit b6387dfc0f69fa7f3df0723e98a52098de82d9a6 Author: crispycat Date: Mon May 27 00:44:14 2024 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..495e59a --- /dev/null +++ b/.gitignore @@ -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 diff --git a/_crispage/version b/_crispage/version new file mode 100644 index 0000000..575adda --- /dev/null +++ b/_crispage/version @@ -0,0 +1 @@ +0.25.1a \ No newline at end of file diff --git a/_scripts/pull_crispage.sh b/_scripts/pull_crispage.sh new file mode 100755 index 0000000..aa4dd7c --- /dev/null +++ b/_scripts/pull_crispage.sh @@ -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"; diff --git a/cds.sqlite b/cds.sqlite new file mode 100644 index 0000000..f8236c4 Binary files /dev/null and b/cds.sqlite differ diff --git a/cds/CDSPackager.php b/cds/CDSPackager.php new file mode 100644 index 0000000..ad1672a --- /dev/null +++ b/cds/CDSPackager.php @@ -0,0 +1,170 @@ +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; + } + } + } +?> diff --git a/cds/DevSuite.php b/cds/DevSuite.php new file mode 100644 index 0000000..2eaf4d4 --- /dev/null +++ b/cds/DevSuite.php @@ -0,0 +1,268 @@ + "\\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 "
";
+			foreach ($this->logs as $log)
+				printf("[%.6f] %s\n", $log[1] - START_TIME, $log[0]);
+			echo "
"; + } + + 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"); + } + } +?> diff --git a/cds/IniWriter.php b/cds/IniWriter.php new file mode 100644 index 0000000..cf304db --- /dev/null +++ b/cds/IniWriter.php @@ -0,0 +1,56 @@ + $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); + } + } +?> diff --git a/cds/actions/Crispage/DevSuite/ClassManagerAction.php b/cds/actions/Crispage/DevSuite/ClassManagerAction.php new file mode 100644 index 0000000..af86dcd --- /dev/null +++ b/cds/actions/Crispage/DevSuite/ClassManagerAction.php @@ -0,0 +1,98 @@ + "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(); + } + } +?> + diff --git a/cds/actions/Crispage/DevSuite/ConsoleAction.php b/cds/actions/Crispage/DevSuite/ConsoleAction.php new file mode 100644 index 0000000..57019b1 --- /dev/null +++ b/cds/actions/Crispage/DevSuite/ConsoleAction.php @@ -0,0 +1,46 @@ + "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(); + } + } +?> diff --git a/cds/actions/Crispage/DevSuite/PackagerAction.php b/cds/actions/Crispage/DevSuite/PackagerAction.php new file mode 100644 index 0000000..8150f1d --- /dev/null +++ b/cds/actions/Crispage/DevSuite/PackagerAction.php @@ -0,0 +1,133 @@ + "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(); + } + } +?> diff --git a/cds/components/Crispage/DevSuite/ClassManagerComponent.php b/cds/components/Crispage/DevSuite/ClassManagerComponent.php new file mode 100644 index 0000000..8164f21 --- /dev/null +++ b/cds/components/Crispage/DevSuite/ClassManagerComponent.php @@ -0,0 +1,114 @@ + "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 { +?> + + +
+ +

app->i18n)("{%CDS_CLASS_MANAGER}"); ?>

+ +

app->i18n)("{%REGISTER_CLASS}"); ?>

+ + + + + +
+ +

app->i18n)("{%REGISTERED_EXTENSIONS}"); ?>

+ + + + + + + + + + + + + data["extensions"] as $ext) { ?> + + + + + + + + + + +
app->i18n)("{%PACKAGE}") ?>app->i18n)("{%ID}") ?>app->i18n)("{%VERSION}") ?>app->i18n)("{%TYPE}") ?>app->i18n)("{%CLASS_NAME}") ?>
package; ?>id; ?>version; ?>type; ?>classname; ?>
+ +
+ +

app->i18n)("{%PLUGIN_MANAGEMENT}"); ?>

+ + + + + + +
+
+ + diff --git a/cds/components/Crispage/DevSuite/ConsoleComponent.php b/cds/components/Crispage/DevSuite/ConsoleComponent.php new file mode 100644 index 0000000..ca2cd7d --- /dev/null +++ b/cds/components/Crispage/DevSuite/ConsoleComponent.php @@ -0,0 +1,39 @@ + "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 { +?> +
+

app->i18n)("{%CDS_CONSOLE}"); ?>

+ + + +
+ " /> +
+

app->i18n)("{%OUTPUT}:"); ?>

+
data["output"] ?? ""; ?>
+
+ + diff --git a/cds/components/Crispage/DevSuite/PackagerComponent.php b/cds/components/Crispage/DevSuite/PackagerComponent.php new file mode 100644 index 0000000..71ecf60 --- /dev/null +++ b/cds/components/Crispage/DevSuite/PackagerComponent.php @@ -0,0 +1,122 @@ + "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 { +?> +
+

app->i18n)("{%CDS_PACKAGER}"); ?>

+ + + +
+ +

app->i18n)("{%PACKAGE_INFO}"); ?>

+ + + + + + + + +
+ + + +
+ +

app->i18n)("{%REGISTERED_EXTENSIONS}"); ?>

+ + + + + + + + + + + + + data["extensions"] as $ext) { ?> + + + + + + + + + + +
app->i18n)("{%PACKAGE}") ?>app->i18n)("{%ID}") ?>app->i18n)("{%VERSION}") ?>app->i18n)("{%TYPE}") ?>app->i18n)("{%CLASS_NAME}") ?>
package != "crispage.devsuite") echo "checked"; ?> />package; ?>id; ?>version; ?>type; ?>classname; ?>
+ +
+ +

app->i18n)("{%CODE_DIRECTORIES}"); ?>

+ + + + + + + + + + data["codedirs"] as $dir) { ?> + + + + + + + + + + + +
app->i18n)("{%PATH}") ?>app->i18n)("{%TYPE}") ?>
/>
+ + + +
+ +
+ + " /> +
+ + diff --git a/cds/plugins/Crispage/DevSuite/CDSPlugin.php b/cds/plugins/Crispage/DevSuite/CDSPlugin.php new file mode 100644 index 0000000..9b7ae8c --- /dev/null +++ b/cds/plugins/Crispage/DevSuite/CDSPlugin.php @@ -0,0 +1,31 @@ + "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(); + } + } +?> diff --git a/cds/static/assets/cds.css b/cds/static/assets/cds.css new file mode 100644 index 0000000..3071ab7 --- /dev/null +++ b/cds/static/assets/cds.css @@ -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; +} diff --git a/cds/static/defaultscripts/install.php b/cds/static/defaultscripts/install.php new file mode 100644 index 0000000..fc45ca7 --- /dev/null +++ b/cds/static/defaultscripts/install.php @@ -0,0 +1,64 @@ +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); +?> diff --git a/cds/static/defaultscripts/uninstall.php b/cds/static/defaultscripts/uninstall.php new file mode 100644 index 0000000..5e618ad --- /dev/null +++ b/cds/static/defaultscripts/uninstall.php @@ -0,0 +1,51 @@ +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); +?> diff --git a/cds/translations/devsuite/cds_en.ini b/cds/translations/devsuite/cds_en.ini new file mode 100644 index 0000000..c89d5c9 --- /dev/null +++ b/cds/translations/devsuite/cds_en.ini @@ -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: %1$s" +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 diff --git a/config.php b/config.php new file mode 100644 index 0000000..9f4eb04 --- /dev/null +++ b/config.php @@ -0,0 +1,100 @@ +; + +_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