WIP uploading files
This commit is contained in:
+39
-41
@@ -1,44 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "lexx27/lucent",
|
"name": "lexx27/lucent",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"description": "Lucent cms",
|
"description": "Lucent cms",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"ext-sqlite3": "*",
|
"ext-sqlite3": "*",
|
||||||
"ext-imagick": "*",
|
"ext-imagick": "*",
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"php": "^8.3",
|
"php": "^8.5",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
"intervention/image": "^2.7",
|
"intervention/image": "^2.7",
|
||||||
"phpoption/phpoption": "^1.9",
|
"phpoption/phpoption": "^1.9",
|
||||||
"spatie/image-optimizer": "^1.6",
|
"spatie/image-optimizer": "^1.6",
|
||||||
"staudenmeir/laravel-cte": "^1.0",
|
"staudenmeir/laravel-cte": "^1.0",
|
||||||
"mustache/mustache": "^2.14"
|
"mustache/mustache": "^2.14"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^1.8",
|
||||||
|
"laravel/framework": "^10.10"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lucent\\": "src/"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"files": [
|
||||||
"phpstan/phpstan": "^1.8",
|
"src/Response.php",
|
||||||
"laravel/framework": "^10.10"
|
"src/macros.php"
|
||||||
},
|
]
|
||||||
"autoload": {
|
},
|
||||||
"psr-4": {
|
"extra": {
|
||||||
"Lucent\\": "src/"
|
"laravel": {
|
||||||
},
|
"providers": [
|
||||||
"files": [
|
"Lucent\\LucentServiceProvider"
|
||||||
"src/Response.php",
|
]
|
||||||
"src/macros.php"
|
}
|
||||||
]
|
},
|
||||||
},
|
"minimum-stability": "stable",
|
||||||
"extra": {
|
"prefer-stable": true
|
||||||
"laravel": {
|
|
||||||
"providers": [
|
|
||||||
"Lucent\\LucentServiceProvider"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"prefer-stable": true
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import Icon from "../../common/Icon.svelte";
|
import Icon from "../../common/Icon.svelte";
|
||||||
import SortFields from "./SortFields.svelte";
|
import SortFields from "./SortFields.svelte";
|
||||||
import AppliedFilter from "./AppliedFilter.svelte";
|
import AppliedFilter from "./AppliedFilter.svelte";
|
||||||
import {createEventDispatcher, getContext} from "svelte";
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
import AppliedFilterNotLinked from "./AppliedFilterNotLinked.svelte";
|
import AppliedFilterNotLinked from "./AppliedFilterNotLinked.svelte";
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@
|
|||||||
} else {
|
} else {
|
||||||
window.location = url;
|
window.location = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadComplete(e) {
|
function uploadComplete(e) {
|
||||||
@@ -51,110 +50,94 @@
|
|||||||
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<div class="toolbar-filters">
|
<div class="toolbar-filters">
|
||||||
|
|
||||||
<SortFields
|
<SortFields
|
||||||
{schema}
|
{schema}
|
||||||
{sortParam}
|
{sortParam}
|
||||||
{sortField}
|
{sortField}
|
||||||
{systemFields}
|
{systemFields}
|
||||||
{inModal}
|
{inModal}
|
||||||
{modalUrl}
|
{modalUrl}
|
||||||
on:refresh
|
on:refresh
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<FilterFields
|
<FilterFields
|
||||||
bind:schema
|
bind:schema
|
||||||
{systemFields}
|
{systemFields}
|
||||||
{operators}
|
{operators}
|
||||||
{filter}
|
{filter}
|
||||||
{inModal}
|
{inModal}
|
||||||
{modalUrl}
|
{modalUrl}
|
||||||
on:refresh
|
on:refresh
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<form method="GET" on:submit={search}>
|
<form method="GET" on:submit={search}>
|
||||||
<input type="search" name="filter[search_regex]" placeholder="Search"
|
<input
|
||||||
class="search" required>
|
type="search"
|
||||||
|
name="filter[search_regex]"
|
||||||
|
placeholder="Search"
|
||||||
|
class="search"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display:flex;align-items: center;gap:4px">
|
<div style="display:flex;align-items: center;gap:4px">
|
||||||
{#if schema.type === "collection"}
|
{#if !inModal && isWritable}
|
||||||
{#if !inModal && isWritable}
|
<a
|
||||||
<a
|
href="{channel.lucentUrl}/records/new?schema={schema.name}"
|
||||||
href="{channel.lucentUrl}/records/new?schema={schema.name}"
|
class="button"
|
||||||
class="button"
|
>
|
||||||
>
|
New Record
|
||||||
New Record
|
</a>
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
{:else }
|
|
||||||
<div>
|
|
||||||
<Uploader {schema} on:uploadComplete={uploadComplete}/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if !inModal}
|
{#if !inModal}
|
||||||
<Dropdown orientation="right">
|
<Dropdown orientation="right">
|
||||||
<div slot="button">
|
<div slot="button">
|
||||||
<Icon icon="ellipsis-vertical"/>
|
<Icon icon="ellipsis-vertical" />
|
||||||
</div>
|
</div>
|
||||||
{#if filter["status_in"] === "trashed"}
|
{#if filter["status_in"] === "trashed"}
|
||||||
{#if isWritable}
|
{#if isWritable}
|
||||||
<a
|
<a
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
href="{channel.lucentUrl}/content/{schema.name}/emptyTrash"
|
href="{channel.lucentUrl}/content/{schema.name}/emptyTrash"
|
||||||
>
|
>
|
||||||
Empty trash
|
Empty trash
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
|
<a class="dropdown-item" href={csvUrl}>Export to CSV</a>
|
||||||
<a
|
<a
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
href={csvUrl}
|
href="{channel.lucentUrl}/content/{schema.name}?filter[status_in]=trashed"
|
||||||
>Export to CSV</a
|
>View trashed records</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="dropdown-item"
|
class="dropdown-item"
|
||||||
href="{channel.lucentUrl}/content/{schema.name}?filter[status_in]=trashed"
|
href="{channel.lucentUrl}/content/{schema.name}?notlinked=*"
|
||||||
>View trashed records</a
|
>View unlinked records</a
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="{channel.lucentUrl}/content/{schema.name}?notlinked=*"
|
|
||||||
>View unlinked records</a
|
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="applied-filters">
|
<div class="applied-filters">
|
||||||
<AppliedFilterNotLinked
|
<AppliedFilterNotLinked {inModal} {modalUrl} on:refresh
|
||||||
{inModal}
|
|
||||||
{modalUrl}
|
|
||||||
on:refresh
|
|
||||||
></AppliedFilterNotLinked>
|
></AppliedFilterNotLinked>
|
||||||
{#if Object.entries(filter).length > 0}
|
{#if Object.entries(filter).length > 0}
|
||||||
{#each Object.entries(filter) as [k, v]}
|
{#each Object.entries(filter) as [k, v]}
|
||||||
<AppliedFilter
|
<AppliedFilter
|
||||||
{schema}
|
{schema}
|
||||||
{operators}
|
{operators}
|
||||||
key={k}
|
key={k}
|
||||||
value={v}
|
value={v}
|
||||||
{inModal}
|
{inModal}
|
||||||
{modalUrl}
|
{modalUrl}
|
||||||
{graph}
|
{graph}
|
||||||
on:refresh
|
on:refresh
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<script>
|
||||||
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
|
import Icon from "../common/Icon.svelte";
|
||||||
|
import Index from "../content/Index.svelte";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
let dialogEl;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
const channel = getContext("channel");
|
||||||
|
$: data = {};
|
||||||
|
let selectedRecords = [];
|
||||||
|
// onMount(() => {
|
||||||
|
// load();
|
||||||
|
// });
|
||||||
|
|
||||||
|
export function close(e) {
|
||||||
|
if (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogEl.close();
|
||||||
|
selectedRecords = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function load(schema) {
|
||||||
|
axios
|
||||||
|
.get(channel.lucentUrl + "/content/" + schema)
|
||||||
|
.then((response) => {
|
||||||
|
data = response.data;
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
function insert(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("insert", {
|
||||||
|
records: selectedRecords,
|
||||||
|
action: "insert",
|
||||||
|
schema: data.schema.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
dispatch("insert", {
|
||||||
|
records: selectedRecords,
|
||||||
|
action: "replace",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function open(schema) {
|
||||||
|
dialogEl.showModal();
|
||||||
|
load(schema);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<dialog bind:this={dialogEl}>
|
||||||
|
{#if data.schema}
|
||||||
|
<div class="dialog-header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button"
|
||||||
|
on:click={insert}
|
||||||
|
disabled={selectedRecords.length === 0}
|
||||||
|
>
|
||||||
|
Insert
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="button"
|
||||||
|
on:click={replace}
|
||||||
|
disabled={selectedRecords.length === 0}
|
||||||
|
>
|
||||||
|
Replace
|
||||||
|
</button>
|
||||||
|
{#if selectedRecords.length > 0}
|
||||||
|
<span class="">
|
||||||
|
{selectedRecords.length} records selected
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<button
|
||||||
|
on:click|preventDefault={close}
|
||||||
|
type="button"
|
||||||
|
class="button close"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<Icon icon="close"></Icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-body">
|
||||||
|
<Index {...data} bind:selected={selectedRecords}></Index>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</dialog>
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import {createEventDispatcher, getContext} from "svelte";
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let schema;
|
export let recordId;
|
||||||
let mimeTypes = "";
|
let mimeTypes = "";
|
||||||
let files = [];
|
let files = [];
|
||||||
let isLoading = false;
|
let isLoading = false;
|
||||||
@@ -17,39 +17,43 @@
|
|||||||
files = e.target.files ? [...e.target.files] : [];
|
files = e.target.files ? [...e.target.files] : [];
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
|
|
||||||
formData.append("schema", schema.name);
|
formData.append("recordId", recordId);
|
||||||
Array.from(files).forEach(function (file) {
|
Array.from(files).forEach(function (file) {
|
||||||
formData.append("files[]", file);
|
formData.append("files[]", file);
|
||||||
});
|
});
|
||||||
dispatch("beforeUpload", files);
|
dispatch("beforeUpload", files);
|
||||||
axios
|
fetch(channel.lucentUrl + "/files/upload", {
|
||||||
.post(channel.lucentUrl + "/files/upload", formData, {
|
method: "POST",
|
||||||
headers: {
|
body: formData,
|
||||||
"Content-Type": "multipart/form-data",
|
headers: {
|
||||||
},
|
"X-CSRF-TOKEN": document.querySelector(
|
||||||
})
|
'meta[name="csrf-token"]',
|
||||||
.then((response) => {
|
).content,
|
||||||
if (response.data.error) {
|
},
|
||||||
dispatch("uploadError", response.data.error);
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (data.error) {
|
||||||
|
dispatch("uploadError", data.error);
|
||||||
} else {
|
} else {
|
||||||
dispatch("uploadComplete", response.data);
|
dispatch("uploadComplete", data);
|
||||||
}
|
}
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
console.log(error.response.data);
|
console.log(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<fieldset class="upload-button" disabled={isLoading}>
|
<fieldset class="upload-button" disabled={isLoading}>
|
||||||
<label class="button primary btn-spinner ">
|
<label class="button primary btn-spinner">
|
||||||
<span
|
<span
|
||||||
class="spinner-border spinner-border-sm"
|
class="spinner-border spinner-border-sm"
|
||||||
role="status"
|
role="status"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
Upload file
|
Upload file
|
||||||
|
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
<script>
|
<script>
|
||||||
import {sortByField} from "../../edges/sortEdges";
|
import { sortByField } from "../../edges/sortEdges";
|
||||||
import Sortable from "../../libs/Sortable.svelte";
|
import Sortable from "../../libs/Sortable.svelte";
|
||||||
import PreviewFile from "../previews/PreviewFile.svelte";
|
import PreviewFile from "../previews/PreviewFile.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
import Dialog from "../../dialog/Dialog.svelte";
|
import Dialog from "../../dialog/Dialog.svelte";
|
||||||
import {insertEdges} from "./reference.js";
|
import { insertEdges } from "./reference.js";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
import FileDialog from "../../dialog/FileDialog.svelte";
|
||||||
|
import Uploader from "../../files/Uploader.svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let field;
|
export let field;
|
||||||
export let record;
|
export let record;
|
||||||
export let graph
|
export let graph;
|
||||||
let browseModal;
|
let browseModal;
|
||||||
$: references = graph?.edges
|
|
||||||
.filter((edge) => edge.field === field.name)
|
|
||||||
.map((edge) => {
|
|
||||||
return graph.records.find((increc) => increc.id === edge.target && record.id === edge.source);
|
|
||||||
}).filter((rec) => (rec?.id ? true : false)) ?? [];
|
|
||||||
|
|
||||||
let collections = channel.schemas.filter((aschema) =>
|
|
||||||
field.collections.includes(aschema.name)
|
|
||||||
);
|
|
||||||
|
|
||||||
function removeReference(e) {
|
function removeReference(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
graph.edges = graph.edges.filter(
|
graph.edges = graph.edges.filter(
|
||||||
(edge) => !(edge.target === e.detail && edge.field === field.name)
|
(edge) => !(edge.target === e.detail && edge.field === field.name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,50 +28,56 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function reorder(e) {
|
async function reorder(e) {
|
||||||
|
graph.edges = await sortByField(
|
||||||
graph.edges = await sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references);
|
e.detail.source,
|
||||||
|
e.detail.target,
|
||||||
|
graph.edges,
|
||||||
|
field.name,
|
||||||
|
references,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert(e) {
|
function insert(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
browseModal.close();
|
browseModal.close();
|
||||||
graph = insertEdges(graph, record, e.detail.records, field.name, e.detail.action);
|
graph = insertEdges(
|
||||||
|
graph,
|
||||||
|
record,
|
||||||
|
e.detail.records,
|
||||||
|
field.name,
|
||||||
|
e.detail.action,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadComplete(e) {
|
||||||
|
records = e.detail;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mb-0">
|
<div class="mb-0">
|
||||||
{#if field.collections.length === 1}
|
<!-- <button
|
||||||
<button
|
class="button"
|
||||||
class="button"
|
on:click={(e) => openFileModal(e, collections[0].name)}
|
||||||
on:click={(e) => openBrowseModal(e, collections[0].name)}
|
>
|
||||||
>
|
Browse
|
||||||
Browse
|
</button> -->
|
||||||
</button>
|
|
||||||
{:else}
|
<div>
|
||||||
<Dropdown>
|
<Uploader recordId={record.id} on:uploadComplete={uploadComplete} />
|
||||||
<div slot="button">
|
</div>
|
||||||
Browse
|
|
||||||
</div>
|
|
||||||
{#each collections as collection}
|
|
||||||
<!-- {`${channelurl}/content/${collection.name}?parent=${record.id}&parentfield=${field.name}`} -->
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
on:click={(e) => openBrowseModal(e, collection.name)}
|
|
||||||
href="/">{collection.label}</a
|
|
||||||
>
|
|
||||||
{/each}
|
|
||||||
</Dropdown>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{#if references.length > 0}
|
<!-- {#if references.length > 0}
|
||||||
<Sortable sortableClass="mt-3" on:update={reorder}>
|
<Sortable sortableClass="mt-3" on:update={reorder}>
|
||||||
{#each references as reference (reference.id)}
|
{#each references as reference (reference.id)}
|
||||||
<!--This div helps the sorting thing-->
|
<!--This div helps the sorting thing-->
|
||||||
<div>
|
<!-- <div>
|
||||||
<PreviewFile record={reference} hasDelete={true} on:remove={removeReference}></PreviewFile>
|
<PreviewFile
|
||||||
|
record={reference}
|
||||||
|
hasDelete={true}
|
||||||
|
on:remove={removeReference}
|
||||||
|
></PreviewFile>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</Sortable>
|
</Sortable>
|
||||||
{/if}
|
{/if} -->
|
||||||
<Dialog bind:this={browseModal} on:insert={insert}></Dialog>
|
<!-- <FileDialog bind:this={browseModal} on:insert={insert}></FileDialog> -->
|
||||||
|
|||||||
+13
-22
@@ -11,7 +11,6 @@ final class Channel
|
|||||||
{
|
{
|
||||||
public string $lucentUrl;
|
public string $lucentUrl;
|
||||||
public string $filesUrl;
|
public string $filesUrl;
|
||||||
public array $disks;
|
|
||||||
public string $previewTargetUrl;
|
public string $previewTargetUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -19,37 +18,29 @@ final class Channel
|
|||||||
* @param Collection<UserCommand> $commands
|
* @param Collection<UserCommand> $commands
|
||||||
*/
|
*/
|
||||||
function __construct(
|
function __construct(
|
||||||
public string $name,
|
public string $name,
|
||||||
public string $url,
|
public string $url,
|
||||||
public string $previewTarget,
|
public string $previewTarget,
|
||||||
public Collection $commands,
|
public Collection $commands,
|
||||||
public Collection $schemas,
|
public Collection $schemas,
|
||||||
public array $imageFilters,
|
public array $imageFilters,
|
||||||
public array $roles,
|
public array $roles,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->lucentUrl = $url . "/lucent";
|
$this->lucentUrl = $url . "/lucent";
|
||||||
$this->filesUrl = $this->makeFilesUrl();
|
$this->filesUrl = $this->makeFilesUrl();
|
||||||
$this->disks = $this->getDisksFromSchemas();
|
|
||||||
$this->previewTargetUrl = $url . "/" . $previewTarget;
|
$this->previewTargetUrl = $url . "/" . $previewTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function makeFilesUrl(): string
|
private function makeFilesUrl(): string
|
||||||
{
|
{
|
||||||
return match (config("filesystems.disks.lucent.driver")) {
|
return match (config("filesystems.disks.lucent.driver")) {
|
||||||
"s3" => config("filesystems.disks.lucent.endpoint") . "/" . config("filesystems.disks.lucent.bucket"),
|
"s3" => config("filesystems.disks.lucent.endpoint") .
|
||||||
"local" => $this->url . "/storage" . config("filesystems.disks.lucent.endpoint"),
|
"/" .
|
||||||
default => ""
|
config("filesystems.disks.lucent.bucket"),
|
||||||
|
"local" => $this->url .
|
||||||
|
"/storage" .
|
||||||
|
config("filesystems.disks.lucent.endpoint"),
|
||||||
|
default => "",
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getDisksFromSchemas()
|
|
||||||
{
|
|
||||||
return $this->schemas->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)->reduce(function (array $carry, Schema $schema) {
|
|
||||||
$carry[$schema->disk] = config("filesystems.disks." . $schema->disk . ".url");
|
|
||||||
return $carry;
|
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ namespace Lucent\Channel;
|
|||||||
|
|
||||||
use Lucent\Channel\Data\UserCommand;
|
use Lucent\Channel\Data\UserCommand;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Data\Schema;
|
||||||
use Lucent\Schema\SchemaService;
|
use Lucent\Schema\SchemaService;
|
||||||
use PhpOption\Option;
|
use PhpOption\Option;
|
||||||
|
|
||||||
|
|||||||
@@ -4,22 +4,18 @@ namespace Lucent\Commands;
|
|||||||
|
|
||||||
use DirectoryIterator;
|
use DirectoryIterator;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Data\Schema;
|
||||||
use Lucent\Schema\SchemaService;
|
use Lucent\Schema\SchemaService;
|
||||||
use Lucent\Schema\Type;
|
|
||||||
|
|
||||||
class CompileSchemas extends Command
|
class CompileSchemas extends Command
|
||||||
{
|
{
|
||||||
|
protected $signature = "lucent:schemas";
|
||||||
|
|
||||||
protected $signature = 'lucent:schemas';
|
protected $description = "Compiles schemas";
|
||||||
|
|
||||||
protected $description = 'Compiles schemas';
|
|
||||||
|
|
||||||
|
|
||||||
public function handle(SchemaService $schemaService)
|
public function handle(SchemaService $schemaService)
|
||||||
{
|
{
|
||||||
|
$configDir = base_path(config("lucent.schemas_path"));
|
||||||
$configDir = base_path(config('lucent.schemas_path'));
|
|
||||||
$schemasDirIterator = new DirectoryIterator($configDir);
|
$schemasDirIterator = new DirectoryIterator($configDir);
|
||||||
$schemas = [];
|
$schemas = [];
|
||||||
|
|
||||||
@@ -28,31 +24,39 @@ class CompileSchemas extends Command
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$schemaJson = file_get_contents($configDir . "/" . $file->getFilename());
|
$schemaJson = file_get_contents(
|
||||||
|
$configDir . "/" . $file->getFilename(),
|
||||||
|
);
|
||||||
$schema = json_decode($schemaJson, true);
|
$schema = json_decode($schemaJson, true);
|
||||||
if (empty($schema)) {
|
if (empty($schema)) {
|
||||||
$this->error("Invalid JSON " . $file->getFilename());
|
$this->error("Invalid JSON " . $file->getFilename());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$schemas[] = $schema;
|
$schemas[] = $schema;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$schemas = collect($schemas)->sortBy("label")->values();
|
$schemas = collect($schemas)->sortBy("label")->values();
|
||||||
$roles = $schemas
|
$roles = $schemas
|
||||||
->map([$schemaService, 'fromArray'])
|
->map([$schemaService, "fromArray"])
|
||||||
->whereIn("type", [Type::COLLECTION, Type::FILES])
|
->reduce(
|
||||||
->reduce(fn($carry, Schema $schema) => array_merge(
|
fn($carry, Schema $schema) => array_merge(
|
||||||
$carry,
|
$carry,
|
||||||
$schema->read,
|
$schema->read,
|
||||||
$schema->write,
|
$schema->write,
|
||||||
config("lucent.canInvite") ?? [],
|
config("lucent.canInvite") ?? [],
|
||||||
config("lucent.canBuild") ?? [],
|
config("lucent.canBuild") ?? [],
|
||||||
), []);
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
$json = [
|
$json = [
|
||||||
"schemas" => $schemas->toArray(),
|
"schemas" => $schemas->toArray(),
|
||||||
"roles" => collect($roles)->push("admin")->push("removed")->unique()->values()->toArray()
|
"roles" => collect($roles)
|
||||||
|
->push("admin")
|
||||||
|
->push("removed")
|
||||||
|
->unique()
|
||||||
|
->values()
|
||||||
|
->toArray(),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!file_exists(storage_path("lucent"))) {
|
if (!file_exists(storage_path("lucent"))) {
|
||||||
@@ -63,5 +67,4 @@ class CompileSchemas extends Command
|
|||||||
|
|
||||||
$this->info("Lucent Schemas were updated");
|
$this->info("Lucent Schemas were updated");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,44 +15,47 @@ use Lucent\Schema\Type;
|
|||||||
|
|
||||||
class RebuildThumbnails extends Command
|
class RebuildThumbnails extends Command
|
||||||
{
|
{
|
||||||
|
protected $signature = "lucent:rebuild:thumbnails";
|
||||||
|
|
||||||
protected $signature = 'lucent:rebuild:thumbnails';
|
protected $description = "Rebuilds thumbnails for path";
|
||||||
|
|
||||||
protected $description = 'Rebuilds thumbnails for path';
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public Query $query,
|
public Query $query,
|
||||||
public FileService $fileService,
|
public FileService $fileService,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function handle(ChannelService $channelService): int
|
public function handle(ChannelService $channelService): int
|
||||||
{
|
{
|
||||||
$channelService->channel->schemas
|
$channelService->channel->schemas
|
||||||
->filter(fn(Schema $schema) => get_class($schema) === FilesSchema::class)
|
->filter(
|
||||||
->map([$this, 'rebuildThumbnails']);
|
fn(Schema $schema) => get_class($schema) === FilesSchema::class,
|
||||||
|
)
|
||||||
|
->map([$this, "rebuildThumbnails"]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rebuildThumbnails(FilesSchema $schema): void
|
public function rebuildThumbnails(FilesSchema $schema): void
|
||||||
{
|
{
|
||||||
|
$this->info("Rebuilding thumbnails for " . $schema->name);
|
||||||
$this->info("Rebuilding thumbnails for ". $schema->name);
|
$records = $this->query
|
||||||
$records = $this->query->limit(0)->filter(["schema" => $schema->name])->run()->records;
|
->limit(0)
|
||||||
|
->filter(["schema" => $schema->name])
|
||||||
|
->run()->records;
|
||||||
$disk = $this->fileService->loadDisk($schema->disk);
|
$disk = $this->fileService->loadDisk($schema->disk);
|
||||||
foreach ($records as $record) {
|
foreach ($records as $record) {
|
||||||
try{
|
try {
|
||||||
|
$this->fileService->createTemplates(
|
||||||
$this->fileService->createTemplates($disk, $record->_file->path);
|
$disk,
|
||||||
|
$record->_file->path,
|
||||||
|
);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo "File ". $record->_file->originalName . " could not be rebuilt \n" ;
|
echo "File " .
|
||||||
|
$record->_file->originalName .
|
||||||
|
" could not be rebuilt \n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class SetupDatabase extends Command
|
|||||||
{
|
{
|
||||||
$this->tableUsers();
|
$this->tableUsers();
|
||||||
$this->tableRecords();
|
$this->tableRecords();
|
||||||
|
$this->tableFiles();
|
||||||
$this->tableRevisions();
|
$this->tableRevisions();
|
||||||
$this->tableSessions();
|
$this->tableSessions();
|
||||||
$this->tableCommandLogs();
|
$this->tableCommandLogs();
|
||||||
@@ -73,11 +74,8 @@ class SetupDatabase extends Command
|
|||||||
$table->string("status");
|
$table->string("status");
|
||||||
$table->jsonb("data");
|
$table->jsonb("data");
|
||||||
$table->jsonb("_sys");
|
$table->jsonb("_sys");
|
||||||
$table->jsonb("_file");
|
|
||||||
$table->text("search")->default("");
|
$table->text("search")->default("");
|
||||||
|
|
||||||
// $table->index(["schema", "_sys->updatedAt", "status"]);
|
// $table->index(["schema", "_sys->updatedAt", "status"]);
|
||||||
|
|
||||||
$table->index("search");
|
$table->index("search");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,6 +102,31 @@ class SetupDatabase extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function tableFiles(): void
|
||||||
|
{
|
||||||
|
$schema = Database::make()->getSchemaBuilder();
|
||||||
|
if (!$schema->hasTable($this->prefix . "files")) {
|
||||||
|
$schema->create($this->prefix . "files", function (
|
||||||
|
Blueprint $table,
|
||||||
|
) {
|
||||||
|
$table->uuid("id")->primary();
|
||||||
|
$table->uuid("record");
|
||||||
|
$table->string("name");
|
||||||
|
$table->string("ogName");
|
||||||
|
$table->string("mime");
|
||||||
|
$table->string("path");
|
||||||
|
$table->integer("size");
|
||||||
|
$table->integer("width");
|
||||||
|
$table->integer("height");
|
||||||
|
$table->string("checksum");
|
||||||
|
});
|
||||||
|
|
||||||
|
DB::statement(
|
||||||
|
"CREATE INDEX ON " . $this->prefix . "files (record)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function tableRevisions(): void
|
private function tableRevisions(): void
|
||||||
{
|
{
|
||||||
$schema = Database::make()->getSchemaBuilder();
|
$schema = Database::make()->getSchemaBuilder();
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Data;
|
||||||
|
|
||||||
|
class File
|
||||||
|
{
|
||||||
|
function __construct(
|
||||||
|
public readonly string $id,
|
||||||
|
public readonly string $recordId,
|
||||||
|
public readonly string $originalName,
|
||||||
|
public readonly string $mime,
|
||||||
|
public readonly string $path,
|
||||||
|
public readonly int $size,
|
||||||
|
public readonly int $width,
|
||||||
|
public readonly int $height,
|
||||||
|
public readonly string $checksum,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static function fromArray(array $data): File
|
||||||
|
{
|
||||||
|
return new File(
|
||||||
|
id: data_get($data, "id"),
|
||||||
|
recordId: data_get($data, "recordId"),
|
||||||
|
originalName: data_get($data, "originalName"),
|
||||||
|
mime: data_get($data, "mime"),
|
||||||
|
path: data_get($data, "path"),
|
||||||
|
size: data_get($data, "size"),
|
||||||
|
width: data_get($data, "width"),
|
||||||
|
height: data_get($data, "height"),
|
||||||
|
checksum: data_get($data, "checksum"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return \json_decode(\json_encode($this), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lucent\Data;
|
||||||
|
|
||||||
|
use Lucent\Primitive\Collection;
|
||||||
|
|
||||||
|
class Schema
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param Collection<FieldInterface> $fields
|
||||||
|
* @param array<string> $visible
|
||||||
|
*/
|
||||||
|
function __construct(
|
||||||
|
public string $name,
|
||||||
|
public string $label,
|
||||||
|
public array $visible,
|
||||||
|
public array $groups,
|
||||||
|
public Collection $fields,
|
||||||
|
public bool $isEntry = false,
|
||||||
|
public string $color = "",
|
||||||
|
public string $sortBy = "-_sys.updatedAt",
|
||||||
|
public ?string $cardTitle = null,
|
||||||
|
public ?string $cardImage = null,
|
||||||
|
public int $revisions = 0,
|
||||||
|
public array $read = [],
|
||||||
|
public array $write = [],
|
||||||
|
) {}
|
||||||
|
}
|
||||||
+62
-53
@@ -10,46 +10,53 @@ use Illuminate\Support\Str;
|
|||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
use Lucent\Database\Database;
|
use Lucent\Database\Database;
|
||||||
|
use Lucent\Id\Id;
|
||||||
use Lucent\LucentException;
|
use Lucent\LucentException;
|
||||||
use Lucent\Record\FileData as RecordFile;
|
use Lucent\Data\File as DataFile;
|
||||||
use Lucent\Record\QueryRecord;
|
use Lucent\Record\QueryRecord;
|
||||||
use Lucent\Schema\FilesSchema;
|
use Lucent\Schema\FilesSchema;
|
||||||
use Lucent\Schema\Schema;
|
|
||||||
use Spatie\ImageOptimizer\OptimizerChainFactory;
|
use Spatie\ImageOptimizer\OptimizerChainFactory;
|
||||||
|
|
||||||
class FileService
|
class FileService
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ChannelService $channelService,
|
public ChannelService $channelService,
|
||||||
public ImageManager $imageManager,
|
public ImageManager $imageManager,
|
||||||
public Logger $logger
|
public Logger $logger,
|
||||||
)
|
) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPath(QueryRecord $file): string
|
public function getPath(QueryRecord $file): string
|
||||||
{
|
{
|
||||||
return $this->channelService->channel->filesUrl . "/" . $file->_file->path;
|
return $this->channelService->channel->filesUrl .
|
||||||
|
"/" .
|
||||||
|
$file->_file->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFromUrl(FilesSchema $schema, string $url): FileUploadResult
|
public function createFromUrl(
|
||||||
{
|
FilesSchema $schema,
|
||||||
|
string $url,
|
||||||
|
): FileUploadResult {
|
||||||
$pathinfo = pathinfo($url);
|
$pathinfo = pathinfo($url);
|
||||||
$contents = file_get_contents($url);
|
$contents = file_get_contents($url);
|
||||||
if ($contents === false) {
|
if ($contents === false) {
|
||||||
throw new LucentException("Failed to upload file from url");
|
throw new LucentException("Failed to upload file from url");
|
||||||
}
|
}
|
||||||
$file = '/tmp/' . $pathinfo['basename'];
|
$file = "/tmp/" . $pathinfo["basename"];
|
||||||
file_put_contents($file, $contents);
|
file_put_contents($file, $contents);
|
||||||
$uploadedFile = new UploadedFile($file, $pathinfo['basename']);
|
$uploadedFile = new UploadedFile($file, $pathinfo["basename"]);
|
||||||
return $this->upload($schema, $uploadedFile);
|
return $this->upload($schema, $uploadedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload(FilesSchema $schema, UploadedFile $file): FileUploadResult
|
public function upload(string $recordId, UploadedFile $file): DataFile
|
||||||
{
|
{
|
||||||
$originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
|
$originalName = pathinfo(
|
||||||
$extension = pathinfo($file->getClientOriginalName(), PATHINFO_EXTENSION);
|
$file->getClientOriginalName(),
|
||||||
|
PATHINFO_FILENAME,
|
||||||
|
);
|
||||||
|
$extension = pathinfo(
|
||||||
|
$file->getClientOriginalName(),
|
||||||
|
PATHINFO_EXTENSION,
|
||||||
|
);
|
||||||
$originalFilename = $file->getClientOriginalName();
|
$originalFilename = $file->getClientOriginalName();
|
||||||
$filename = $this->createFileName($originalName, $extension);
|
$filename = $this->createFileName($originalName, $extension);
|
||||||
$mimetype = $file->getMimeType();
|
$mimetype = $file->getMimeType();
|
||||||
@@ -58,72 +65,74 @@ class FileService
|
|||||||
$optimizerChain->setTimeout(30)->optimize($file->getPathName());
|
$optimizerChain->setTimeout(30)->optimize($file->getPathName());
|
||||||
|
|
||||||
$checksum = sha1_file($file);
|
$checksum = sha1_file($file);
|
||||||
$recordId = $this->checkDuplicate($schema->name, $checksum, $file->getSize());
|
|
||||||
|
|
||||||
if (!empty($recordId)) {
|
$disk = $this->loadDisk();
|
||||||
return new FileUploadResult(
|
$path = "files/" . $recordId . "/" . $filename;
|
||||||
recordFile: null,
|
|
||||||
duplicateId: $recordId,
|
|
||||||
isDuplicate: true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$disk = $this->loadDisk($schema);
|
|
||||||
$path = $schema->path . "/" . $filename;
|
|
||||||
$res = $disk->put(
|
$res = $disk->put(
|
||||||
$path,
|
$path,
|
||||||
file_get_contents($file),
|
file_get_contents($file),
|
||||||
// 'public' // now managed by aws policy
|
// 'public' // now managed by aws policy
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($res === false) {
|
if ($res === false) {
|
||||||
throw new LucentException("File $filename not uploaded");
|
throw new LucentException("File $filename not uploaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->isImage($mimetype)){
|
if ($this->isImage($mimetype)) {
|
||||||
$this->createTemplates($disk, $path);
|
$this->createTemplates($disk, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[$width, $height] = $this->isImage($mimetype)
|
||||||
list($width, $height) = $this->isImage($mimetype) ? getimagesize($file) : [0, 0];
|
? getimagesize($file)
|
||||||
$recordFile = new RecordFile(
|
: [0, 0];
|
||||||
|
return new DataFile(
|
||||||
|
id: Id::new(),
|
||||||
|
recordId: $recordId,
|
||||||
originalName: $originalFilename,
|
originalName: $originalFilename,
|
||||||
mime: $mimetype,
|
mime: $mimetype,
|
||||||
path: $path,
|
path: $path,
|
||||||
disk: $schema->disk,
|
|
||||||
size: $file->getSize(),
|
size: $file->getSize(),
|
||||||
width: $width,
|
width: $width,
|
||||||
height: $height,
|
height: $height,
|
||||||
checksum: $checksum
|
checksum: $checksum,
|
||||||
);
|
|
||||||
|
|
||||||
return new FileUploadResult(
|
|
||||||
recordFile: $recordFile,
|
|
||||||
duplicateId: "",
|
|
||||||
isDuplicate: false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createFileName(string $originalName, string $extension): string
|
private function createFileName(
|
||||||
{
|
string $originalName,
|
||||||
return Str::slug($originalName, '-') . '-' . uniqid() . '.' . $extension;
|
string $extension,
|
||||||
|
): string {
|
||||||
|
return Str::slug($originalName, "-") .
|
||||||
|
"-" .
|
||||||
|
uniqid() .
|
||||||
|
"." .
|
||||||
|
$extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function isImage(string $mimetype): bool
|
private function isImage(string $mimetype): bool
|
||||||
{
|
{
|
||||||
$imageMimes = ['image/webp', 'image/gif', 'image/jpeg', 'image/png', 'image/tiff'];
|
$imageMimes = [
|
||||||
|
"image/webp",
|
||||||
|
"image/gif",
|
||||||
|
"image/jpeg",
|
||||||
|
"image/png",
|
||||||
|
"image/tiff",
|
||||||
|
];
|
||||||
return in_array($mimetype, $imageMimes);
|
return in_array($mimetype, $imageMimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadDisk(Schema|string $schema): Filesystem
|
public function loadDisk(): Filesystem
|
||||||
{
|
{
|
||||||
return Storage::disk($schema->disk ?? $schema);
|
return Storage::disk(config("lucent.disk"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkDuplicate(string $schemaName, string $checksum, int $filesize): string
|
private function checkDuplicate(
|
||||||
{
|
string $schemaName,
|
||||||
|
string $checksum,
|
||||||
$record = Database::make()->table("lucent_records")
|
int $filesize,
|
||||||
|
): string {
|
||||||
|
$record = Database::make()
|
||||||
|
->table("lucent_records")
|
||||||
->where("schema", $schemaName)
|
->where("schema", $schemaName)
|
||||||
->where("_file->checksum", $checksum)
|
->where("_file->checksum", $checksum)
|
||||||
->where("_file->size", $filesize)
|
->where("_file->size", $filesize)
|
||||||
@@ -137,14 +146,14 @@ class FileService
|
|||||||
$originalImage = $this->imageManager->make($disk->get($path));
|
$originalImage = $this->imageManager->make($disk->get($path));
|
||||||
foreach (config("lucent.imageFilters") as $preset => $filterClass) {
|
foreach (config("lucent.imageFilters") as $preset => $filterClass) {
|
||||||
$imageClone = clone $originalImage;
|
$imageClone = clone $originalImage;
|
||||||
$image = $imageClone->filter(new $filterClass);
|
$image = $imageClone->filter(new $filterClass());
|
||||||
$templateUri = "/templates/" . $preset . "/" . $path;
|
$templateUri = "/templates/" . $preset . "/" . $path;
|
||||||
$disk->put($templateUri, $image->encode('webp', 75));
|
$disk->put($templateUri, $image->encode("webp", 75));
|
||||||
}
|
}
|
||||||
|
|
||||||
$thumbDir = "thumbs/" . $path;
|
$thumbDir = "thumbs/" . $path;
|
||||||
|
|
||||||
$image = $originalImage->fit(300, 300);
|
$image = $originalImage->fit(300, 300);
|
||||||
$disk->put($thumbDir, $image->encode('webp', 75));
|
$disk->put($thumbDir, $image->encode("webp", 75));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,19 +15,14 @@ use Lucent\Record\Status;
|
|||||||
use function Lucent\Response\fail;
|
use function Lucent\Response\fail;
|
||||||
use function Lucent\Response\ok;
|
use function Lucent\Response\ok;
|
||||||
|
|
||||||
|
|
||||||
class FileController extends Controller
|
class FileController extends Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ChannelService $channelService,
|
private readonly ChannelService $channelService,
|
||||||
private readonly RecordService $recordService,
|
private readonly RecordService $recordService,
|
||||||
private readonly FileService $fileService,
|
private readonly FileService $fileService,
|
||||||
private readonly Query $query
|
private readonly Query $query,
|
||||||
)
|
) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function fromDisk(Request $request, string $disk)
|
public function fromDisk(Request $request, string $disk)
|
||||||
{
|
{
|
||||||
@@ -38,7 +33,7 @@ class FileController extends Controller
|
|||||||
|
|
||||||
public function thumb(Request $request, string $disk)
|
public function thumb(Request $request, string $disk)
|
||||||
{
|
{
|
||||||
$imagePath = "thumbs/".$request->route("any");
|
$imagePath = "thumbs/" . $request->route("any");
|
||||||
$disk = $this->fileService->loadDisk($disk);
|
$disk = $this->fileService->loadDisk($disk);
|
||||||
return response()->file($disk->path($imagePath));
|
return response()->file($disk->path($imagePath));
|
||||||
}
|
}
|
||||||
@@ -49,34 +44,21 @@ class FileController extends Controller
|
|||||||
return $disk->download($request->input("path"));
|
return $disk->download($request->input("path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function upload(Request $request)
|
public function upload(Request $request)
|
||||||
{
|
{
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'files.*' => 'required|file|max:100000',
|
"files.*" => "required|file|max:100000",
|
||||||
]);
|
]);
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return fail($validator->errors()->first());
|
return fail($validator->errors()->first());
|
||||||
}
|
}
|
||||||
$schema = $this->channelService->channel->schemas->firstWhere("name", $request->input("schema"));
|
$recordId = $request->input("recordId");
|
||||||
$files = $request->file('files');
|
$files = $request->file("files");
|
||||||
|
|
||||||
foreach ($files as $file) {
|
return ok(
|
||||||
$this->recordService->createFromUploadedFile($file, new RecordInputData(
|
collect($files)
|
||||||
schemaName: $schema->name,
|
->map(fn($file) => $this->fileService->upload($recordId, $file))
|
||||||
status: Status::PUBLISHED,
|
->toArray(),
|
||||||
), []);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
$graph = $this->query
|
|
||||||
->filter([
|
|
||||||
"schema" => $schema->name
|
|
||||||
])
|
|
||||||
->limit(15)
|
|
||||||
->skip(0)
|
|
||||||
->sort("-_sys.updatedAt")
|
|
||||||
->run();
|
|
||||||
|
|
||||||
return ok($graph->records->toArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,59 +2,39 @@
|
|||||||
|
|
||||||
namespace Lucent\Schema;
|
namespace Lucent\Schema;
|
||||||
|
|
||||||
|
use Lucent\Data\Schema;
|
||||||
use Lucent\LucentException;
|
use Lucent\LucentException;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
|
|
||||||
class SchemaService
|
class SchemaService
|
||||||
{
|
{
|
||||||
|
public function __construct() {}
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromArray(array $schemaArr): Schema
|
public function fromArray(array $schemaArr): Schema
|
||||||
{
|
{
|
||||||
|
return new Schema(
|
||||||
return match ($schemaArr["type"]) {
|
name: $schemaArr["name"],
|
||||||
"collection" => new CollectionSchema(
|
label: $schemaArr["label"],
|
||||||
name: $schemaArr["name"],
|
visible: $schemaArr["visible"] ?? [],
|
||||||
label: $schemaArr["label"],
|
groups: $schemaArr["groups"] ?? [],
|
||||||
visible: $schemaArr["visible"] ?? [],
|
fields: Collection::make($schemaArr["fields"])->map([
|
||||||
groups: $schemaArr["groups"] ?? [],
|
$this,
|
||||||
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
|
"mapFields",
|
||||||
isEntry: $schemaArr["isEntry"] ?? false,
|
]),
|
||||||
color: $schemaArr["color"] ?? "",
|
isEntry: $schemaArr["isEntry"] ?? false,
|
||||||
sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt",
|
color: $schemaArr["color"] ?? "",
|
||||||
cardTitle: $schemaArr["titleTemplate"] ?? $schemaArr["cardTitle"] ?? null,
|
sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt",
|
||||||
cardImage: $schemaArr["cardImage"] ?? null,
|
cardTitle: $schemaArr["titleTemplate"] ??
|
||||||
revisions: $schemaArr["revisions"] ?? 0,
|
($schemaArr["cardTitle"] ?? null),
|
||||||
read: $schemaArr["read"] ?? [],
|
cardImage: $schemaArr["cardImage"] ?? null,
|
||||||
write: $schemaArr["write"] ?? [],
|
revisions: $schemaArr["revisions"] ?? 0,
|
||||||
),
|
read: $schemaArr["read"] ?? [],
|
||||||
"files" => new FilesSchema(
|
write: $schemaArr["write"] ?? [],
|
||||||
name: $schemaArr["name"],
|
);
|
||||||
label: $schemaArr["label"],
|
|
||||||
fields: (new Collection($schemaArr["fields"]))->map([$this, 'mapFields']),
|
|
||||||
disk: $schemaArr["disk"] ?? "lucent",
|
|
||||||
path: $schemaArr["path"] ?? $schemaArr["name"],
|
|
||||||
groups: $schemaArr["groups"] ?? [],
|
|
||||||
isEntry: $schemaArr["isEntry"] ?? false,
|
|
||||||
sortBy: $schemaArr["sortBy"] ?? "-_sys.updatedAt",
|
|
||||||
color: $schemaArr["color"] ?? "",
|
|
||||||
cardTitle: $schemaArr["titleTemplate"] ?? $schemaArr["cardTitle"] ?? null,
|
|
||||||
cardImage: $schemaArr["cardImage"] ?? null,
|
|
||||||
revisions: $schemaArr["revisions"] ?? 0,
|
|
||||||
read: $schemaArr["read"] ?? [],
|
|
||||||
write: $schemaArr["write"] ?? [],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mapFields(array $field): FieldInterface
|
public function mapFields(array $field): FieldInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
$schemaFields = [
|
$schemaFields = [
|
||||||
\Lucent\Schema\Ui\Checkbox::class,
|
\Lucent\Schema\Ui\Checkbox::class,
|
||||||
\Lucent\Schema\Ui\Color::class,
|
\Lucent\Schema\Ui\Color::class,
|
||||||
@@ -70,16 +50,22 @@ class SchemaService
|
|||||||
\Lucent\Schema\Ui\Text::class,
|
\Lucent\Schema\Ui\Text::class,
|
||||||
\Lucent\Schema\Ui\Textarea::class,
|
\Lucent\Schema\Ui\Textarea::class,
|
||||||
];
|
];
|
||||||
$ui = collect($schemaFields)->filter(function ($className) use ($field) {
|
$ui = collect($schemaFields)
|
||||||
return str_ends_with(strtolower($className), "\\" . strtolower($field["ui"]));
|
->filter(function ($className) use ($field) {
|
||||||
})->first();
|
return str_ends_with(
|
||||||
|
strtolower($className),
|
||||||
|
"\\" . strtolower($field["ui"]),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
->first();
|
||||||
|
|
||||||
if (empty($ui)) {
|
if (empty($ui)) {
|
||||||
throw new LucentException("Field UI " . $field["ui"] . " not found");
|
throw new LucentException(
|
||||||
|
"Field UI " . $field["ui"] . " not found",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($field["ui"]);
|
unset($field["ui"]);
|
||||||
return new $ui(...$field);
|
return new $ui(...$field);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user