This commit is contained in:
2024-03-19 23:05:57 +02:00
parent 137c338719
commit 1f03eebd08
34 changed files with 557 additions and 169 deletions
+2 -1
View File
@@ -15,7 +15,8 @@
"spatie/image-optimizer": "^1.6", "spatie/image-optimizer": "^1.6",
"staudenmeir/laravel-cte": "^1.0", "staudenmeir/laravel-cte": "^1.0",
"ext-pdo": "*", "ext-pdo": "*",
"opis/json-schema": "^2.3" "opis/json-schema": "^2.3",
"symfony/yaml": "^7.0"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^1.8" "phpstan/phpstan": "^1.8"
Generated
+151 -1
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "63bda0f783c53f088a85b8b35874ff00", "content-hash": "8a508d36870e3e47f5b756acc01e1458",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -1906,6 +1906,85 @@
], ],
"time": "2023-05-23T14:45:45+00:00" "time": "2023-05-23T14:45:45+00:00"
}, },
{
"name": "symfony/polyfill-ctype",
"version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
"reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-29T20:11:03+00:00"
},
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.28.0", "version": "v1.28.0",
@@ -2306,6 +2385,77 @@
], ],
"time": "2023-05-30T17:17:10+00:00" "time": "2023-05-30T17:17:10+00:00"
}, },
{
"name": "symfony/yaml",
"version": "v7.0.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "2d4fca631c00700597e9442a0b2451ce234513d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/2d4fca631c00700597e9442a0b2451ce234513d3",
"reference": "2d4fca631c00700597e9442a0b2451ce234513d3",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<6.4"
},
"require-dev": {
"symfony/console": "^6.4|^7.0"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v7.0.3"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-01-23T15:02:46+00:00"
},
{ {
"name": "voku/portable-ascii", "name": "voku/portable-ascii",
"version": "2.0.1", "version": "2.0.1",
+2 -2
View File
@@ -23,7 +23,7 @@
export let data; export let data;
// export let layout; // export let layout;
export let channel; export let channel;
export let sidebar;
export let axios; export let axios;
export let readableSchemas; export let readableSchemas;
@@ -35,7 +35,7 @@
</script> </script>
<Navbar schema={data.schema}/> <Navbar {sidebar}/>
<svelte:component this={components[view]} {title} {...data}/> <svelte:component this={components[view]} {title} {...data}/>
+10 -89
View File
@@ -1,26 +1,11 @@
<script> <script>
import Avatar from "./account/Avatar.svelte"; import Avatar from "./account/Avatar.svelte";
import NavbarMenu from "./NavbarMenu.svelte";
import {getContext} from "svelte"; import {getContext} from "svelte";
export let schema; export let sidebar;
const channel = getContext("channel"); const channel = getContext("channel");
const readableSchemas = getContext("readableSchemas");
const user = getContext("user"); const user = getContext("user");
let contentIsOpen = false; let contentIsOpen = false;
const fileSchemas = readableSchemas.filter((sc) => sc.type === "files");
const otherSchemas = readableSchemas.filter((sc) => !sc.isEntry && sc.type === "collection");
let filesIsActive = false;
let otherIsActive = false;
if(schema){
filesIsActive = fileSchemas.filter(s => s.name === schema.name).length > 0;
otherIsActive = otherSchemas.filter(s => s.name === schema.name).length > 0;
}
</script> </script>
<nav class="lx-nav"> <nav class="lx-nav">
@@ -29,12 +14,7 @@
<button on:click={(e) => contentIsOpen = true} class="btn btn-primary btn-sm d-xxl-none">« Content</button> <button on:click={(e) => contentIsOpen = true} class="btn btn-primary btn-sm d-xxl-none">« Content</button>
</div> </div>
<div class="d-flex align-items-center "> <div class="d-flex align-items-center ">
<a class="nav-item" href="{channel.lucentUrl}">{channel.name}</a>
<a class="nav-item" href="{channel.lucentUrl}/members">Members</a>
{#if channel.generateCommand}
<a href="{channel.lucentUrl}/build-report" class="btn btn-outline-primary btn-sm d-">Build website</a>
{/if}
<!-- <div>--> <!-- <div>-->
<!-- <form method="GET">--> <!-- <form method="GET">-->
<!-- <input type="search" name="filter[search_regex]" placeholder="Search"--> <!-- <input type="search" name="filter[search_regex]" placeholder="Search"-->
@@ -42,7 +22,12 @@
<!-- </form>--> <!-- </form>-->
<!-- </div>--> <!-- </div>-->
</div> </div>
<div> <div class="d-flex align-items-center ">
<a class="nav-item" href="{channel.lucentUrl}/members">Members</a>
{#if channel.generateCommand}
<a href="{channel.lucentUrl}/build-report" class="btn btn-outline-primary btn-sm d-">Build website</a>
{/if}
<a class="nav-item" href="{channel.lucentUrl}/profile"> <a class="nav-item" href="{channel.lucentUrl}/profile">
<Avatar side="28" name={user.name}/> <Avatar side="28" name={user.name}/>
</a> </a>
@@ -51,76 +36,12 @@
</nav> </nav>
<div class="offcanvas offcanvas-start d-xxl-block show border-0 bg-light-subtle" class:d-none={!contentIsOpen} <div class="offcanvas offcanvas-start d-xxl-block show border-0 bg-primary-subtle " class:d-none={!contentIsOpen}
style="padding-top:36px " data-bs-scroll="true" data-bs-scroll="true"
data-bs-backdrop="false" data-bs-backdrop="false"
tabindex="-1" aria-labelledby="offcanvasScrollingLabel"> tabindex="-1" aria-labelledby="offcanvasScrollingLabel">
<!-- <div class="offcanvas-header">-->
<!-- <h5 class="offcanvas-title" id="offcanvasScrollingLabel">Content</h5>-->
<!-- </div>-->
<div class="offcanvas-body"> <div class="offcanvas-body">
<button on:click={(e) => contentIsOpen = false} class="btn btn-primary btn-sm d-xxl-none mb-4">« close</button> <button on:click={(e) => contentIsOpen = false} class="btn btn-primary btn-sm d-xxl-none mb-4">« close</button>
<div class="accordion"> {@html sidebar}
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingMain">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseMain" aria-expanded="true"
aria-controls="panelsStayOpen-collapseMain">
Main
</button>
</h2>
<div id="panelsStayOpen-collapseMain" class="accordion-collapse collapse show"
aria-labelledby="panelsStayOpen-headingMain">
<div class="accordion-body">
<NavbarMenu
schemas={ readableSchemas.filter((sc) => sc.isEntry)}
schema={schema}
/>
</div>
</div>
</div>
{#if otherSchemas.length > 0}
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingOther">
<button class="accordion-button" class:collapsed={!otherIsActive} type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseOther" aria-expanded={otherIsActive}
aria-controls="panelsStayOpen-collapseOther">
Other
</button>
</h2>
<div id="panelsStayOpen-collapseOther" class="accordion-collapse collapse"
class:show={otherIsActive}
aria-labelledby="panelsStayOpen-headingOther">
<div class="accordion-body">
<NavbarMenu
schemas={ otherSchemas}
schema={schema}
/>
</div>
</div>
</div>
{/if}
{#if fileSchemas.length > 0}
<div class="accordion-item">
<h2 class="accordion-header" id="panelsStayOpen-headingFS">
<button class="accordion-button " class:collapsed={!filesIsActive} type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseFS" aria-expanded={filesIsActive}
aria-controls="panelsStayOpen-collapseFS">
Filesystem
</button>
</h2>
<div id="panelsStayOpen-collapseFS" class="accordion-collapse collapse" class:show={filesIsActive}
aria-labelledby="panelsStayOpen-headingFS">
<div class="accordion-body">
<NavbarMenu
schemas={ fileSchemas}
schema={schema}
/>
</div>
</div>
</div>
{/if}
</div>
</div> </div>
</div> </div>
-16
View File
@@ -1,16 +0,0 @@
<script>
import {getContext} from "svelte";
const channel = getContext("channel");
export let schemas;
export let schema;
</script>
<div class="list-group list-group-flush">
{#each schemas as aschema}
<a class="list-group-item list-group-item-action" class:active={aschema.name === schema?.name}
aria-current="page"
href="{channel.lucentUrl}/content/{aschema.name}">{aschema.label}</a>
{/each}
</div>
+1 -1
View File
@@ -47,7 +47,7 @@
</script> </script>
<div class="wrapper-large transparent "> <div class="wrapper-large transparent ">
<div class="lx-card mb-4 {inModal ? 'mt-0' : 'mt-5'}"> <div class="lx-card mb-4 mt-0">
<h3 class="header-normal mb-5 "> <h3 class="header-normal mb-5 ">
{schema.label} {schema.label}
</h3> </h3>
+1 -1
View File
@@ -23,7 +23,7 @@
<div class="wrapper-normal transparent"> <div class="wrapper-normal transparent">
<h3 class="header-small mb-4 mt-5">Latest Content changes</h3> <h3 class="header-small mb-4 ">Latest Content changes</h3>
{#if records.length > 0} {#if records.length > 0}
<div class="lx-card mb-4"> <div class="lx-card mb-4">
<div class="lx-table p-0"> <div class="lx-table p-0">
+64 -6
View File
@@ -1,16 +1,74 @@
.lx-nav{ .lx-nav {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
background-color: rgba(255,255,255,1); //background-color: rgba(255,255,255,1);
margin-bottom:0px ; margin-bottom: 0px;
.nav-item{
padding:16px 0; .nav-item {
padding: 16px 0;
margin: 0 16px; margin: 0 16px;
color: $primary; color: $primary;
} }
a{
a {
text-decoration: none; text-decoration: none;
} }
}
.accordion {
border: none;
}
.accordion-item {
border: none;
background: transparent;
}
.accordion-button:not(.collapsed) {
box-shadow: none;
}
.offcanvas-body {
padding: 0;
overflow: hidden;
}
.accordion-button:not(.collapsed) {
background: rgba(11, 93, 30, 0.2) !important;
font-weight: bold;
}
.accordion .list-group-item {
margin-left: 20px;
border-left: 2px solid rgba(11, 93, 30, 0.2);
border-top: none;
border-bottom: none;
}
.accordion .list-group-item:hover {
background: rgba(11, 93, 30, 0.1) !important;
}
.accordion .list-group-item.active {
border-left: 2px solid rgba(11, 93, 30, 0.8);
border-top: none;
border-bottom: none;
background: rgba(11, 93, 30, 0.1) !important;
color: var(--bs-primary);
}
.sidebar-logo{
padding: 5px 20px;
border-bottom: 1px solid rgba(11, 93, 30, 0.2);
}
.sidebar-logo a{
text-decoration: none;
font-size: 25px;
color: var(--bs-primary);
} }
+3 -2
View File
@@ -3,8 +3,9 @@
return [ return [
"env" => env("LUCENT_ENV", "production"), "env" => env("LUCENT_ENV", "production"),
"schemas_path" => env("LUCENT_SCHEMAS_PATH", "app/Lucent"), "schemas_path" => env("LUCENT_SCHEMAS_PATH", "app/Lucent"),
"json_schemas_path" => env("LUCENT_JSON_SCHEMA_PATH", "json_schemas"), "json_schemas_path" => env("LUCENT_JSON_SCHEMA_PATH", "json_schema"),
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION',"sqlite")), "sidebar_path" => env("LUCENT_SIDEBAR_PATH", "app/Lucent"),
"database" => env('LUCENT_DB_CONNECTION', env('DB_CONNECTION', "sqlite")),
"name" => env("LUCENT_NAME", "Lucent"), "name" => env("LUCENT_NAME", "Lucent"),
"url" => env("LUCENT_URL", env('APP_URL')), "url" => env("LUCENT_URL", env('APP_URL')),
"previewTarget" => env("LUCENT_PREVIEW_TARGET", "previewTarget"), "previewTarget" => env("LUCENT_PREVIEW_TARGET", "previewTarget"),
@@ -37,7 +37,6 @@ class SchemaController extends Controller
name: $request->input("name"), name: $request->input("name"),
label: $request->input("label"), label: $request->input("label"),
type: $request->input("type"), type: $request->input("type"),
isEntry: $request->input("isEntry"),
revisionRetentionDays: $request->input("revisionRetentionDays"), revisionRetentionDays: $request->input("revisionRetentionDays"),
revisionRetentionNumber: $request->input("revisionRetentionNumber"), revisionRetentionNumber: $request->input("revisionRetentionNumber"),
trashedRetentionDays: $request->input("trashedRetentionDays"), trashedRetentionDays: $request->input("trashedRetentionDays"),
@@ -34,7 +34,7 @@ class GenerateJsonSchema extends Command
$definitions = $this->channelService->channel->schemas->whereIn("type", [Type::COLLECTION, Type::FILES])->values()->map(fn(Schema $schema) => $this->fromSchema($schema)); $definitions = $this->channelService->channel->schemas->whereIn("type", [Type::COLLECTION, Type::FILES])->values()->map(fn(Schema $schema) => $this->fromSchema($schema));
$definitions->map(function (Definition $definition) use ($definitions) { $definitions->map(function (Definition $definition) use ($definitions) {
$this->writeToFile($definition,$definitions); $this->writeToFile($definition, $definitions);
}); });
} }
@@ -58,17 +58,17 @@ class GenerateJsonSchema extends Command
mkdir($json_schema_dir); mkdir($json_schema_dir);
} }
$nameAr = explode("/", $definition->_id); $nameAr = explode("/", $definition->_id);
$name = end($nameAr); $nameEtx = end($nameAr);
$name = str_replace(".schema.json", "", $nameEtx);
$defArray = $definition->toArray(); $defArray = $definition->toArray();
$defArray['$defs'] = $definitions->map(fn($def) => $def->toArray())->keyBy(function ($def){ $defArray['$defs'] = $definitions->map(fn($def) => $def->toArray())->keyBy(function ($def) {
$nameAr = explode("/", $def['$id']); $nameAr = explode("/", $def['$id']);
return str_replace(".json","",end($nameAr)); return str_replace(".schema.json", "", end($nameAr));
}); });
unset($defArray['$defs'][str_replace(".json","",$name)]); unset($defArray['$defs'][$name]);
file_put_contents($json_schema_dir . "/" . $nameEtx, json_encode($defArray, JSON_UNESCAPED_SLASHES));
file_put_contents($json_schema_dir . "/" . $name,json_encode($defArray,JSON_UNESCAPED_SLASHES));
} }
+5 -5
View File
@@ -46,7 +46,7 @@ class Definition
private static function getBasePath(string $schemaName): string private static function getBasePath(string $schemaName): string
{ {
return config("lucent.url") . "/" . config("lucent.json_schemas_path") . "/" . $schemaName . ".json"; return config("lucent.url") . "/" . config("lucent.json_schemas_path") . "/" . $schemaName . ".schema.json";
} }
private static function getRefPath(string $schemaName): string private static function getRefPath(string $schemaName): string
@@ -81,22 +81,22 @@ class Definition
if ($property->maxItems->getOrElse(null) === 1) { if ($property->maxItems->getOrElse(null) === 1) {
$newProperty["type"] = "object"; $newProperty["type"] = "object";
if ($property->_ref->count() == 1) { if ($property->_ref->count() == 1) {
$newProperty['$ref'] = $property->_ref->map(fn($ref)=>self::getRefPath($ref))->first(); $newProperty['$ref'] = $property->_ref->map(fn($ref)=>self::getBasePath($ref))->first();
} else { } else {
$newProperty["oneOf"] = $property->_ref->map(function (string $ref) { $newProperty["oneOf"] = $property->_ref->map(function (string $ref) {
return [ return [
'$ref' => self::getRefPath($ref) '$ref' => self::getBasePath($ref)
]; ];
}); });
} }
} else { } else {
$newProperty["type"] = "array"; $newProperty["type"] = "array";
if ($property->_ref->count() == 1) { if ($property->_ref->count() == 1) {
$newProperty["items"]['$ref'] = $property->_ref->map(fn($ref)=>self::getRefPath($ref))->first(); $newProperty["items"]['$ref'] = $property->_ref->map(fn($ref)=>self::getBasePath($ref))->first();
} else { } else {
$newProperty["items"]["anyOf"] = $property->_ref->map(function (string $ref) { $newProperty["items"]["anyOf"] = $property->_ref->map(function (string $ref) {
return [ return [
'$ref' => self::getRefPath($ref) '$ref' => self::getBasePath($ref)
]; ];
}); });
} }
+1 -1
View File
@@ -7,7 +7,6 @@ use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Intervention\Image\ImageManager; use Intervention\Image\ImageManager;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Commands\CompileSchemas;
use Lucent\Commands\LiveLink; use Lucent\Commands\LiveLink;
use Lucent\Commands\RebuildThumbnails; use Lucent\Commands\RebuildThumbnails;
use Lucent\Commands\RemoveOrphanEdges; use Lucent\Commands\RemoveOrphanEdges;
@@ -17,6 +16,7 @@ use Lucent\JsonSchema\Command\GenerateJsonSchema;
use Lucent\Query\DatabaseGraph\DatabaseGraph; use Lucent\Query\DatabaseGraph\DatabaseGraph;
use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph; use Lucent\Query\DatabaseGraph\PgsqlDatabaseGraph;
use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph; use Lucent\Query\DatabaseGraph\SqliteDatabaseGraph;
use Lucent\Schema\Commands\CompileSchemas;
class LucentServiceProvider extends ServiceProvider class LucentServiceProvider extends ServiceProvider
{ {
+16 -8
View File
@@ -2,10 +2,16 @@
namespace Lucent\Schema\BlockUi; namespace Lucent\Schema\BlockUi;
use Lucent\JsonSchema\Property\Property;
use Lucent\JsonSchema\Property\RefProperty;
use Lucent\JsonSchema\Property\TypeProperty;
use Lucent\Primitive\Collection;
use Lucent\Schema\FieldInfo; use Lucent\Schema\FieldInfo;
use Lucent\Schema\FieldInterface; use Lucent\Schema\FieldInterface;
use Lucent\Schema\FieldType; use Lucent\Schema\FieldType;
use Lucent\Schema\Type;
use Lucent\Schema\Validator\MinMaxInterface; use Lucent\Schema\Validator\MinMaxInterface;
use PhpOption\Option;
class File implements FieldInterface, MinMaxInterface class File implements FieldInterface, MinMaxInterface
{ {
@@ -19,9 +25,11 @@ class File implements FieldInterface, MinMaxInterface
public string $name, public string $name,
public string $label, public string $label,
public string $mime = "", public string $mime = "",
public string $help = "",
public ?int $min = null, public ?int $min = null,
public ?int $max = null, public ?int $max = null,
public array $collections = [], public array $collections = [],
public string $group = "",
) )
{ {
$this->info = new FieldInfo("file", "File", FieldType::FILE); $this->info = new FieldInfo("file", "File", FieldType::FILE);
@@ -50,20 +58,20 @@ class File implements FieldInterface, MinMaxInterface
return count($value) < $this->min; return count($value) < $this->min;
} }
public function isRequired(): bool
{
return false;
}
public function toJsonSchema(): Property public function toJsonSchema(): Property
{ {
return new TypeProperty( return new RefProperty(
type: PropertyType::STRING,
id: $this->name, id: $this->name,
title: Option::fromValue($this->label), title: Option::fromValue($this->label),
description: Option::fromValue($this->help, ""), description: Option::fromValue($this->help, ""),
default: Option::fromValue($this->default, ""), _ref: new Collection($this->collections),
minLength: Option::fromValue($this->min), minItems: Option::fromValue($this->min),
maxLength: Option::fromValue($this->max), maxItems: Option::fromValue($this->max),
readOnly: Option::fromValue($this->readonly, false),
enum: Option::fromValue($this->selectOptions),
comment: Option::fromValue($this->group, ""), comment: Option::fromValue($this->group, ""),
format: none(),
); );
} }
+4
View File
@@ -29,5 +29,9 @@ class Heading implements FieldInterface
return $output; return $output;
} }
public function isRequired(): bool
{
return false;
}
} }
+4 -1
View File
@@ -28,6 +28,9 @@ class Markdown implements FieldInterface
$output[$this->name] = $input[$this->name] ?? ""; $output[$this->name] = $input[$this->name] ?? "";
return $output; return $output;
} }
public function isRequired(): bool
{
return false;
}
} }
+4 -1
View File
@@ -49,6 +49,9 @@ class Reference implements FieldInterface, MinMaxInterface
return count($value) < $this->min; return count($value) < $this->min;
} }
public function isRequired(): bool
{
return false;
}
} }
+4 -1
View File
@@ -28,6 +28,9 @@ class Rich implements FieldInterface
$output[$this->name] = $input[$this->name] ?? ""; $output[$this->name] = $input[$this->name] ?? "";
return $output; return $output;
} }
public function isRequired(): bool
{
return false;
}
} }
+4 -1
View File
@@ -28,6 +28,9 @@ class Textarea implements FieldInterface
$output[$this->name] = $input[$this->name] ?? ""; $output[$this->name] = $input[$this->name] ?? "";
return $output; return $output;
} }
public function isRequired(): bool
{
return false;
}
} }
-1
View File
@@ -18,7 +18,6 @@ class CollectionSchema implements Schema
public array $visible, public array $visible,
public array $groups, public array $groups,
public Collection $fields, public Collection $fields,
public bool $isEntry = false,
public string $color = "", public string $color = "",
public string $sortBy = "-_sys.updatedAt", public string $sortBy = "-_sys.updatedAt",
public string $titleTemplate = "", public string $titleTemplate = "",
@@ -1,12 +1,13 @@
<?php <?php
namespace Lucent\Commands; namespace Lucent\Schema\Commands;
use DirectoryIterator; use DirectoryIterator;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Lucent\Schema\Schema; use Lucent\Schema\Schema;
use Lucent\Schema\SchemaService; use Lucent\Schema\SchemaService;
use Lucent\Schema\Type; use Lucent\Schema\Type;
use function Lucent\Commands\base_path;
class CompileSchemas extends Command class CompileSchemas extends Command
{ {
-1
View File
@@ -11,5 +11,4 @@ enum FieldType: string
case FILE = 'file'; case FILE = 'file';
case JSON = 'json'; case JSON = 'json';
case REFERENCE = 'reference'; case REFERENCE = 'reference';
case TAB = 'tab';
} }
-1
View File
@@ -19,7 +19,6 @@ class FilesSchema implements Schema
public Collection $fields, public Collection $fields,
public string $path, public string $path,
public array $groups, public array $groups,
public bool $isEntry = false,
public string $sortBy = "-_sys.updatedAt", public string $sortBy = "-_sys.updatedAt",
public string $color = "", public string $color = "",
public string $titleTemplate = "", public string $titleTemplate = "",
-2
View File
@@ -22,7 +22,6 @@ class SchemaService
visible: $schemaArr["visible"] ?? [], visible: $schemaArr["visible"] ?? [],
groups: $schemaArr["groups"] ?? [], groups: $schemaArr["groups"] ?? [],
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']), fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
isEntry: $schemaArr["isEntry"] ?? false,
color: $schemaArr["color"] ?? "", color: $schemaArr["color"] ?? "",
sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt", sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt",
titleTemplate: $schemaArr["titleTemplate"] ?? "", titleTemplate: $schemaArr["titleTemplate"] ?? "",
@@ -36,7 +35,6 @@ class SchemaService
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']), fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
path: $schemaArr["path"] ?? $schemaArr["name"], path: $schemaArr["path"] ?? $schemaArr["name"],
groups: $schemaArr["groups"] ?? [], groups: $schemaArr["groups"] ?? [],
isEntry: $schemaArr["isEntry"] ?? false,
sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt", sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt",
color: $schemaArr["color"] ?? "", color: $schemaArr["color"] ?? "",
titleTemplate: $schemaArr["titleTemplate"] ?? "", titleTemplate: $schemaArr["titleTemplate"] ?? "",
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace Lucent\Schema\Sidebar\Item;
abstract class Item
{
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Lucent\Schema\Sidebar\Item;
class LinkItem extends Item
{
public function __construct(
public string $label,
public string $href,
)
{
}
public static function fromArray(array $data): self
{
return new self(
label: $data["label"],
href: $data["href"],
);
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Lucent\Schema\Sidebar\Item;
class SchemaItem extends Item
{
public function __construct(
public string $label,
public string $schema,
)
{
}
public static function fromArray(array $data): self
{
return new self(
label: $data["label"],
schema: $data["schema"],
);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Lucent\Schema\Sidebar;
use Lucent\Primitive\Collection;
use Lucent\Schema\Sidebar\Item\Item;
use Lucent\Schema\Sidebar\Item\LinkItem;
use Lucent\Schema\Sidebar\Item\SchemaItem;
class Section
{
/**
* @param Collection<Item> $items
*/
public function __construct(
public string $label,
public Collection $items
)
{
}
public static function fromArray(array $data): self
{
return new self(
label: $data["label"],
items: (new Collection($data["items"]))->map(fn($itemData) => isset($itemData["schema"]) ? SchemaItem::fromArray($itemData) : LinkItem::fromArray($itemData))
);
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Lucent\Schema\Sidebar;
use Lucent\Primitive\Collection;
class Sidebar
{
/**
* @param Collection<Section> $sections
*/
public function __construct(
public Collection $sections
)
{
}
public static function fromArray(array $data): self
{
return new self((new Collection($data))->map(fn($sectionData) => Section::fromArray($sectionData)));
}
public function getUsedSchemas(): array
{
return $this->sections->reduce(function (array $carry, Section $section) {
return array_merge($carry, $section->items->pluck("schema")->toArray());
}, []);
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Lucent\Schema\Sidebar;
use Lucent\Channel\Channel;
use PhpOption\Option;
use Symfony\Component\Yaml\Yaml;
class SidebarService
{
public function __construct()
{
}
/**
* @param Channel $channel
* @param array<string> $readableSchemas
* @param string $currentSchema
* @return string
*/
public function render(Channel $channel, array $readableSchemas, string $currentSchema): string
{
$schemas = $channel->schemas->whereIn("name", $readableSchemas);
return view('lucent::sidebar.sidebar', [
'sidebar' => $this->fromConfig(),
'schemas' => $schemas,
'channel' => $channel,
'currentSchema' => $currentSchema,
'lucentUrl' => $channel->lucentUrl,
])->render();
}
/**
* @return Option<Sidebar>
*/
private function fromConfig(): Option
{
$configPath = base_path(config("lucent.sidebar_path")) . "/sidebar.yaml";
if (!file_exists($configPath)) {
return none();
}
$yaml = Yaml::parse(file_get_contents($configPath));
if (empty($yaml)) {
return none();
}
$sidebar = Sidebar::fromArray($yaml);
return some($sidebar);
}
}
-1
View File
@@ -22,7 +22,6 @@ class Reference implements FieldInterface, MinMaxInterface
public function __construct( public function __construct(
public string $name, public string $name,
public string $label, public string $label,
public string $mime = "",
public string $help = "", public string $help = "",
public ?int $min = null, public ?int $min = null,
public ?int $max = null, public ?int $max = null,
+6 -3
View File
@@ -6,27 +6,30 @@ use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Lucent\Account\AccountService; use Lucent\Account\AccountService;
use Lucent\Channel\ChannelService; use Lucent\Channel\ChannelService;
use Lucent\Schema\Sidebar\SidebarService;
class Svelte class Svelte
{ {
public function __construct( public function __construct(
public ChannelService $channelService, public ChannelService $channelService,
public AccountService $accountService public AccountService $accountService,
public SidebarService $sidebarService
) )
{ {
} }
function render(string $layout, string $view, string $title = "", mixed $data = []): View|Factory function render(string $layout, string $view, string $title = "", mixed $data = []): View|Factory
{ {
$readableSchemas = $this->accountService->currentReadableSchemas();
$context = []; $context = [];
$context["user"] = session('user'); $context["user"] = session('user');
$context["view"] = $view; $context["view"] = $view;
$context["layout"] = $layout; $context["layout"] = $layout;
$context["sidebar"] = $this->sidebarService->render($this->channelService->channel,$readableSchemas, $data["schema"]?->name ?? "");
$context["title"] = $title; $context["title"] = $title;
$context["data"] = $data; $context["data"] = $data;
$context["channel"] = $this->channelService->channel; $context["channel"] = $this->channelService->channel;
$context["readableSchemas"] = $this->accountService->currentReadableSchemas(); $context["readableSchemas"] = $readableSchemas;
$json = json_encode($context); $json = json_encode($context);
$divTag = sprintf('<div class="lucent-component" data-layout="%s"></div>', $layout); $divTag = sprintf('<div class="lucent-component" data-layout="%s"></div>', $layout);
@@ -0,0 +1,33 @@
<div class="accordion-item">
@php
$show = $schemas->where("name", $currentSchema)->isNotEmpty() || $main;
@endphp
<h2 class="accordion-header" id="panelsStayOpen-headingMain">
<button class="accordion-button bg-primary-subtle {{$show ? "" : "collapsed"}}" type="button"
data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapseMain" aria-expanded="{{$show}}"
aria-controls="panelsStayOpen-collapseMain">
@if($main)
Content
@else
Other
@endif
</button>
</h2>
<div id="panelsStayOpen-collapseMain" class="accordion-collapse collapse {{$show ? "show" : ""}}"
aria-labelledby="panelsStayOpen-headingMain"
data-bs-parent="#main-sidebar">
<div class="list-group list-group-flush">
@foreach($schemas as $schema)
<a class="list-group-item list-group-item-action bg-primary-subtle {{ $schema->name == $currentSchema ? "active" : ""}}"
aria-current="page"
href="{{$lucentUrl}}/content/{{$schema->name}}">{{$schema->label}}</a>
@endforeach
</div>
</div>
</div>
+52
View File
@@ -0,0 +1,52 @@
<div class="sidebar-logo">
<a href="{{$channel->lucentUrl}}">{{$channel->name}}</a>
</div>
<div class="accordion" id="main-sidebar">
@if($sidebar->isEmpty())
@include("lucent::sidebar.accordion-item", ["schemas" => $schemas, "currentSchema" => $currentSchema, "lucentUrl" => $lucentUrl, "main" => true])
@else
@foreach($sidebar->get()->sections as $index => $section)
@php
$show = $section->items->where("schema", $currentSchema)->isNotEmpty();
@endphp
<div class="accordion-item ">
<h2 class="accordion-header" id="panelsStayOpen-heading-{{$index}}">
<button class="accordion-button rounded-0 bg-primary-subtle {{$show ? "" : "collapsed"}}"
type="button" data-bs-toggle="collapse"
data-bs-target="#panelsStayOpen-collapse-{{$index}}" aria-expanded="{{$show}}"
aria-controls="panelsStayOpen-collapse-{{$index}}">
{{$section->label}}
</button>
</h2>
<div id="panelsStayOpen-collapse-{{$index}}"
class="accordion-collapse collapse {{$show ? "show" : ""}}"
aria-labelledby="panelsStayOpen-heading-{{$index}}"
data-bs-parent="#main-sidebar">
<div class="list-group list-group-flush bg-primary-subtle">
@foreach($section->items as $item)
@if(isset($item->href))
<a class="list-group-item list-group-item-action bg-primary-subtle"
aria-current="page"
href="{{$item->href}}"
target="_blank"
>{{$item->label}}</a>
@else
@php
$schema = $schemas->where("name",$item->schema)->first();
@endphp
<a class="list-group-item list-group-item-action bg-primary-subtle {{ $schema->name == $currentSchema ? "active" : ""}}"
aria-current="page"
href="{{$lucentUrl}}/content/{{$schema->name}}">{{$item->label}}</a>
@endif
@endforeach
</div>
</div>
</div>
@endforeach
@include("lucent::sidebar.accordion-item", ["schemas" => $schemas->whereNotIn("name",$sidebar->get()->getUsedSchemas()), "currentSchema" => $currentSchema, "lucentUrl" => $lucentUrl, "main" => false])
@endif
</div>