file uploads
This commit is contained in:
@@ -1,68 +0,0 @@
|
|||||||
<script>
|
|
||||||
import {getContext} from "svelte";
|
|
||||||
import Preview from "../files/Preview.svelte";
|
|
||||||
import {selectRecord} from "./functions/recordSelect.js";
|
|
||||||
|
|
||||||
const channel = getContext("channel");
|
|
||||||
|
|
||||||
export let schema;
|
|
||||||
export let records;
|
|
||||||
export let isWritable;
|
|
||||||
export let selected = [];
|
|
||||||
|
|
||||||
function select(record) {
|
|
||||||
selected = selectRecord(record, selected)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<div class="row" style="max-width:1000px">
|
|
||||||
{#each records as record (record.id)}
|
|
||||||
<div class="col-6 col-md-4">
|
|
||||||
<div
|
|
||||||
class="file-wrapper rounded p-2 mb-4 bg-light"
|
|
||||||
class:selected={selected.includes(record)}
|
|
||||||
>
|
|
||||||
{#if isWritable}
|
|
||||||
<div class="form-check">
|
|
||||||
<input
|
|
||||||
on:change={() => select(record)}
|
|
||||||
class="form-check-input "
|
|
||||||
type="checkbox"
|
|
||||||
checked={selected.find(
|
|
||||||
(r) => r.id === record.id
|
|
||||||
)}
|
|
||||||
value={record}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
<Preview {record} size="medium"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
|
||||||
title={record._file.path}
|
|
||||||
class="d-block text-center overflow-hidden text-nowrap my-2 "
|
|
||||||
style="
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #333;
|
|
||||||
">{record._file.path}</a
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="lx-small-text text-muted d-block text-center"
|
|
||||||
>{record._file.mime}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.form-check {
|
|
||||||
display: inline-block;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
import Pagination from "./pagination/Pagination.svelte";
|
import Pagination from "./pagination/Pagination.svelte";
|
||||||
import ActionsOnSelected from "./ActionsOnSelected.svelte";
|
import ActionsOnSelected from "./ActionsOnSelected.svelte";
|
||||||
import Table from "./Table.svelte";
|
import Table from "./Table.svelte";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
const axios = getContext("axios");
|
const axios = getContext("axios");
|
||||||
export let schema;
|
export let schema;
|
||||||
@@ -47,12 +47,12 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<div class="{inModal ? 'mt-0' : 'mt-5'}">
|
<div class={inModal ? "mt-0" : "mt-5"}>
|
||||||
<h3 class="header-normal mb-5 ">
|
<h3 class="header-normal mb-5">
|
||||||
{schema.label}
|
{schema.label}
|
||||||
</h3>
|
</h3>
|
||||||
{#if selected.length > 0 && !inModal && isWritable}
|
{#if selected.length > 0 && !inModal && isWritable}
|
||||||
<ActionsOnSelected {schema} {selected} {filter}/>
|
<ActionsOnSelected {schema} {selected} {filter} />
|
||||||
{:else}
|
{:else}
|
||||||
<Tools
|
<Tools
|
||||||
bind:schema
|
bind:schema
|
||||||
@@ -82,7 +82,6 @@
|
|||||||
{isWritable}
|
{isWritable}
|
||||||
bind:selected
|
bind:selected
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
@@ -94,4 +93,3 @@
|
|||||||
{modalUrl}
|
{modalUrl}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import RenderField from "./RenderField.svelte";
|
import RenderField from "./RenderField.svelte";
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import Status from "../records/Status.svelte";
|
import Status from "../records/Status.svelte";
|
||||||
import {usernameById} from "../account/users";
|
import { usernameById } from "../account/users";
|
||||||
import {friendlyDate} from "../../helpers";
|
import { friendlyDate } from "../../helpers";
|
||||||
|
|
||||||
export let schema;
|
export let schema;
|
||||||
export let users;
|
export let users;
|
||||||
@@ -12,7 +12,6 @@
|
|||||||
export let sortParam;
|
export let sortParam;
|
||||||
export let sortField;
|
export let sortField;
|
||||||
export let visibleColumns;
|
export let visibleColumns;
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each visibleColumns as field, index}
|
{#each visibleColumns as field, index}
|
||||||
@@ -20,7 +19,7 @@
|
|||||||
class="field-ui-{field.info.name}"
|
class="field-ui-{field.info.name}"
|
||||||
class:is-sort={field.name === sortField.name}
|
class:is-sort={field.name === sortField.name}
|
||||||
>
|
>
|
||||||
<RenderField {record} {schema} {graph} {field}/>
|
<RenderField {record} {schema} {graph} {field} />
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
{#if schema.visible?.includes("status")}
|
{#if schema.visible?.includes("status")}
|
||||||
@@ -28,32 +27,40 @@
|
|||||||
class="text-center"
|
class="text-center"
|
||||||
class:is-sort={"-status" == sortParam || "status" == sortParam}
|
class:is-sort={"-status" == sortParam || "status" == sortParam}
|
||||||
>
|
>
|
||||||
<Status status={record.status}/>
|
<Status status={record.status} />
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
{#if schema.visible?.includes("_sys.createdBy")}
|
{#if schema.visible?.includes("_sys.createdBy")}
|
||||||
<td
|
<td
|
||||||
class="text-center"
|
class="text-center"
|
||||||
class:is-sort={"-_sys.createdBy" == sortParam || "_sys.createdBy" == sortParam}
|
class:is-sort={"-_sys.createdBy" == sortParam ||
|
||||||
|
"_sys.createdBy" == sortParam}
|
||||||
>
|
>
|
||||||
<Avatar name={usernameById(users, record._sys.createdBy)} side={24}/>
|
<Avatar name={usernameById(users, record.createdBy)} side={24} />
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
{#if schema.visible?.includes("_sys.updatedBy")}
|
{#if schema.visible?.includes("_sys.updatedBy")}
|
||||||
<td
|
<td
|
||||||
class="text-center"
|
class="text-center"
|
||||||
class:is-sort={"-_sys.updatedBy" == sortParam || "_sys.updatedBy" == sortParam}
|
class:is-sort={"-_sys.updatedBy" == sortParam ||
|
||||||
|
"_sys.updatedBy" == sortParam}
|
||||||
>
|
>
|
||||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
<Avatar name={usernameById(users, record.updatedBy)} side={24} />
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
{#if schema.visible?.includes("_sys.createdAt")}
|
{#if schema.visible?.includes("_sys.createdAt")}
|
||||||
<td class:is-sort={"-_sys.createdAt" == sortParam || "_sys.createdAt" == sortParam}>
|
<td
|
||||||
{friendlyDate(record._sys.createdAt)}
|
class:is-sort={"-_sys.createdAt" == sortParam ||
|
||||||
|
"_sys.createdAt" == sortParam}
|
||||||
|
>
|
||||||
|
{friendlyDate(record.createdAt)}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
{#if schema.visible?.includes("_sys.updatedAt")}
|
{#if schema.visible?.includes("_sys.updatedAt")}
|
||||||
<td class:is-sort={"-_sys.updatedAt" == sortParam || "_sys.updatedAt" == sortParam}>
|
<td
|
||||||
{friendlyDate(record._sys.updatedAt)}
|
class:is-sort={"-_sys.updatedAt" == sortParam ||
|
||||||
|
"_sys.updatedAt" == sortParam}
|
||||||
|
>
|
||||||
|
{friendlyDate(record.updatedAt)}
|
||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import RecordRow from "./RecordRow.svelte";
|
import RecordRow from "./RecordRow.svelte";
|
||||||
import {previewTitle} from "../records/Preview";
|
import { previewTitle } from "../records/Preview";
|
||||||
import {usernameById} from "../account/users";
|
import { usernameById } from "../account/users";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import {selectRecord, toggleAll} from "./functions/recordSelect.js";
|
import { selectRecord, toggleAll } from "./functions/recordSelect.js";
|
||||||
import Checkbox from "../common/Checkbox.svelte";
|
import Checkbox from "../common/Checkbox.svelte";
|
||||||
import Preview from "../files/Preview.svelte";
|
import Preview from "../files/Preview.svelte";
|
||||||
import {fileurl} from "../files/imageserver.js";
|
import { fileurl } from "../files/imageserver.js";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
|
|
||||||
@@ -23,17 +23,19 @@
|
|||||||
export let selected = [];
|
export let selected = [];
|
||||||
|
|
||||||
function eventToggleAll(e) {
|
function eventToggleAll(e) {
|
||||||
selected = toggleAll(e, records, selected)
|
selected = toggleAll(e, records, selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(record) {
|
function select(record) {
|
||||||
selected = selectRecord(record, selected)
|
selected = selectRecord(record, selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
$: visibleColumns = schema.fields.filter(c => schema.visible?.includes(c.name) ?? [])
|
$: visibleColumns = schema.fields.filter(
|
||||||
|
(c) => schema.visible?.includes(c.name) ?? [],
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="table mt-5 ">
|
<div class="table mt-5">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -42,10 +44,10 @@
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
value=""
|
value=""
|
||||||
on:change={eventToggleAll}
|
on:change={eventToggleAll}
|
||||||
indeterminate={selected.length > 0 && selected.length < records.length}
|
indeterminate={selected.length > 0 &&
|
||||||
|
selected.length < records.length}
|
||||||
checked={selected.length === records.length}
|
checked={selected.length === records.length}
|
||||||
>
|
></Checkbox>
|
||||||
</Checkbox>
|
|
||||||
</th>
|
</th>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -54,12 +56,13 @@
|
|||||||
class="field-ui-{field.info.name ?? field.ui}"
|
class="field-ui-{field.info.name ?? field.ui}"
|
||||||
class:is-sort={field.name === sortField.name}
|
class:is-sort={field.name === sortField.name}
|
||||||
scope="col"
|
scope="col"
|
||||||
title={field.help}
|
title={field.help}>{field.label}</th
|
||||||
>{field.label}</th
|
|
||||||
>
|
>
|
||||||
{/each}
|
{/each}
|
||||||
{#each systemFields.filter(c => schema.visible?.includes(c.name)) as sysField}
|
{#each systemFields.filter( (c) => schema.visible?.includes(c.name), ) as sysField}
|
||||||
<th class:is-sort={sysField.name === sortField.name}>{sysField.label}</th>
|
<th class:is-sort={sysField.name === sortField.name}
|
||||||
|
>{sysField.label}</th
|
||||||
|
>
|
||||||
{/each}
|
{/each}
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -68,59 +71,29 @@
|
|||||||
{#each records as record (record.id)}
|
{#each records as record (record.id)}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="title-td">
|
<td class="title-td">
|
||||||
<div
|
<div class="title-td-contents">
|
||||||
class="title-td-contents"
|
|
||||||
>
|
|
||||||
{#if isWritable}
|
{#if isWritable}
|
||||||
<Checkbox
|
<Checkbox
|
||||||
on:change={() => select(record)}
|
on:change={() => select(record)}
|
||||||
checked={selected.find((r) => r.id === record.id)}
|
checked={selected.find(
|
||||||
|
(r) => r.id === record.id,
|
||||||
|
)}
|
||||||
value={record}
|
value={record}
|
||||||
>
|
></Checkbox>
|
||||||
</Checkbox>
|
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if record._file?.path}
|
|
||||||
<div class="file-table-row">
|
|
||||||
<Preview record={record} size={record._file?.width > 0 ? "medium" : "small"}/>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{#if record.status === "draft"}
|
|
||||||
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
|
||||||
{/if}
|
|
||||||
<a
|
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
|
||||||
target={inModal ? "_blank" : "_self"}
|
|
||||||
>
|
|
||||||
{previewTitle(channel.schemas, record, graph)}
|
|
||||||
</a>
|
|
||||||
<span>{(record._file.size / 1024).toFixed(1)}kB</span>
|
|
||||||
|
|
||||||
{#if record._file.width > 0}
|
|
||||||
<span>{record._file.width + "x" + record._file.height}</span>
|
|
||||||
{/if}
|
|
||||||
<a
|
|
||||||
href="{fileurl(channel,record)}"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<a
|
<a
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
href="{channel.lucentUrl}/records/{record.id}"
|
||||||
target={inModal ? "_blank" : "_self"}
|
target={inModal ? "_blank" : "_self"}
|
||||||
>
|
>
|
||||||
{#if record.status === "draft"}
|
{#if record.status === "draft"}
|
||||||
<span style="text-transform: uppercase;font-size:10px">{record.status}</span>
|
<span
|
||||||
|
style="text-transform: uppercase;font-size:10px"
|
||||||
|
>{record.status}</span
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{previewTitle(channel.schemas, record, graph)}
|
{previewTitle(channel.schemas, record, graph)}
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<RecordRow
|
<RecordRow
|
||||||
@@ -134,10 +107,7 @@
|
|||||||
/>
|
/>
|
||||||
<td>
|
<td>
|
||||||
<Avatar
|
<Avatar
|
||||||
name={usernameById(
|
name={usernameById(users, record.updatedBy)}
|
||||||
users,
|
|
||||||
record._sys.updatedBy
|
|
||||||
)}
|
|
||||||
side={24}
|
side={24}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@@ -146,4 +116,3 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import FilterFields from "./FilterFields.svelte";
|
import FilterFields from "./FilterFields.svelte";
|
||||||
import Uploader from "../../files/Uploader.svelte";
|
|
||||||
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";
|
||||||
|
|||||||
@@ -33,18 +33,12 @@
|
|||||||
|
|
||||||
function insert(e) {
|
function insert(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dispatch("insert", {
|
dispatch("insert_files", selectedRecords);
|
||||||
records: selectedRecords,
|
|
||||||
action: "insert",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replace(e) {
|
function replace(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dispatch("insert", {
|
dispatch("replace_files", selectedRecords);
|
||||||
records: selectedRecords,
|
|
||||||
action: "replace",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function open(recordId) {
|
export function open(recordId) {
|
||||||
|
|||||||
@@ -97,21 +97,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<!-- <RecordRow
|
|
||||||
{record}
|
|
||||||
{graph}
|
|
||||||
{schema}
|
|
||||||
{visibleColumns}
|
|
||||||
{sortParam}
|
|
||||||
{sortField}
|
|
||||||
{users}
|
|
||||||
/> -->
|
|
||||||
<!-- <td>
|
|
||||||
<Avatar
|
|
||||||
name={usernameById(users, record._sys.updatedBy)}
|
|
||||||
side={24}
|
|
||||||
/>
|
|
||||||
</td> -->
|
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function sortByField(from, to, edges, fieldName, references) {
|
export function sortByField(from, to, edges, fieldName, references) {
|
||||||
if (from === to) {
|
if (from === to) {
|
||||||
return edges;
|
return edges;
|
||||||
}
|
}
|
||||||
let referenceIds = references.map(r => r.id);
|
let referenceIds = references.map((r) => r.id);
|
||||||
let edgesTosort = edges?.filter((ed) => ed.field === fieldName && ed.depth === 1 && referenceIds.includes(ed.target)) ?? [];
|
let edgesTosort =
|
||||||
let remainingEdge = edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
edges?.filter(
|
||||||
|
(ed) =>
|
||||||
|
ed.field === fieldName &&
|
||||||
|
ed.depth === 1 &&
|
||||||
|
referenceIds.includes(ed.target),
|
||||||
|
) ?? [];
|
||||||
|
let remainingEdge =
|
||||||
|
edges?.filter((ed) => !(ed.field === fieldName && ed.depth === 1)) ?? [];
|
||||||
|
|
||||||
edgesTosort = array_move(edgesTosort,from, to);
|
edgesTosort = array_move(edgesTosort, from, to);
|
||||||
return [...remainingEdge, ...edgesTosort];
|
return [...remainingEdge, ...edgesTosort];
|
||||||
}
|
}
|
||||||
|
|
||||||
function array_move(arr, old_index, new_index) {
|
export function array_move(arr, old_index, new_index) {
|
||||||
if (new_index >= arr.length) {
|
if (new_index >= arr.length) {
|
||||||
var k = new_index - arr.length + 1;
|
var k = new_index - arr.length + 1;
|
||||||
while (k--) {
|
while (k--) {
|
||||||
@@ -22,4 +26,4 @@ function array_move(arr, old_index, new_index) {
|
|||||||
}
|
}
|
||||||
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
arr.splice(new_index, 0, arr.splice(old_index, 1)[0]);
|
||||||
return arr; // for testing
|
return arr; // for testing
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<script>
|
<script>
|
||||||
import {formatDistanceToNow, parseJSON} from "date-fns";
|
import { formatDistanceToNow, parseJSON } from "date-fns";
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import {previewTitle} from "../records/Preview";
|
import { previewTitle } from "../records/Preview";
|
||||||
import Preview from "../files/Preview.svelte";
|
import Preview from "../files/Preview.svelte";
|
||||||
import {usernameById} from "../account/users";
|
import { usernameById } from "../account/users";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let users;
|
export let users;
|
||||||
export let graph;
|
export let graph;
|
||||||
export let record;
|
export let record;
|
||||||
let schema = channel.schemas.find((s) => s.name === record.schema);
|
let schema = channel.schemas.find((s) => s.name === record.schema);
|
||||||
let frieldlyUpdatedAt = formatDistanceToNow(
|
let frieldlyUpdatedAt = formatDistanceToNow(parseJSON(record.updatedAt), {
|
||||||
parseJSON(record._sys.updatedAt),
|
addSuffix: true,
|
||||||
{addSuffix: true}
|
});
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
@@ -23,26 +22,19 @@
|
|||||||
<span class="status">DRAFT</span>
|
<span class="status">DRAFT</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if schema.type === "files"}
|
{#if schema.type === "files"}
|
||||||
<Preview {record} size="tiny" showFilename={true}/>
|
<Preview {record} size="tiny" showFilename={true} />
|
||||||
{:else}
|
{:else}
|
||||||
<a
|
<a href="{channel.lucentUrl}/records/{record.id}">
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
|
||||||
|
|
||||||
>
|
|
||||||
{previewTitle(channel.schemas, record, graph)}
|
{previewTitle(channel.schemas, record, graph)}
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td><a
|
<td><a href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a> </td>
|
||||||
href="{channel.lucentUrl}/content/{schema.name}">{schema.label}</a
|
|
||||||
>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<div style="display: flex;gap: 14px">
|
<div style="display: flex;gap: 14px">
|
||||||
<Avatar name={usernameById(users, record._sys.updatedBy)} side={24}/>
|
<Avatar name={usernameById(users, record.updatedBy)} side={24} />
|
||||||
<div class="ms-2">
|
<div class="ms-2">
|
||||||
{frieldlyUpdatedAt}
|
{frieldlyUpdatedAt}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
|
// https://codesandbox.io/s/codemirror-remark-editor-4m4z9?file=/src/CodeEditor.js:374-387
|
||||||
import {onDestroy, onMount} from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import {basicSetup, EditorView} from "codemirror";
|
import { basicSetup, EditorView } from "codemirror";
|
||||||
import {autocompletion, completionKeymap} from "@codemirror/autocomplete";
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
||||||
import {Compartment, EditorState} from "@codemirror/state";
|
import { Compartment, EditorState } from "@codemirror/state";
|
||||||
import {keymap} from "@codemirror/view";
|
import { keymap } from "@codemirror/view";
|
||||||
import {indentWithTab} from "@codemirror/commands";
|
import { indentWithTab } from "@codemirror/commands";
|
||||||
import {markdown} from "@codemirror/lang-markdown";
|
import { markdown } from "@codemirror/lang-markdown";
|
||||||
import {lintKeymap} from "@codemirror/lint";
|
import { lintKeymap } from "@codemirror/lint";
|
||||||
|
|
||||||
let parentElement;
|
let parentElement;
|
||||||
let codeMirrorView;
|
let codeMirrorView;
|
||||||
@@ -17,10 +16,10 @@
|
|||||||
|
|
||||||
export function insertMedia(info) {
|
export function insertMedia(info) {
|
||||||
let insertText = "";
|
let insertText = "";
|
||||||
if (info.record._file.width > 0) {
|
if (info.file.width > 0) {
|
||||||
insertText = ``;
|
insertText = ``;
|
||||||
} else {
|
} else {
|
||||||
insertText = `[${info.record._file.originalName}](${info.originalUrl})`;
|
insertText = `[${info.file.filename}](${info.originalUrl})`;
|
||||||
}
|
}
|
||||||
const cursor = codeMirrorView.state.selection.main.head;
|
const cursor = codeMirrorView.state.selection.main.head;
|
||||||
const transaction = codeMirrorView.state.update({
|
const transaction = codeMirrorView.state.update({
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
insert: insertText,
|
insert: insertText,
|
||||||
},
|
},
|
||||||
// the next 2 lines will set the appropriate cursor position after inserting the new text.
|
// the next 2 lines will set the appropriate cursor position after inserting the new text.
|
||||||
selection: {anchor: cursor + 1},
|
selection: { anchor: cursor + 1 },
|
||||||
scrollIntoView: true,
|
scrollIntoView: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -46,11 +45,7 @@
|
|||||||
doc: value,
|
doc: value,
|
||||||
extensions: [
|
extensions: [
|
||||||
basicSetup,
|
basicSetup,
|
||||||
keymap.of([
|
keymap.of([indentWithTab, ...lintKeymap, ...completionKeymap]),
|
||||||
indentWithTab,
|
|
||||||
...lintKeymap,
|
|
||||||
...completionKeymap
|
|
||||||
]),
|
|
||||||
language.of(markdown()),
|
language.of(markdown()),
|
||||||
markdown(),
|
markdown(),
|
||||||
autocompletion(),
|
autocompletion(),
|
||||||
@@ -63,17 +58,14 @@
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
EditorView.contentAttributes.of({spellcheck: "true"})
|
EditorView.contentAttributes.of({ spellcheck: "true" }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
codeMirrorView = new EditorView({
|
codeMirrorView = new EditorView({
|
||||||
state,
|
state,
|
||||||
parent: parentElement,
|
parent: parentElement,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@@ -83,4 +75,4 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="is-editable-{editable}" bind:this={parentElement}/>
|
<div class="is-editable-{editable}" bind:this={parentElement} />
|
||||||
|
|||||||
@@ -1,61 +1,60 @@
|
|||||||
<script>
|
<script>
|
||||||
import {onDestroy, onMount} from "svelte";
|
import { onDestroy, onMount } from "svelte";
|
||||||
import Trix from "trix"
|
import Trix from "trix";
|
||||||
import "trix/dist/trix.css"
|
import "trix/dist/trix.css";
|
||||||
|
|
||||||
export let value = "";
|
export let value = "";
|
||||||
export let field;
|
export let field;
|
||||||
let editor;
|
let editor;
|
||||||
|
|
||||||
|
|
||||||
function updateValue(e) {
|
function updateValue(e) {
|
||||||
value = e.target.value;
|
value = e.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertMedia(info){
|
export function insertMedia(info) {
|
||||||
if(info.record._file.width > 0){
|
if (info.file.width > 0) {
|
||||||
var attachment = new Trix.Attachment({ content: info.html })
|
var attachment = new Trix.Attachment({ content: info.html });
|
||||||
editor.editor.insertAttachment(attachment)
|
editor.editor.insertAttachment(attachment);
|
||||||
}else{
|
} else {
|
||||||
editor.editor.insertHTML(`<a href="${info.originalUrl}">${info.record._file.originalName}</a>`)
|
editor.editor.insertHTML(
|
||||||
|
`<a href="${info.originalUrl}">${info.file.filename}</a>`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
editor.addEventListener("trix-file-accept", (e) => {
|
editor.addEventListener("trix-file-accept", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
})
|
});
|
||||||
|
|
||||||
editor.addEventListener("trix-before-initialize", (e) => {
|
editor.addEventListener("trix-before-initialize", (e) => {
|
||||||
Trix.config.blockAttributes.heading1.tagName = 'h2';
|
Trix.config.blockAttributes.heading1.tagName = "h2";
|
||||||
const { toolbarElement } = e.target
|
const { toolbarElement } = e.target;
|
||||||
const h1Button = toolbarElement.querySelector("[data-trix-attribute=heading1]")
|
const h1Button = toolbarElement.querySelector(
|
||||||
h1Button.insertAdjacentHTML("afterend", `<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`)
|
"[data-trix-attribute=heading1]",
|
||||||
})
|
);
|
||||||
|
h1Button.insertAdjacentHTML(
|
||||||
|
"afterend",
|
||||||
})
|
`<button style="text-indent: initial;padding: 14px 10px !important;" type="button" class="trix-button trix-button--icon" data-trix-attribute="heading3" title="Heading 3" tabindex="-1" data-trix-active="">H3</button>`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
// onDestroy(() => {
|
// onDestroy(() => {
|
||||||
// editor.removeEventListener("trix-before-initialize")
|
// editor.removeEventListener("trix-before-initialize")
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
Trix.config.blockAttributes.default.breakOnReturn = false;
|
||||||
Trix.config.blockAttributes.default.breakOnReturn = false
|
|
||||||
Trix.config.blockAttributes.heading3 = {
|
Trix.config.blockAttributes.heading3 = {
|
||||||
tagName: 'h3',
|
tagName: "h3",
|
||||||
terminal: true,
|
terminal: true,
|
||||||
breakOnReturn: true,
|
breakOnReturn: true,
|
||||||
group: false
|
group: false,
|
||||||
}
|
};
|
||||||
// console.log(Trix.config)
|
// console.log(Trix.config)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tox-wrapper">
|
<div class="tox-wrapper">
|
||||||
<input id="x-{field.name}" {value} type="hidden">
|
<input id="x-{field.name}" {value} type="hidden" />
|
||||||
<trix-editor
|
<trix-editor
|
||||||
bind:this={editor}
|
bind:this={editor}
|
||||||
class=" content"
|
class=" content"
|
||||||
@@ -63,6 +62,5 @@
|
|||||||
role="textbox"
|
role="textbox"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:trix-change={updateValue}
|
on:trix-change={updateValue}
|
||||||
|
|
||||||
></trix-editor>
|
></trix-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import {afterUpdate, getContext, onMount} from "svelte";
|
import { afterUpdate, getContext, onMount } from "svelte";
|
||||||
import {isEqual} from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import EditHeader from "./header/EditHeader.svelte"
|
import EditHeader from "./header/EditHeader.svelte";
|
||||||
import FilePreview from "./FilePreview.svelte"
|
import ContentTabs from "./header/ContentTabs.svelte";
|
||||||
import ContentTabs from "./header/ContentTabs.svelte"
|
import FormField from "./FormField.svelte";
|
||||||
import FormField from "./FormField.svelte"
|
import Graph from "./Graph.svelte";
|
||||||
import Graph from "./Graph.svelte"
|
import Info from "./Info.svelte";
|
||||||
import Info from "./Info.svelte"
|
import ErrorAlert from "../common/ErrorAlert.svelte";
|
||||||
import ErrorAlert from "../common/ErrorAlert.svelte"
|
|
||||||
import Title from "./header/Title.svelte";
|
import Title from "./header/Title.svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
export let record;
|
export let record;
|
||||||
export let graph = {
|
export let graph = {
|
||||||
records: [],
|
records: [],
|
||||||
edges: []
|
edges: [],
|
||||||
};
|
};
|
||||||
// export let recordHistory;
|
// export let recordHistory;
|
||||||
export let isCreateMode;
|
export let isCreateMode;
|
||||||
@@ -33,10 +32,7 @@
|
|||||||
} error(s)`
|
} error(s)`
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let activeFields = schema.fields.filter(
|
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||||
(f) => f.name !== "id"
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
setOriginalContent();
|
setOriginalContent();
|
||||||
@@ -45,10 +41,7 @@
|
|||||||
function setOriginalContent() {
|
function setOriginalContent() {
|
||||||
originalContent = {
|
originalContent = {
|
||||||
data: JSON.parse(JSON.stringify(record.data)),
|
data: JSON.parse(JSON.stringify(record.data)),
|
||||||
schema: record.schema,
|
|
||||||
status: record.status,
|
status: record.status,
|
||||||
_sys: JSON.parse(JSON.stringify(record._sys)),
|
|
||||||
_file: JSON.parse(JSON.stringify(record._file)),
|
|
||||||
edges: JSON.parse(JSON.stringify(graph.edges)),
|
edges: JSON.parse(JSON.stringify(graph.edges)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -79,10 +72,7 @@
|
|||||||
}
|
}
|
||||||
return !isEqual(originalContent, {
|
return !isEqual(originalContent, {
|
||||||
data: record.data,
|
data: record.data,
|
||||||
schema: record.schema,
|
|
||||||
status: record.status,
|
status: record.status,
|
||||||
_sys: record._sys,
|
|
||||||
_file: record._file,
|
|
||||||
edges: graph.edges,
|
edges: graph.edges,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -104,7 +94,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove trashed edges
|
// remove trashed edges
|
||||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id);
|
graph.edges = graph.edges?.filter(
|
||||||
|
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||||
|
);
|
||||||
axios
|
axios
|
||||||
.post(channel.lucentUrl + "/records", {
|
.post(channel.lucentUrl + "/records", {
|
||||||
record: record,
|
record: record,
|
||||||
@@ -115,7 +107,8 @@
|
|||||||
console.log("SAVE: SAVED");
|
console.log("SAVE: SAVED");
|
||||||
|
|
||||||
if (isCreateMode) {
|
if (isCreateMode) {
|
||||||
window.location = channel.lucentUrl + "/records/" + record.id;
|
window.location =
|
||||||
|
channel.lucentUrl + "/records/" + record.id;
|
||||||
} else {
|
} else {
|
||||||
record = response.data.records[0] ?? null;
|
record = response.data.records[0] ?? null;
|
||||||
if (!record) {
|
if (!record) {
|
||||||
@@ -137,7 +130,7 @@
|
|||||||
errorMessage = error.response.data.error;
|
errorMessage = error.response.data.error;
|
||||||
} else {
|
} else {
|
||||||
validationErrors = error.response.data.error;
|
validationErrors = error.response.data.error;
|
||||||
console.log(validationErrors)
|
console.log(validationErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@@ -149,17 +142,14 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:beforeunload={beforeUnload}/>
|
<svelte:window on:beforeunload={beforeUnload} />
|
||||||
|
|
||||||
<div class="record-edit">
|
<div class="record-edit">
|
||||||
<div class="tools-header">
|
<div class="tools-header">
|
||||||
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
|
<!-- <Manager managerRecords={recordHistory} {graph}/>-->
|
||||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||||
{#if isCreateMode}
|
{#if isCreateMode}
|
||||||
<button
|
<button class="button primary btn-spinner" on:click={save}>
|
||||||
class="button primary btn-spinner"
|
|
||||||
on:click={save}
|
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
class="spinner-border spinner-border-sm"
|
class="spinner-border spinner-border-sm"
|
||||||
role="status"
|
role="status"
|
||||||
@@ -181,26 +171,19 @@
|
|||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Title {schema} {record} {isCreateMode}/>
|
<Title {schema} {record} {isCreateMode} />
|
||||||
|
|
||||||
|
<ErrorAlert message={errorMessage} />
|
||||||
<ErrorAlert message={errorMessage}/>
|
|
||||||
|
|
||||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||||
<ContentTabs
|
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||||
{schema}
|
|
||||||
{isCreateMode}
|
|
||||||
bind:active={activeContentTab}
|
|
||||||
/>
|
|
||||||
{#if !["_graph", "_info"].includes(activeContentTab)}
|
{#if !["_graph", "_info"].includes(activeContentTab)}
|
||||||
<FilePreview {record} {schema}/>
|
|
||||||
{#each activeFields as field (field.name)}
|
{#each activeFields as field (field.name)}
|
||||||
{#if activeContentTab === field.group}
|
{#if activeContentTab === field.group}
|
||||||
<FormField
|
<FormField
|
||||||
bind:data={record.data}
|
bind:data={record.data}
|
||||||
bind:graph={graph}
|
bind:graph
|
||||||
{field}
|
{field}
|
||||||
{schema}
|
{schema}
|
||||||
{record}
|
{record}
|
||||||
@@ -210,9 +193,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else if activeContentTab === "_graph"}
|
{:else if activeContentTab === "_graph"}
|
||||||
<Graph {graph} {record}/>
|
<Graph {graph} {record} />
|
||||||
{:else if activeContentTab === "_info"}
|
{:else if activeContentTab === "_info"}
|
||||||
<Info {record} {graph} {users} {schema}/>
|
<Info {record} {graph} {users} {schema} />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -45,25 +45,20 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="editor-field">
|
<div class="editor-field">
|
||||||
<FieldHeader {field} {id}/>
|
<FieldHeader {field} {id} />
|
||||||
{#if field.info.name === "reference" && field.layout === "tags"}
|
{#if field.info.name === "reference" && field.layout === "tags"}
|
||||||
<ReferenceTags
|
<ReferenceTags bind:graph {id} {record} {field} {validationErrors} />
|
||||||
bind:graph
|
|
||||||
{id}
|
|
||||||
{record}
|
|
||||||
{field}
|
|
||||||
{validationErrors}
|
|
||||||
/>
|
|
||||||
{:else if field.info.name === "reference"}
|
{:else if field.info.name === "reference"}
|
||||||
<Reference
|
<Reference bind:graph {id} {record} {field} {validationErrors} />
|
||||||
bind:graph
|
{:else if field.info.name === "file"}
|
||||||
{id}
|
<!-- <File bind:graph {record} {field} {validationErrors} /> -->
|
||||||
|
<File
|
||||||
|
bind:value={data[field.name]}
|
||||||
{record}
|
{record}
|
||||||
|
{id}
|
||||||
{field}
|
{field}
|
||||||
{validationErrors}
|
{validationErrors}
|
||||||
/>
|
/>
|
||||||
{:else if field.info.name === "file"}
|
|
||||||
<File bind:graph {record} {field} {validationErrors}/>
|
|
||||||
{:else if field.info.name === "text"}
|
{:else if field.info.name === "text"}
|
||||||
<Text
|
<Text
|
||||||
bind:value={data[field.name]}
|
bind:value={data[field.name]}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import {friendlyDate} from "../../helpers";
|
import { friendlyDate } from "../../helpers";
|
||||||
import Avatar from "../account/Avatar.svelte";
|
import Avatar from "../account/Avatar.svelte";
|
||||||
import {usernameById} from "../account/users";
|
import { usernameById } from "../account/users";
|
||||||
import {isEqual} from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import Icon from "../common/Icon.svelte";
|
import Icon from "../common/Icon.svelte";
|
||||||
import RevisionCell from "./revisions/RevisionCell.svelte";
|
import RevisionCell from "./revisions/RevisionCell.svelte";
|
||||||
import {getContext} from "svelte";
|
import { getContext } from "svelte";
|
||||||
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
|
import RevisionEdgeRow from "./revisions/RevisionEdgeRow.svelte";
|
||||||
|
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
@@ -30,27 +30,27 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
function getEdgesByField(fieldsWithDiff, revision) {
|
function getEdgesByField(fieldsWithDiff, revision) {
|
||||||
|
edgeFieldsDiff = graph.edges
|
||||||
edgeFieldsDiff = graph.edges.filter((e) => e.depth === 1).reduce((c, e) => {
|
.filter((e) => e.depth === 1)
|
||||||
|
.reduce((c, e) => {
|
||||||
if (!c[e.field]) {
|
if (!c[e.field]) {
|
||||||
c[e.field] = {
|
c[e.field] = {
|
||||||
record: [],
|
record: [],
|
||||||
revision: [],
|
revision: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
c[e.field]["record"].push(e);
|
||||||
c[e.field]["record"].push(e)
|
|
||||||
return c;
|
return c;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
||||||
edgeFieldsDiff = revision._edges.reduce((c, e) => {
|
edgeFieldsDiff = revision._edges.reduce((c, e) => {
|
||||||
if (!c[e.field]) {
|
if (!c[e.field]) {
|
||||||
c[e.field] = {
|
c[e.field] = {
|
||||||
record: [],
|
record: [],
|
||||||
revision: [],
|
revision: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
c[e.field]["revision"].push(e);
|
||||||
c[e.field]["revision"].push(e)
|
|
||||||
return c;
|
return c;
|
||||||
}, edgeFieldsDiff);
|
}, edgeFieldsDiff);
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
fieldsWithDiff = schema.fields.filter((f) => {
|
fieldsWithDiff = schema.fields.filter((f) => {
|
||||||
return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
|
return !isEqual(selectedRevision.data[f.name], record.data[f.name]);
|
||||||
});
|
});
|
||||||
getEdgesByField(fieldsWithDiff, revision)
|
getEdgesByField(fieldsWithDiff, revision);
|
||||||
revisionSection.scrollIntoView();
|
revisionSection.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
rollbackError = "";
|
rollbackError = "";
|
||||||
axios
|
axios
|
||||||
.post(
|
.post(
|
||||||
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision._sys.version}`
|
`${channel.lucentUrl}/records/${record.id}/rollback/${selectedRevision.version}`,
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="lx-card ">
|
<div class="lx-card">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<div>
|
<div>
|
||||||
@@ -93,29 +93,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="label text-end text-muted">current version </span>
|
<span class="label text-end text-muted">current version </span>
|
||||||
{record._sys.version}
|
{record.version}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="label text-end text-muted"> created </span>
|
<span class="label text-end text-muted"> created </span>
|
||||||
<Avatar
|
<Avatar
|
||||||
name={usernameById(users, record._sys.createdBy)}
|
name={usernameById(users, record.createdBy)}
|
||||||
side={24}
|
side={24}
|
||||||
/>
|
/>
|
||||||
{friendlyDate(record._sys.createdAt)}
|
{friendlyDate(record.createdAt)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="label text-end text-muted">updated </span>
|
<span class="label text-end text-muted">updated </span>
|
||||||
<Avatar
|
<Avatar
|
||||||
name={usernameById(users, record._sys.updatedBy)}
|
name={usernameById(users, record.updatedBy)}
|
||||||
side={24}
|
side={24}
|
||||||
/>
|
/>
|
||||||
{friendlyDate(record._sys.updatedAt)}
|
{friendlyDate(record.updatedAt)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="label d-block text-muted "
|
<span class="label d-block text-muted">Rules for this schema </span>
|
||||||
>Rules for this schema
|
|
||||||
</span>
|
|
||||||
<small>
|
<small>
|
||||||
Each record maintains the last {schema.revisions}
|
Each record maintains the last {schema.revisions}
|
||||||
versions
|
versions
|
||||||
@@ -127,31 +125,29 @@
|
|||||||
{#if schema.revisions > 0}
|
{#if schema.revisions > 0}
|
||||||
<div class="header-small mb-3">Revisions</div>
|
<div class="header-small mb-3">Revisions</div>
|
||||||
{#each revisions as revision}
|
{#each revisions as revision}
|
||||||
{#if revision._sys.version !== record._sys.version}
|
{#if revision.version !== record.version}
|
||||||
<div
|
<div
|
||||||
class="revision"
|
class="revision"
|
||||||
class:active={revision._sys.version ===
|
class:active={revision.version ===
|
||||||
selectedRevision?._sys.version}
|
selectedRevision?.version}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div class="version">
|
<div class="version">
|
||||||
<span>version {revision._sys.version}</span>
|
<span>version {revision.version}</span>
|
||||||
<Avatar
|
<Avatar
|
||||||
name={usernameById(users, revision._sys.updatedBy)}
|
name={usernameById(users, revision.updatedBy)}
|
||||||
side={24}
|
side={24}
|
||||||
/>
|
/>
|
||||||
{friendlyDate(revision._sys.updatedAt)}
|
{friendlyDate(revision.updatedAt)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-3 text-center">
|
<div class="col-3 text-center">
|
||||||
<button
|
<button
|
||||||
disabled={revision._sys.version ===
|
disabled={revision.version ===
|
||||||
selectedRevision?._sys.version}
|
selectedRevision?.version}
|
||||||
class="button"
|
class="button"
|
||||||
on:click={(e) => compare(e, revision)}
|
on:click={(e) => compare(e, revision)}
|
||||||
>Compare
|
>Compare
|
||||||
</button
|
</button>
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -169,15 +165,13 @@
|
|||||||
<p class="text-center fw-bold mb-3 mt-5">
|
<p class="text-center fw-bold mb-3 mt-5">
|
||||||
If you choose to rollback to this revision
|
If you choose to rollback to this revision
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button on:click={rollback} class="button">
|
||||||
on:click={rollback}
|
Rollback to version {selectedRevision.version}
|
||||||
class="button"
|
|
||||||
>
|
|
||||||
Rollback to version {selectedRevision._sys.version}
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{#if rollbackError}
|
{#if rollbackError}
|
||||||
<span class="d-block text-danger mt-3">{rollbackError}</span>
|
<span class="d-block text-danger mt-3">{rollbackError}</span
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
{#each fieldsWithDiff as field}
|
{#each fieldsWithDiff as field}
|
||||||
@@ -188,10 +182,7 @@
|
|||||||
<!-- <div class="d-block" style="width:200px;">
|
<!-- <div class="d-block" style="width:200px;">
|
||||||
{field.label}
|
{field.label}
|
||||||
</div> -->
|
</div> -->
|
||||||
<div
|
<div class="revision-field" style="overflow:hidden">
|
||||||
class="revision-field"
|
|
||||||
style="overflow:hidden"
|
|
||||||
>
|
|
||||||
<div class="compare-left">
|
<div class="compare-left">
|
||||||
<RevisionCell
|
<RevisionCell
|
||||||
{field}
|
{field}
|
||||||
@@ -226,22 +217,16 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<p class="text-center fw-bold mb-3 mt-5">
|
<p class="text-center fw-bold mb-3 mt-5">Record References</p>
|
||||||
Record References
|
|
||||||
</p>
|
|
||||||
{#each Object.entries(edgeFieldsDiff) as [field, edges]}
|
{#each Object.entries(edgeFieldsDiff) as [field, edges]}
|
||||||
<div
|
<div class="revision-references" style="overflow:hidden">
|
||||||
class="revision-references"
|
|
||||||
style="overflow:hidden"
|
|
||||||
>
|
|
||||||
<div class="reference-field">
|
<div class="reference-field">
|
||||||
{field}:
|
{field}:
|
||||||
</div>
|
</div>
|
||||||
<div class="reference-compare">
|
<div class="reference-compare">
|
||||||
|
|
||||||
<p class="">Record</p>
|
<p class="">Record</p>
|
||||||
{#each edges.record as edge}
|
{#each edges.record as edge}
|
||||||
<RevisionEdgeRow {edge}/>
|
<RevisionEdgeRow {edge} />
|
||||||
{:else}
|
{:else}
|
||||||
<p>No references</p>
|
<p>No references</p>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -249,7 +234,7 @@
|
|||||||
<div class="reference-compare">
|
<div class="reference-compare">
|
||||||
<p class="text-success">Revision</p>
|
<p class="text-success">Revision</p>
|
||||||
{#each edges.revision as edge}
|
{#each edges.revision as edge}
|
||||||
<RevisionEdgeRow {edge}/>
|
<RevisionEdgeRow {edge} />
|
||||||
{:else}
|
{:else}
|
||||||
<p>No references</p>
|
<p>No references</p>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -258,7 +243,5 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import {afterUpdate, createEventDispatcher, getContext, onMount} from "svelte";
|
import {
|
||||||
|
afterUpdate,
|
||||||
|
createEventDispatcher,
|
||||||
|
getContext,
|
||||||
|
onMount,
|
||||||
|
} from "svelte";
|
||||||
|
|
||||||
import {isEqual} from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import FormField from "./FormField.svelte";
|
import FormField from "./FormField.svelte";
|
||||||
import FilePreview from "./FilePreview.svelte";
|
import FilePreview from "./FilePreview.svelte";
|
||||||
import ContentTabs from "./header/ContentTabs.svelte";
|
import ContentTabs from "./header/ContentTabs.svelte";
|
||||||
@@ -16,7 +21,7 @@
|
|||||||
export let record;
|
export let record;
|
||||||
export let graph = {
|
export let graph = {
|
||||||
records: [],
|
records: [],
|
||||||
edges: []
|
edges: [],
|
||||||
};
|
};
|
||||||
export let isCreateMode;
|
export let isCreateMode;
|
||||||
let originalContent;
|
let originalContent;
|
||||||
@@ -29,9 +34,7 @@
|
|||||||
} error(s)`
|
} error(s)`
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let activeFields = schema.fields.filter(
|
let activeFields = schema.fields.filter((f) => f.name !== "id");
|
||||||
(f) => f.name !== "id"
|
|
||||||
);
|
|
||||||
|
|
||||||
let tabname = "_default";
|
let tabname = "_default";
|
||||||
let fieldToTabs = schema.fields.reduce((c, f) => {
|
let fieldToTabs = schema.fields.reduce((c, f) => {
|
||||||
@@ -53,8 +56,6 @@
|
|||||||
data: JSON.parse(JSON.stringify(record.data)),
|
data: JSON.parse(JSON.stringify(record.data)),
|
||||||
schema: record.schema,
|
schema: record.schema,
|
||||||
status: record.status,
|
status: record.status,
|
||||||
_sys: JSON.parse(JSON.stringify(record._sys)),
|
|
||||||
_file: JSON.parse(JSON.stringify(record._file)),
|
|
||||||
edges: JSON.parse(JSON.stringify(graph.edges)),
|
edges: JSON.parse(JSON.stringify(graph.edges)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -87,8 +88,6 @@
|
|||||||
data: record.data,
|
data: record.data,
|
||||||
schema: record.schema,
|
schema: record.schema,
|
||||||
status: record.status,
|
status: record.status,
|
||||||
_sys: record._sys,
|
|
||||||
_file: record._file,
|
|
||||||
edges: graph.edges,
|
edges: graph.edges,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -114,8 +113,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// remove trashed edges
|
// remove trashed edges
|
||||||
graph.edges = graph.edges?.filter((edge) => !edge._isTrashed && edge.source === record.id) ?? [];
|
graph.edges =
|
||||||
|
graph.edges?.filter(
|
||||||
|
(edge) => !edge._isTrashed && edge.source === record.id,
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
axios
|
axios
|
||||||
.post(channel.lucentUrl + "/records", {
|
.post(channel.lucentUrl + "/records", {
|
||||||
@@ -150,16 +151,13 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:beforeunload={beforeUnload}/>
|
<svelte:window on:beforeunload={beforeUnload} />
|
||||||
|
|
||||||
<div class="inline-edit record-edit">
|
<div class="inline-edit record-edit">
|
||||||
<div class="tools-header">
|
<div class="tools-header">
|
||||||
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab/>
|
<EditHeader {schema} bind:record {isCreateMode} bind:activeContentTab />
|
||||||
{#if isCreateMode}
|
{#if isCreateMode}
|
||||||
<button
|
<button class="button primary btn-spinner" on:click={save}>
|
||||||
class="button primary btn-spinner"
|
|
||||||
on:click={save}
|
|
||||||
>
|
|
||||||
<span
|
<span
|
||||||
class="spinner-border spinner-border-sm"
|
class="spinner-border spinner-border-sm"
|
||||||
role="status"
|
role="status"
|
||||||
@@ -181,24 +179,19 @@
|
|||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Title {schema} {record} {isCreateMode}/>
|
<Title {schema} {record} {isCreateMode} />
|
||||||
<ErrorAlert message={errorMessage}/>
|
<ErrorAlert message={errorMessage} />
|
||||||
|
|
||||||
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
<div class=" mt-4" style="margin-bottom:150px;position:relative;">
|
||||||
<ContentTabs
|
<ContentTabs {schema} {isCreateMode} bind:active={activeContentTab} />
|
||||||
{schema}
|
<FilePreview {record} {schema} />
|
||||||
{isCreateMode}
|
|
||||||
bind:active={activeContentTab}
|
|
||||||
/>
|
|
||||||
<FilePreview {record} {schema}/>
|
|
||||||
<!-- <fieldset disabled="disabled"> -->
|
<!-- <fieldset disabled="disabled"> -->
|
||||||
{#each activeFields as field (field.name)}
|
{#each activeFields as field (field.name)}
|
||||||
{#if activeContentTab === field.group}
|
{#if activeContentTab === field.group}
|
||||||
<FormField
|
<FormField
|
||||||
bind:data={record.data}
|
bind:data={record.data}
|
||||||
bind:graph={graph}
|
bind:graph
|
||||||
{field}
|
{field}
|
||||||
{schema}
|
{schema}
|
||||||
{record}
|
{record}
|
||||||
@@ -210,4 +203,3 @@
|
|||||||
<!-- </fieldset> -->
|
<!-- </fieldset> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Mustache from "mustache";
|
import Mustache from "mustache";
|
||||||
import {stripHtml} from "../../helpers";
|
import { stripHtml } from "../../helpers";
|
||||||
|
|
||||||
export function previewTitle(schemas, record, graph) {
|
export function previewTitle(schemas, record, graph) {
|
||||||
let schema = schemas.find((aSchema) => aSchema.name === record?.schema);
|
let schema = schemas.find((aSchema) => aSchema.name === record?.schema);
|
||||||
@@ -18,14 +18,14 @@ export function previewTitle(schemas, record, graph) {
|
|||||||
|
|
||||||
function noTemplate(schema, record) {
|
function noTemplate(schema, record) {
|
||||||
if (schema?.type === "files") {
|
if (schema?.type === "files") {
|
||||||
return record._file.path;
|
return file.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = stripHtml(
|
let title = stripHtml(
|
||||||
record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name]
|
record?.data[schema.fields.filter((f) => f.info.name === "text")[0]?.name],
|
||||||
).slice(0, 300);
|
).slice(0, 300);
|
||||||
|
|
||||||
if(title.trim() === ""){
|
if (title.trim() === "") {
|
||||||
return "~Untitled~";
|
return "~Untitled~";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { sortByField } from "../../edges/sortEdges";
|
import { array_move } 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 Dialog from "../../dialog/Dialog.svelte";
|
|
||||||
import { insertEdges } from "./reference.js";
|
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
import FileDialog from "../../dialog/FileDialog.svelte";
|
import FileDialog from "../../dialog/FileDialog.svelte";
|
||||||
import Uploader from "../../files/Uploader.svelte";
|
import Uploader from "../../files/Uploader.svelte";
|
||||||
@@ -12,36 +9,28 @@
|
|||||||
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 value = [];
|
||||||
let browseModal;
|
let browseModal;
|
||||||
|
|
||||||
function removeReference(e) {
|
function removeFile(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
graph.edges = graph.edges.filter(
|
value = value.filter((f) => !(f.id === e.detail));
|
||||||
(edge) => !(edge.target === e.detail && edge.field === field.name),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reorder(e) {
|
async function reorder(e) {
|
||||||
graph.edges = await sortByField(
|
value = await array_move(value, e.detail.source, e.detail.target);
|
||||||
e.detail.source,
|
|
||||||
e.detail.target,
|
|
||||||
graph.edges,
|
|
||||||
field.name,
|
|
||||||
references,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert(e) {
|
function insertFiles(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
browseModal.close();
|
browseModal.close();
|
||||||
graph = insertEdges(
|
value = [...(value ?? []), ...(e.detail ?? [])];
|
||||||
graph,
|
}
|
||||||
record,
|
|
||||||
e.detail.records,
|
function replaceFiles(e) {
|
||||||
field.name,
|
e.preventDefault();
|
||||||
e.detail.action,
|
browseModal.close();
|
||||||
);
|
value = e.detail ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadComplete(e) {
|
function uploadComplete(e) {
|
||||||
@@ -61,18 +50,22 @@
|
|||||||
<Uploader recordId={record.id} on:uploadComplete={uploadComplete} />
|
<Uploader recordId={record.id} on:uploadComplete={uploadComplete} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- {#if references.length > 0}
|
{#if value.length > 0}
|
||||||
<Sortable sortableClass="mt-3" on:update={reorder}>
|
<Sortable sortableClass="mt-3" on:update={reorder}>
|
||||||
{#each references as reference (reference.id)}
|
{#each value ?? [] as aFile (aFile.id)}
|
||||||
<!--This div helps the sorting thing-->
|
<!--This div helps the sorting thing-->
|
||||||
<!-- <div>
|
<div>
|
||||||
<PreviewFile
|
<PreviewFile
|
||||||
record={reference}
|
file={aFile}
|
||||||
hasDelete={true}
|
hasDelete={true}
|
||||||
on:remove={removeReference}
|
on:remove_file={removeFile}
|
||||||
></PreviewFile>
|
></PreviewFile>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</Sortable>
|
</Sortable>
|
||||||
{/if} -->
|
{/if}
|
||||||
<FileDialog bind:this={browseModal} on:insert={insert}></FileDialog>
|
<FileDialog
|
||||||
|
bind:this={browseModal}
|
||||||
|
on:insert_files={insertFiles}
|
||||||
|
on:replace_files={replaceFiles}
|
||||||
|
></FileDialog>
|
||||||
|
|||||||
@@ -1,91 +1,80 @@
|
|||||||
<script>
|
<script>
|
||||||
import Icon from "../../common/Icon.svelte";
|
import Icon from "../../common/Icon.svelte";
|
||||||
|
|
||||||
import {createEventDispatcher, getContext} from "svelte";
|
import { createEventDispatcher, getContext } from "svelte";
|
||||||
import Preview from "../../files/Preview.svelte";
|
import Preview from "../../files/Preview.svelte";
|
||||||
import {previewTitle} from "./../Preview";
|
import { previewTitle } from "./../Preview";
|
||||||
import {fileurl, htmlurl} from "../../files/imageserver.js"
|
import { fileurl, htmlurl } from "../../files/imageserver.js";
|
||||||
import Status from "./../Status.svelte";
|
import Status from "./../Status.svelte";
|
||||||
import Dropdown from "../../common/Dropdown.svelte";
|
import Dropdown from "../../common/Dropdown.svelte";
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const channel = getContext("channel");
|
const channel = getContext("channel");
|
||||||
export let record;
|
export let file;
|
||||||
export let hasDelete = false;
|
export let hasDelete = false;
|
||||||
export let hasInsert = false;
|
export let hasInsert = false;
|
||||||
|
|
||||||
let schema = channel.schemas.find((aschema) => aschema.name === record.schema);
|
|
||||||
let cardTitle = previewTitle(channel.schemas, record);
|
|
||||||
let imagePresets = Object.keys(channel.imageFilters);
|
let imagePresets = Object.keys(channel.imageFilters);
|
||||||
|
|
||||||
function remove(e) {
|
function remove(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dispatch("remove", record.id);
|
dispatch("remove_file", file.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insert(e, preset) {
|
function insert(e, preset) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let html = htmlurl(channel, record, preset)
|
// let html = htmlurl(channel, record, preset);
|
||||||
let url = !preset ? `/${record._file.path}` : `/templates/${preset}/${record._file.path}`;
|
// let url = !preset
|
||||||
dispatch("editor-insert", {
|
// ? `/${record._file.path}`
|
||||||
html: html,
|
// : `/templates/${preset}/${record._file.path}`;
|
||||||
url: channel.filesUrl + url,
|
// dispatch("editor-insert", {
|
||||||
originalUrl: channel.filesUrl + "/" + record._file.path,
|
// html: html,
|
||||||
record: record
|
// url: channel.filesUrl + url,
|
||||||
});
|
// originalUrl: channel.filesUrl + "/" + record._file.path,
|
||||||
|
// record: record,
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="preview-file">
|
<div class="preview-file">
|
||||||
<div style="display: flex;align-items: center;gap: 10px;">
|
<div style="display: flex;align-items: center;gap: 10px;">
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<Preview {record} size="small"/>
|
<Preview {file} size="small" />
|
||||||
</div>
|
</div>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div>
|
<div>
|
||||||
<a
|
{file.filename}
|
||||||
class="record-title"
|
</div>
|
||||||
href="{channel.lucentUrl}/records/{record.id}"
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="display: flex;gap:4px; align-items: center; margin-right: 10px;"
|
||||||
>
|
>
|
||||||
{cardTitle}
|
|
||||||
</a>
|
|
||||||
<small class="d-block">
|
|
||||||
from {schema.label}
|
|
||||||
{#if record.status === "draft"}
|
|
||||||
<Status status={record.status}/>
|
|
||||||
{/if}
|
|
||||||
</small>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex;gap:4px; align-items: center; margin-right: 10px;">
|
|
||||||
{#if hasInsert}
|
{#if hasInsert}
|
||||||
<div class="reference-action">
|
<div class="reference-action">
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<div slot="button">
|
<div slot="button">
|
||||||
<Icon icon="photo-film"/>
|
<Icon icon="photo-film" />
|
||||||
</div>
|
</div>
|
||||||
<button class="dropdown-item button" on:click={e => insert(e,null)}>original</button>
|
<button
|
||||||
|
class="dropdown-item button"
|
||||||
|
on:click={(e) => insert(e, null)}>original</button
|
||||||
|
>
|
||||||
{#each imagePresets as preset}
|
{#each imagePresets as preset}
|
||||||
<button class="dropdown-item button" on:click={e => insert(e,preset)}>{preset}</button>
|
<button
|
||||||
|
class="dropdown-item button"
|
||||||
|
on:click={(e) => insert(e, preset)}>{preset}</button
|
||||||
|
>
|
||||||
{/each}
|
{/each}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if hasDelete}
|
{#if hasDelete}
|
||||||
<div class="reference-action">
|
<div class="reference-action">
|
||||||
<button
|
<button class="button" on:click={remove}>
|
||||||
class="button"
|
<Icon icon="trash-can" />
|
||||||
on:click={remove}
|
|
||||||
>
|
|
||||||
<Icon icon="trash-can"/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -12,22 +12,9 @@
|
|||||||
<div class="{colorClass} field-content">
|
<div class="{colorClass} field-content">
|
||||||
<div class="d-flex align-items-center text-center flex-wrap">
|
<div class="d-flex align-items-center text-center flex-wrap">
|
||||||
{#each edges[field.name] as edgeRecord}
|
{#each edges[field.name] as edgeRecord}
|
||||||
{#if edgeRecord._file?.path}
|
<div class="ms-2">
|
||||||
<div
|
<PreviewCardSmall record={edgeRecord} />
|
||||||
class="ms-2 "
|
|
||||||
style="max-width:64px;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;"
|
|
||||||
>
|
|
||||||
<Preview
|
|
||||||
record={edgeRecord}
|
|
||||||
size="small"
|
|
||||||
showFilename={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<div class="ms-2 ">
|
|
||||||
<PreviewCardSmall record={edgeRecord}/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,7 +30,6 @@
|
|||||||
|
|
||||||
<!-- {/if} -->
|
<!-- {/if} -->
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.field-content {
|
.field-content {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ namespace Lucent\Channel;
|
|||||||
|
|
||||||
use Lucent\Channel\Data\UserCommand;
|
use Lucent\Channel\Data\UserCommand;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
use Lucent\Schema\FilesSchema;
|
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Schema\Schema;
|
||||||
|
|
||||||
final class Channel
|
final class Channel
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Lucent\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Lucent\Primitive\Collection;
|
|
||||||
use Lucent\Schema\FilesSchema;
|
|
||||||
|
|
||||||
class GenerateFileSchema extends Command
|
|
||||||
{
|
|
||||||
|
|
||||||
protected $signature = 'lucent:generate:file {name}';
|
|
||||||
|
|
||||||
protected $description = 'Generate a lucent file schema';
|
|
||||||
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$name = $this->argument('name');
|
|
||||||
$schema = new FilesSchema(
|
|
||||||
name: $name,
|
|
||||||
label: $name,
|
|
||||||
fields: Collection::make(),
|
|
||||||
disk: "lucent",
|
|
||||||
path: $name,
|
|
||||||
groups: []
|
|
||||||
);
|
|
||||||
|
|
||||||
$json = json_encode($schema, JSON_PRETTY_PRINT);
|
|
||||||
$configDir = base_path(config('lucent.schemas_path'));
|
|
||||||
$schemaPath = $configDir . "/" . $name . '.json';
|
|
||||||
|
|
||||||
if (file_exists($schemaPath)) {
|
|
||||||
$this->error("The schema file already exists.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents($schemaPath, $json);
|
|
||||||
$this->info("The schema file has been created.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ use Exception;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
use Lucent\Channel\ChannelService;
|
use Lucent\Channel\ChannelService;
|
||||||
|
use Lucent\File\FileRepo;
|
||||||
use Lucent\File\FileService;
|
use Lucent\File\FileService;
|
||||||
use Lucent\Query\Query;
|
use Lucent\Query\Query;
|
||||||
use Lucent\Schema\FilesSchema;
|
use Lucent\Schema\FilesSchema;
|
||||||
@@ -26,35 +27,16 @@ class RebuildThumbnails extends Command
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(ChannelService $channelService): int
|
public function handle(): void
|
||||||
{
|
{
|
||||||
$channelService->channel->schemas
|
$this->info("Rebuilding thumbnails ");
|
||||||
->filter(
|
$files = FileRepo::query()->get();
|
||||||
fn(Schema $schema) => get_class($schema) === FilesSchema::class,
|
$disk = $this->fileService->loadDisk();
|
||||||
)
|
foreach ($files as $file) {
|
||||||
->map([$this, "rebuildThumbnails"]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rebuildThumbnails(FilesSchema $schema): void
|
|
||||||
{
|
|
||||||
$this->info("Rebuilding thumbnails for " . $schema->name);
|
|
||||||
$records = $this->query
|
|
||||||
->limit(0)
|
|
||||||
->filter(["schema" => $schema->name])
|
|
||||||
->run()->records;
|
|
||||||
$disk = $this->fileService->loadDisk($schema->disk);
|
|
||||||
foreach ($records as $record) {
|
|
||||||
try {
|
try {
|
||||||
$this->fileService->createTemplates(
|
$this->fileService->createTemplates($disk, $file->path);
|
||||||
$disk,
|
|
||||||
$record->_file->path,
|
|
||||||
);
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo "File " .
|
echo "File " . $file->filename . " could not be rebuilt \n";
|
||||||
$record->_file->originalName .
|
|
||||||
" could not be rebuilt \n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,19 +71,23 @@ class SetupDatabase extends Command
|
|||||||
) {
|
) {
|
||||||
$table->uuid("id")->primary();
|
$table->uuid("id")->primary();
|
||||||
$table->string("schema");
|
$table->string("schema");
|
||||||
|
$table->integer("version");
|
||||||
$table->string("status");
|
$table->string("status");
|
||||||
$table->jsonb("data");
|
$table->jsonb("data");
|
||||||
$table->jsonb("_sys");
|
$table->timestampTz("createdAt");
|
||||||
|
$table->timestampTz("updatedAt");
|
||||||
|
$table->string("createdBy");
|
||||||
|
$table->string("updatedBy");
|
||||||
$table->text("search")->default("");
|
$table->text("search")->default("");
|
||||||
// $table->index(["schema", "_sys->updatedAt", "status"]);
|
$table->index(["schema", "updatedAt", "status"]);
|
||||||
$table->index("search");
|
$table->index("search");
|
||||||
});
|
});
|
||||||
|
|
||||||
DB::statement(
|
// DB::statement(
|
||||||
"CREATE INDEX ON " .
|
// "CREATE INDEX ON " .
|
||||||
$this->prefix .
|
// $this->prefix .
|
||||||
'records (schema, ((_sys->>\'updatedAt\')), status)',
|
// 'records (schema, ((_sys->>\'updatedAt\')), status)',
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$schema->hasTable($this->prefix . "edges")) {
|
if (!$schema->hasTable($this->prefix . "edges")) {
|
||||||
@@ -140,8 +144,11 @@ class SetupDatabase extends Command
|
|||||||
$table->uuid("recordId");
|
$table->uuid("recordId");
|
||||||
$table->string("schema");
|
$table->string("schema");
|
||||||
$table->jsonb("data");
|
$table->jsonb("data");
|
||||||
$table->jsonb("_sys");
|
$table->integer("version");
|
||||||
$table->jsonb("_file");
|
$table->timestampTz("createdAt");
|
||||||
|
$table->timestampTz("updatedAt");
|
||||||
|
$table->string("createdBy");
|
||||||
|
$table->string("updatedBy");
|
||||||
$table->jsonb("_edges");
|
$table->jsonb("_edges");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Lucent\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Lucent\Database\Database;
|
|
||||||
|
|
||||||
class UpgradeFiles122 extends Command
|
|
||||||
{
|
|
||||||
|
|
||||||
protected $signature = 'lucent:upgrade:files_1_2_2 {schema} {disk}';
|
|
||||||
protected $description = 'Upgrade to the new filesystem';
|
|
||||||
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$schema = $this->argument('schema');
|
|
||||||
$disk = $this->argument('disk');
|
|
||||||
$db = Database::make();
|
|
||||||
$records = $db->table("lucent_records")->where("schema", $schema)->get();
|
|
||||||
foreach ($records as $record) {
|
|
||||||
$array = json_decode($record->_file, true);
|
|
||||||
$array["disk"] = $disk;
|
|
||||||
$db->table("lucent_records")->where("id", $record->id)->update(["_file" => json_encode($array)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Lucent\File;
|
namespace Lucent\File;
|
||||||
|
|
||||||
|
use Illuminate\Database\Query\Builder;
|
||||||
use Lucent\Data\File as DataFile;
|
use Lucent\Data\File as DataFile;
|
||||||
use Lucent\Database\Database;
|
use Lucent\Database\Database;
|
||||||
use Lucent\Data\File;
|
use Lucent\Data\File;
|
||||||
@@ -15,6 +16,11 @@ class FileRepo
|
|||||||
Database::make()->table("lucent_files")->insert($file->toDB());
|
Database::make()->table("lucent_files")->insert($file->toDB());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function query(): Builder
|
||||||
|
{
|
||||||
|
return Database::make()->table("lucent_files");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return File[]
|
* @return File[]
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ use Lucent\Id\Id;
|
|||||||
use Lucent\LucentException;
|
use Lucent\LucentException;
|
||||||
use Lucent\Data\File as DataFile;
|
use Lucent\Data\File as DataFile;
|
||||||
use Lucent\Record\QueryRecord;
|
use Lucent\Record\QueryRecord;
|
||||||
use Lucent\Schema\FilesSchema;
|
|
||||||
use Spatie\ImageOptimizer\OptimizerChainFactory;
|
use Spatie\ImageOptimizer\OptimizerChainFactory;
|
||||||
|
|
||||||
class FileService
|
class FileService
|
||||||
@@ -25,15 +24,8 @@ class FileService
|
|||||||
public Logger $logger,
|
public Logger $logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function getPath(QueryRecord $file): string
|
|
||||||
{
|
|
||||||
return $this->channelService->channel->filesUrl .
|
|
||||||
"/" .
|
|
||||||
$file->_file->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createFromUrl(
|
public function createFromUrl(
|
||||||
FilesSchema $schema,
|
string $recordId,
|
||||||
string $url,
|
string $url,
|
||||||
): FileUploadResult {
|
): FileUploadResult {
|
||||||
$pathinfo = pathinfo($url);
|
$pathinfo = pathinfo($url);
|
||||||
@@ -44,7 +36,7 @@ class FileService
|
|||||||
$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($recordId, $uploadedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function upload(string $recordId, UploadedFile $file): DataFile
|
public function upload(string $recordId, UploadedFile $file): DataFile
|
||||||
@@ -130,21 +122,6 @@ class FileService
|
|||||||
return Storage::disk(config("lucent.disk"));
|
return Storage::disk(config("lucent.disk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkDuplicate(
|
|
||||||
string $schemaName,
|
|
||||||
string $checksum,
|
|
||||||
int $filesize,
|
|
||||||
): string {
|
|
||||||
$record = Database::make()
|
|
||||||
->table("lucent_records")
|
|
||||||
->where("schema", $schemaName)
|
|
||||||
->where("_file->checksum", $checksum)
|
|
||||||
->where("_file->size", $filesize)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return $record->id ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createTemplates(Filesystem $disk, string $path): void
|
public function createTemplates(Filesystem $disk, string $path): void
|
||||||
{
|
{
|
||||||
$originalImage = $this->imageManager->make($disk->get($path));
|
$originalImage = $this->imageManager->make($disk->get($path));
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ class RecordController extends Controller
|
|||||||
|
|
||||||
$users = $this->accountService->all();
|
$users = $this->accountService->all();
|
||||||
$schema = $this->channelService->getSchema($schemaName)->get();
|
$schema = $this->channelService->getSchema($schemaName)->get();
|
||||||
|
|
||||||
$urlParams = $request->all();
|
$urlParams = $request->all();
|
||||||
$sort = data_get($urlParams, "sort") ?? $schema->sortBy;
|
$sort = data_get($urlParams, "sort") ?? $schema->sortBy;
|
||||||
$filter = data_get($urlParams, "filter") ?? [];
|
$filter = data_get($urlParams, "filter") ?? [];
|
||||||
@@ -86,8 +85,8 @@ class RecordController extends Controller
|
|||||||
->childrenFields($schema?->visible ?? [])
|
->childrenFields($schema?->visible ?? [])
|
||||||
->childrenDepth(1)
|
->childrenDepth(1)
|
||||||
->parentsDepth(0)
|
->parentsDepth(0)
|
||||||
->runWithCount();
|
|
||||||
|
|
||||||
|
->runWithCount();
|
||||||
$records = $graph->getRootRecords()->toArray();
|
$records = $graph->getRootRecords()->toArray();
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
|
|||||||
@@ -89,8 +89,6 @@ class LucentServiceProvider extends ServiceProvider
|
|||||||
RemoveOrphanEdges::class,
|
RemoveOrphanEdges::class,
|
||||||
SetupDatabase::class,
|
SetupDatabase::class,
|
||||||
GenerateCollectionSchema::class,
|
GenerateCollectionSchema::class,
|
||||||
GenerateFileSchema::class,
|
|
||||||
UpgradeFiles122::class,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -78,6 +78,7 @@ final class Query
|
|||||||
->table("lucent_records")
|
->table("lucent_records")
|
||||||
->whereIn("id", $edgesIds)
|
->whereIn("id", $edgesIds)
|
||||||
->whereIn("status", $this->options->status)
|
->whereIn("status", $this->options->status)
|
||||||
|
|
||||||
->get()
|
->get()
|
||||||
->toArray();
|
->toArray();
|
||||||
}
|
}
|
||||||
@@ -295,7 +296,8 @@ final class Query
|
|||||||
public function orderByQuery(Builder $query): Builder
|
public function orderByQuery(Builder $query): Builder
|
||||||
{
|
{
|
||||||
foreach ($this->options->sort as $item) {
|
foreach ($this->options->sort as $item) {
|
||||||
$field = str_replace(".", "->", ltrim($item, "-"));
|
$field = str_replace("_sys.", "", ltrim($item, "-"));
|
||||||
|
$field = str_replace(".", "->", $field);
|
||||||
$dir = str_starts_with($item, "-") ? "desc" : "asc";
|
$dir = str_starts_with($item, "-") ? "desc" : "asc";
|
||||||
if ($field) {
|
if ($field) {
|
||||||
$query->orderBy($field, $dir);
|
$query->orderBy($field, $dir);
|
||||||
|
|||||||
@@ -2,24 +2,25 @@
|
|||||||
|
|
||||||
namespace Lucent\Record;
|
namespace Lucent\Record;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Lucent\LucentException;
|
use Lucent\LucentException;
|
||||||
|
|
||||||
class QueryRecord
|
class QueryRecord
|
||||||
{
|
{
|
||||||
|
|
||||||
function __construct(
|
function __construct(
|
||||||
public string $id,
|
public string $id,
|
||||||
public string $schema,
|
public string $schema,
|
||||||
public Status $status,
|
public Status $status,
|
||||||
public System $_sys,
|
|
||||||
public RecordData $data,
|
public RecordData $data,
|
||||||
|
public Carbon $createdAt,
|
||||||
|
public Carbon $updatedAt,
|
||||||
|
public string $createdBy,
|
||||||
|
public string $updatedBy,
|
||||||
|
public int $version,
|
||||||
public bool $isRoot,
|
public bool $isRoot,
|
||||||
public ?FileData $_file = null,
|
|
||||||
public array $_children = [],
|
public array $_children = [],
|
||||||
public array $_parents = [],
|
public array $_parents = [],
|
||||||
)
|
) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromRecord(Record $record): QueryRecord
|
public static function fromRecord(Record $record): QueryRecord
|
||||||
{
|
{
|
||||||
@@ -27,10 +28,13 @@ class QueryRecord
|
|||||||
id: $record->id,
|
id: $record->id,
|
||||||
schema: $record->schema,
|
schema: $record->schema,
|
||||||
status: $record->status,
|
status: $record->status,
|
||||||
_sys: $record->_sys,
|
|
||||||
data: $record->data,
|
data: $record->data,
|
||||||
|
createdAt: $record->createdAt,
|
||||||
|
updatedAt: $record->updatedAt,
|
||||||
|
createdBy: $record->createdBy,
|
||||||
|
updatedBy: $record->updatedBy,
|
||||||
|
version: $record->version,
|
||||||
isRoot: false,
|
isRoot: false,
|
||||||
_file: $record->_file,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-31
@@ -2,36 +2,46 @@
|
|||||||
|
|
||||||
namespace Lucent\Record;
|
namespace Lucent\Record;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Record implements JsonSerializable
|
class Record implements JsonSerializable
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
function __construct(
|
function __construct(
|
||||||
public string $id,
|
public string $id,
|
||||||
public string $schema,
|
public string $schema,
|
||||||
public Status $status,
|
public Status $status,
|
||||||
public System $_sys,
|
public Carbon $createdAt,
|
||||||
|
public Carbon $updatedAt,
|
||||||
|
public string $createdBy,
|
||||||
|
public string $updatedBy,
|
||||||
|
public int $version,
|
||||||
public RecordData $data,
|
public RecordData $data,
|
||||||
public ?FileData $_file = null,
|
) {}
|
||||||
)
|
|
||||||
|
private function indexValues(array $arrObject)
|
||||||
{
|
{
|
||||||
}
|
return trim(
|
||||||
|
Str::lower(
|
||||||
|
collect($arrObject)
|
||||||
private function indexValues(array $arrObject){
|
->map(function ($value) {
|
||||||
|
if (is_array($value)) {
|
||||||
return trim(Str::lower(collect($arrObject)
|
|
||||||
->map(function($value){
|
|
||||||
if(is_array($value)){
|
|
||||||
return $this->indexValues($value ?? []);
|
return $this->indexValues($value ?? []);
|
||||||
}
|
}
|
||||||
return str_replace(array("\r", "\n"), '', strip_tags((string)$value));
|
return str_replace(
|
||||||
|
["\r", "\n"],
|
||||||
|
"",
|
||||||
|
strip_tags((string) $value),
|
||||||
|
);
|
||||||
})
|
})
|
||||||
->values()->join(" ")." ". $this->_file?->originalName ?? ""));
|
->values()
|
||||||
|
->join(" ") .
|
||||||
|
" " .
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toDB(): array
|
public function toDB(): array
|
||||||
@@ -41,8 +51,11 @@ class Record implements JsonSerializable
|
|||||||
"id" => $this->id,
|
"id" => $this->id,
|
||||||
"status" => $this->status->value,
|
"status" => $this->status->value,
|
||||||
"schema" => $this->schema,
|
"schema" => $this->schema,
|
||||||
"_sys" => json_encode($this->_sys),
|
"createdAt" => $this->createdAt->toDateTimeString(),
|
||||||
"_file" => json_encode($this->_file),
|
"updatedAt" => $this->updatedAt->toDateTimeString(),
|
||||||
|
"createdBy" => $this->createdBy,
|
||||||
|
"updatedBy" => $this->updatedBy,
|
||||||
|
"version" => $this->version,
|
||||||
"data" => json_encode($this->data),
|
"data" => json_encode($this->data),
|
||||||
"search" => $searchIndex,
|
"search" => $searchIndex,
|
||||||
];
|
];
|
||||||
@@ -50,30 +63,21 @@ class Record implements JsonSerializable
|
|||||||
|
|
||||||
public static function fromDB(stdClass $data): Record
|
public static function fromDB(stdClass $data): Record
|
||||||
{
|
{
|
||||||
|
|
||||||
$file = json_decode($data->_file, true);
|
|
||||||
if (!empty($file)) {
|
|
||||||
|
|
||||||
$file = FileData::fromArray($file);
|
|
||||||
} else {
|
|
||||||
$file = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Record(
|
return new Record(
|
||||||
id: $data->id,
|
id: $data->id,
|
||||||
schema: $data->schema,
|
schema: $data->schema,
|
||||||
status: Status::from($data->status),
|
status: Status::from($data->status),
|
||||||
_sys: System::fromArray(json_decode($data->_sys, true)),
|
createdAt: Carbon::parse($data->createdAt),
|
||||||
|
updatedAt: Carbon::parse($data->updatedAt),
|
||||||
|
createdBy: $data->createdBy,
|
||||||
|
updatedBy: $data->updatedBy,
|
||||||
|
version: $data->version,
|
||||||
data: new RecordData(json_decode($data->data, true)),
|
data: new RecordData(json_decode($data->data, true)),
|
||||||
_file: $file,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function jsonSerialize(): static
|
public function jsonSerialize(): static
|
||||||
{
|
{
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Lucent\Record;
|
namespace Lucent\Record;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Http\UploadedFile;
|
use Illuminate\Http\UploadedFile;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Lucent\Account\AuthService;
|
use Lucent\Account\AuthService;
|
||||||
@@ -16,7 +17,7 @@ use Lucent\Record\InputData\EdgeInputData;
|
|||||||
use Lucent\Record\InputData\RecordInputData;
|
use Lucent\Record\InputData\RecordInputData;
|
||||||
use Lucent\Revision\RevisionService;
|
use Lucent\Revision\RevisionService;
|
||||||
use Lucent\Schema\FieldInterface;
|
use Lucent\Schema\FieldInterface;
|
||||||
use Lucent\Schema\Schema;
|
use Lucent\Data\Schema;
|
||||||
use Lucent\Schema\Type;
|
use Lucent\Schema\Type;
|
||||||
use Lucent\Schema\Validator\Validator;
|
use Lucent\Schema\Validator\Validator;
|
||||||
use Lucent\Schema\Validator\ValidatorException;
|
use Lucent\Schema\Validator\ValidatorException;
|
||||||
@@ -115,9 +116,12 @@ readonly class RecordService
|
|||||||
id: $newRecordId,
|
id: $newRecordId,
|
||||||
schema: $data->schemaName,
|
schema: $data->schemaName,
|
||||||
status: $data->status,
|
status: $data->status,
|
||||||
_sys: System::newRecord($this->authService->currentUserId()),
|
version: 1,
|
||||||
|
createdBy: $this->authService->currentUserId(),
|
||||||
|
updatedBy: $this->authService->currentUserId(),
|
||||||
|
createdAt: Carbon::now(),
|
||||||
|
updatedAt: Carbon::now(),
|
||||||
data: $formattedData,
|
data: $formattedData,
|
||||||
_file: !empty($file) ? FileData::fromArray(toArray($file)) : null,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($data->status === Status::PUBLISHED) {
|
if ($data->status === Status::PUBLISHED) {
|
||||||
@@ -173,9 +177,12 @@ readonly class RecordService
|
|||||||
id: $record->id,
|
id: $record->id,
|
||||||
schema: $record->schema,
|
schema: $record->schema,
|
||||||
status: $status,
|
status: $status,
|
||||||
_sys: $record->_sys->update($this->authService->currentUserId()),
|
version: $record->version + 1,
|
||||||
|
createdBy: $record->createdBy,
|
||||||
|
updatedBy: $this->authService->currentUserId(),
|
||||||
|
createdAt: $record->createdAt,
|
||||||
|
updatedAt: Carbon::now(),
|
||||||
data: $record->data->merge($formattedData),
|
data: $record->data->merge($formattedData),
|
||||||
_file: $record->_file,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
RecordRepo::update($newRecord);
|
RecordRepo::update($newRecord);
|
||||||
@@ -205,12 +212,12 @@ readonly class RecordService
|
|||||||
$record->schema,
|
$record->schema,
|
||||||
new RecordData($data),
|
new RecordData($data),
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($status === Status::PUBLISHED) {
|
if ($status === Status::PUBLISHED) {
|
||||||
$errors = $this->recordValidator->check(
|
$errors = $this->recordValidator->check(
|
||||||
$record->schema,
|
$record->schema,
|
||||||
$formattedData,
|
$formattedData,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($errors->isNotEmpty()) {
|
if ($errors->isNotEmpty()) {
|
||||||
$this->recordValidator->throwException($errors);
|
$this->recordValidator->throwException($errors);
|
||||||
}
|
}
|
||||||
@@ -220,9 +227,12 @@ readonly class RecordService
|
|||||||
id: $record->id,
|
id: $record->id,
|
||||||
schema: $record->schema,
|
schema: $record->schema,
|
||||||
status: $status,
|
status: $status,
|
||||||
_sys: $record->_sys->update($this->authService->currentUserId()),
|
version: $record->version + 1,
|
||||||
|
createdBy: $record->createdBy,
|
||||||
|
updatedBy: $this->authService->currentUserId(),
|
||||||
|
createdAt: $record->createdAt,
|
||||||
|
updatedAt: Carbon::now(),
|
||||||
data: $record->data->merge($formattedData),
|
data: $record->data->merge($formattedData),
|
||||||
_file: $record->_file,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
RecordRepo::update($newRecord);
|
RecordRepo::update($newRecord);
|
||||||
@@ -278,7 +288,6 @@ readonly class RecordService
|
|||||||
data: $record->data->toArray(),
|
data: $record->data->toArray(),
|
||||||
status: Status::DRAFT,
|
status: Status::DRAFT,
|
||||||
),
|
),
|
||||||
file: $record->_file,
|
|
||||||
edges: $newEdgesData,
|
edges: $newEdgesData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -337,9 +346,12 @@ readonly class RecordService
|
|||||||
id: Id::new(),
|
id: Id::new(),
|
||||||
schema: $schema->name,
|
schema: $schema->name,
|
||||||
status: Status::DRAFT,
|
status: Status::DRAFT,
|
||||||
_sys: System::newRecord($this->authService->currentUserId()),
|
version: 1,
|
||||||
|
createdBy: $this->authService->currentUserId(),
|
||||||
|
updatedBy: $this->authService->currentUserId(),
|
||||||
|
createdAt: Carbon::now(),
|
||||||
|
updatedAt: Carbon::now(),
|
||||||
data: $formattedData,
|
data: $formattedData,
|
||||||
_file: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Lucent\Record;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Lucent\Schema\Schema;
|
|
||||||
|
|
||||||
readonly class System
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
function __construct(
|
|
||||||
public int $version,
|
|
||||||
public string $createdBy,
|
|
||||||
public string $updatedBy,
|
|
||||||
public string $createdAt,
|
|
||||||
public string $updatedAt,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function fromArray(array $data): System
|
|
||||||
{
|
|
||||||
return new System(
|
|
||||||
version: data_get($data, "version"),
|
|
||||||
createdBy: data_get($data, "createdBy"),
|
|
||||||
updatedBy: data_get($data, "updatedBy"),
|
|
||||||
createdAt: data_get($data, "createdAt"),
|
|
||||||
updatedAt: data_get($data, "updatedAt"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function newRecord(string $userId): System
|
|
||||||
{
|
|
||||||
$now = Carbon::now()->toJson();
|
|
||||||
return new System(
|
|
||||||
version: 1,
|
|
||||||
createdBy: $userId,
|
|
||||||
updatedBy: $userId,
|
|
||||||
createdAt: $now,
|
|
||||||
updatedAt: $now,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function update(string $userId): System
|
|
||||||
{
|
|
||||||
$now = Carbon::now()->toJson();
|
|
||||||
return new System(
|
|
||||||
version: $this->version + 1,
|
|
||||||
createdBy: $this->createdBy,
|
|
||||||
updatedBy: $userId,
|
|
||||||
createdAt: $this->createdAt,
|
|
||||||
updatedAt: $now,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
+13
-17
@@ -2,51 +2,47 @@
|
|||||||
|
|
||||||
namespace Lucent\Revision;
|
namespace Lucent\Revision;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Lucent\Edge\Edge;
|
use Lucent\Edge\Edge;
|
||||||
use Lucent\Record\FileData;
|
|
||||||
use Lucent\Record\Record;
|
use Lucent\Record\Record;
|
||||||
use Lucent\Record\RecordData;
|
use Lucent\Record\RecordData;
|
||||||
use Lucent\Record\System;
|
|
||||||
|
|
||||||
readonly class Revision
|
readonly class Revision
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $id
|
* @param string $id
|
||||||
* @param string $recordId
|
* @param string $recordId
|
||||||
* @param string $schema
|
* @param string $schema
|
||||||
* @param System $_sys
|
|
||||||
* @param RecordData $data
|
* @param RecordData $data
|
||||||
* @param list<Edge> $_edges
|
* @param list<Edge> $_edges
|
||||||
* @param FileData|null $_file
|
|
||||||
*/
|
*/
|
||||||
function __construct(
|
function __construct(
|
||||||
public string $id,
|
public string $id,
|
||||||
public string $recordId,
|
public string $recordId,
|
||||||
public string $schema,
|
public string $schema,
|
||||||
public System $_sys,
|
public Carbon $createdAt,
|
||||||
|
public Carbon $updatedAt,
|
||||||
|
public string $createdBy,
|
||||||
|
public string $updatedBy,
|
||||||
|
public int $version,
|
||||||
public RecordData $data,
|
public RecordData $data,
|
||||||
public array $_edges,
|
public array $_edges,
|
||||||
public ?FileData $_file = null,
|
) {}
|
||||||
|
|
||||||
)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function fromRecord(Record $record, array $edges): Revision
|
public static function fromRecord(Record $record, array $edges): Revision
|
||||||
{
|
{
|
||||||
return new Revision(
|
return new Revision(
|
||||||
id: (string)Str::uuid(),
|
id: (string) Str::uuid(),
|
||||||
recordId: $record->id,
|
recordId: $record->id,
|
||||||
schema: $record->schema,
|
schema: $record->schema,
|
||||||
_sys: $record->_sys,
|
createdAt: $record->createdAt,
|
||||||
|
updatedAt: $record->updatedAt,
|
||||||
|
createdBy: $record->createdBy,
|
||||||
|
updatedBy: $record->updatedBy,
|
||||||
|
version: $record->version,
|
||||||
data: $record->data,
|
data: $record->data,
|
||||||
_edges: $edges,
|
_edges: $edges,
|
||||||
_file: $record->_file
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ namespace Lucent\Revision;
|
|||||||
use Lucent\Database\Database;
|
use Lucent\Database\Database;
|
||||||
use Lucent\Edge\Edge;
|
use Lucent\Edge\Edge;
|
||||||
use Lucent\Primitive\Collection;
|
use Lucent\Primitive\Collection;
|
||||||
use Lucent\Record\FileData;
|
|
||||||
use Lucent\Record\RecordData;
|
use Lucent\Record\RecordData;
|
||||||
use Lucent\Record\System;
|
|
||||||
use PhpOption\Option;
|
use PhpOption\Option;
|
||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
class RevisionRepo
|
class RevisionRepo
|
||||||
{
|
{
|
||||||
|
|
||||||
private string $table = "lucent_revisions";
|
private string $table = "lucent_revisions";
|
||||||
|
|
||||||
public function create(Revision $revision): string
|
public function create(Revision $revision): string
|
||||||
@@ -23,17 +20,17 @@ class RevisionRepo
|
|||||||
return $revision->id;
|
return $revision->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<Revision>
|
* @return Collection<Revision>
|
||||||
**/
|
**/
|
||||||
public function getByRecordId(string $rid): Collection
|
public function getByRecordId(string $rid): Collection
|
||||||
{
|
{
|
||||||
$revisions = Database::make()->table($this->table)
|
$revisions = Database::make()
|
||||||
|
->table($this->table)
|
||||||
->where("recordId", $rid)
|
->where("recordId", $rid)
|
||||||
->get()
|
->get()
|
||||||
->map([$this, 'fromDB'])
|
->map([$this, "fromDB"])
|
||||||
->sortByDesc("_sys.version")
|
->sortByDesc("version")
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
return new Collection($revisions);
|
return new Collection($revisions);
|
||||||
@@ -41,29 +38,31 @@ class RevisionRepo
|
|||||||
|
|
||||||
public function cleanupRecord(string $rid, int $numKeep): void
|
public function cleanupRecord(string $rid, int $numKeep): void
|
||||||
{
|
{
|
||||||
$revisionIds = Database::make()->table($this->table)
|
$revisionIds = Database::make()
|
||||||
|
->table($this->table)
|
||||||
->where("recordId", $rid)
|
->where("recordId", $rid)
|
||||||
->orderBy("_sys->version", "desc")
|
->orderBy("version", "desc")
|
||||||
->limit(100)
|
->limit(100)
|
||||||
->skip($numKeep)
|
->skip($numKeep)
|
||||||
->get()
|
->get()
|
||||||
->pluck("id");
|
->pluck("id");
|
||||||
|
|
||||||
Database::make()->table($this->table)
|
Database::make()
|
||||||
|
->table($this->table)
|
||||||
->whereIn("id", $revisionIds)
|
->whereIn("id", $revisionIds)
|
||||||
->delete();
|
->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Option<Revision>
|
* @return Option<Revision>
|
||||||
*/
|
*/
|
||||||
public function getByRecordIdAndVersion(string $rid, int $version): Option
|
public function getByRecordIdAndVersion(string $rid, int $version): Option
|
||||||
{
|
{
|
||||||
|
$res = Database::make()
|
||||||
$res = Database::make()->table($this->table)
|
->table($this->table)
|
||||||
->where("recordId", $rid)
|
->where("recordId", $rid)
|
||||||
->where('_sys->version', $version)->first();
|
->where("version", $version)
|
||||||
|
->first();
|
||||||
|
|
||||||
if (empty($res)) {
|
if (empty($res)) {
|
||||||
return none();
|
return none();
|
||||||
@@ -78,8 +77,11 @@ class RevisionRepo
|
|||||||
"id" => $revision->id,
|
"id" => $revision->id,
|
||||||
"recordId" => $revision->recordId,
|
"recordId" => $revision->recordId,
|
||||||
"schema" => $revision->schema,
|
"schema" => $revision->schema,
|
||||||
"_sys" => json_encode($revision->_sys),
|
"createdAt" => $revision->createdAt->toDateTimeString(),
|
||||||
"_file" => json_encode($revision->_file),
|
"updatedAt" => $revision->updatedAt->toDateTimeString(),
|
||||||
|
"createdBy" => $revision->createdBy,
|
||||||
|
"updatedBy" => $revision->updatedBy,
|
||||||
|
"version" => $revision->version,
|
||||||
"data" => json_encode($revision->data),
|
"data" => json_encode($revision->data),
|
||||||
"_edges" => json_encode($revision->_edges),
|
"_edges" => json_encode($revision->_edges),
|
||||||
];
|
];
|
||||||
@@ -87,24 +89,22 @@ class RevisionRepo
|
|||||||
|
|
||||||
public function fromDB(stdClass $data): Revision
|
public function fromDB(stdClass $data): Revision
|
||||||
{
|
{
|
||||||
$file = json_decode($data->_file, true);
|
$edges = array_map(
|
||||||
if (!empty($file)) {
|
fn($e) => Edge::fromArray($e),
|
||||||
|
json_decode($data->_edges ?? "[]", true),
|
||||||
$file = FileData::fromArray($file);
|
);
|
||||||
} else {
|
|
||||||
$file = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$edges = array_map(fn($e) => Edge::fromArray($e), json_decode($data->_edges ?? "[]", true));
|
|
||||||
|
|
||||||
return new Revision(
|
return new Revision(
|
||||||
id: $data->id,
|
id: $data->id,
|
||||||
recordId: $data->recordId,
|
recordId: $data->recordId,
|
||||||
schema: $data->schema,
|
schema: $data->schema,
|
||||||
_sys: System::fromArray(json_decode($data->_sys, true)),
|
createdBy: $data->createdBy,
|
||||||
|
updatedBy: $data->updatedBy,
|
||||||
|
createdAt: $data->createdAt,
|
||||||
|
updatedAt: $data->updatedAt,
|
||||||
|
version: $data->version,
|
||||||
data: new RecordData(json_decode($data->data, true)),
|
data: new RecordData(json_decode($data->data, true)),
|
||||||
_edges: $edges,
|
_edges: $edges,
|
||||||
_file: $file
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,18 @@ final class Nullable
|
|||||||
public bool $nullable,
|
public bool $nullable,
|
||||||
public mixed $value,
|
public mixed $value,
|
||||||
public mixed $default,
|
public mixed $default,
|
||||||
)
|
) {}
|
||||||
{
|
|
||||||
|
public static function make(
|
||||||
|
bool $nullable,
|
||||||
|
mixed $value,
|
||||||
|
mixed $default,
|
||||||
|
): Nullable {
|
||||||
|
return new self($nullable, $value, $default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function value(): mixed
|
public function value(): mixed
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!empty($this->value)) {
|
if (!empty($this->value)) {
|
||||||
return $this->value;
|
return $this->value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,16 @@ class SchemaService
|
|||||||
|
|
||||||
public function fromArray(array $schemaArr): Schema
|
public function fromArray(array $schemaArr): Schema
|
||||||
{
|
{
|
||||||
|
$schemaArr["fields"] = [
|
||||||
|
[
|
||||||
|
"ui" => "text",
|
||||||
|
"name" => "name",
|
||||||
|
"label" => "Name",
|
||||||
|
"required" => true,
|
||||||
|
],
|
||||||
|
...$schemaArr["fields"],
|
||||||
|
];
|
||||||
|
|
||||||
return new Schema(
|
return new Schema(
|
||||||
name: $schemaArr["name"],
|
name: $schemaArr["name"],
|
||||||
label: $schemaArr["label"],
|
label: $schemaArr["label"],
|
||||||
|
|||||||
+2
-44
@@ -9,10 +9,7 @@ readonly class System
|
|||||||
public string $label,
|
public string $label,
|
||||||
public string $ui,
|
public string $ui,
|
||||||
public bool $files,
|
public bool $files,
|
||||||
)
|
) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static function getById(string $id): System
|
public static function getById(string $id): System
|
||||||
{
|
{
|
||||||
@@ -26,7 +23,7 @@ readonly class System
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"_sys.status" => new System(
|
"_sys.status" => new System(
|
||||||
name: "status",
|
name: "_sys.status",
|
||||||
label: "Status",
|
label: "Status",
|
||||||
ui: "status",
|
ui: "status",
|
||||||
files: false,
|
files: false,
|
||||||
@@ -55,45 +52,6 @@ readonly class System
|
|||||||
ui: "datetime",
|
ui: "datetime",
|
||||||
files: false,
|
files: false,
|
||||||
),
|
),
|
||||||
"_file.path" => new System(
|
|
||||||
name: "_file.path",
|
|
||||||
label: "Filename",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
"_file.mime" => new System(
|
|
||||||
name: "_file.mime",
|
|
||||||
label: "Mime Type",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
|
|
||||||
"_file.width" => new System(
|
|
||||||
name: "_file.width",
|
|
||||||
label: "Dimensions",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
"_file.size" => new System(
|
|
||||||
name: "_file.size",
|
|
||||||
label: "Size",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
"_file.originalName" => new System(
|
|
||||||
name: "_file.originalName",
|
|
||||||
label: "Original Name",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
"_file.checksum" => new System(
|
|
||||||
name: "_file.checksum",
|
|
||||||
label: "Checksum",
|
|
||||||
ui: "text",
|
|
||||||
files: true,
|
|
||||||
),
|
|
||||||
|
|
||||||
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ class File implements FieldInterface, MinMaxInterface
|
|||||||
{
|
{
|
||||||
public FieldInfo $info;
|
public FieldInfo $info;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string[] $collections
|
* @param string[] $collections
|
||||||
*/
|
*/
|
||||||
@@ -24,13 +23,14 @@ class File implements FieldInterface, MinMaxInterface
|
|||||||
public ?int $max = null,
|
public ?int $max = null,
|
||||||
public array $collections = [],
|
public array $collections = [],
|
||||||
public string $group = "",
|
public string $group = "",
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->info = new FieldInfo("file", "File", FieldType::FILE);
|
$this->info = new FieldInfo("file", "File", FieldType::FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function format(array $input, array $output): array
|
public function format(array $input, array $output): array
|
||||||
{
|
{
|
||||||
|
$value = $input[$this->name] ?? [];
|
||||||
|
$output[$this->name] = $value;
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +51,4 @@ class File implements FieldInterface, MinMaxInterface
|
|||||||
|
|
||||||
return count($value) < $this->min;
|
return count($value) < $this->min;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -25,26 +25,29 @@ class Slug implements FieldInterface, RequiredInterface
|
|||||||
public bool $readonly = false,
|
public bool $readonly = false,
|
||||||
public string $source = "",
|
public string $source = "",
|
||||||
public string $group = "",
|
public string $group = "",
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->info = new FieldInfo("slug", "Slug", FieldType::STRING);
|
$this->info = new FieldInfo("slug", "Slug", FieldType::STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function format(array $input, array $output): array
|
public function format(array $input, array $output): array
|
||||||
{
|
{
|
||||||
$value = !empty($input[$this->name]) ? (string)$input[$this->name] : null;
|
$value = !empty($input[$this->name])
|
||||||
if(empty($value)){
|
? (string) $input[$this->name]
|
||||||
|
: null;
|
||||||
|
if (empty($value)) {
|
||||||
$value = Str::slug($input[$this->source]);
|
$value = Str::slug($input[$this->source]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$output[$this->name] = (new Nullable($this->nullable, $value, ""))->value();
|
$output[$this->name] = new Nullable(
|
||||||
|
$this->nullable,
|
||||||
|
$value,
|
||||||
|
"",
|
||||||
|
)->value();
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function failRequired(mixed $value): bool
|
public function failRequired(mixed $value): bool
|
||||||
{
|
{
|
||||||
return empty(trim($value));
|
return empty(trim($value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-18
@@ -3,7 +3,7 @@
|
|||||||
use PhpOption\None;
|
use PhpOption\None;
|
||||||
use PhpOption\Some;
|
use PhpOption\Some;
|
||||||
|
|
||||||
if (!function_exists('some')) {
|
if (!function_exists("some")) {
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param T $value
|
* @param T $value
|
||||||
@@ -15,51 +15,50 @@ if (!function_exists('some')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists("none")) {
|
||||||
if (!function_exists('none')) {
|
|
||||||
function none(): None
|
function none(): None
|
||||||
{
|
{
|
||||||
return None::create();
|
return None::create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('toArray')) {
|
if (!function_exists("toArray")) {
|
||||||
function toArray(mixed $data): array
|
function toArray(mixed $data): array
|
||||||
{
|
{
|
||||||
return \json_decode(\json_encode($data), true);
|
return \json_decode(\json_encode($data), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists("make_dir_r")) {
|
||||||
if (!function_exists('make_dir_r')) {
|
|
||||||
function make_dir_r(string $path): void
|
function make_dir_r(string $path): void
|
||||||
{
|
{
|
||||||
is_dir($path) || mkdir($path, 0777, true);
|
is_dir($path) || mkdir($path, 0777, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists("schemas_path")) {
|
||||||
if (!function_exists('schemas_path')) {
|
|
||||||
function schemas_path(): string
|
function schemas_path(): string
|
||||||
{
|
{
|
||||||
return storage_path("lucent/lucent.schemas.json");
|
return storage_path("lucent/lucent.schemas.json");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('lucent_file')) {
|
if (!function_exists("lucent_file")) {
|
||||||
function lucent_file(\Lucent\Record\QueryRecord $record): string
|
function lucent_file(\Lucent\Data\File $file): string
|
||||||
{
|
{
|
||||||
$path = $record->_file->path;
|
$path = $file->path;
|
||||||
return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] ."/". $path;
|
return app()->make(\Lucent\Channel\ChannelService::class)->channel
|
||||||
|
->filesUrl .
|
||||||
|
"/" .
|
||||||
|
$path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists('lucent_image')) {
|
if (!function_exists("lucent_image")) {
|
||||||
function lucent_image(\Lucent\Record\QueryRecord $record, string $template): string
|
function lucent_image(\Lucent\Data\File $file, string $template): string
|
||||||
{
|
{
|
||||||
$path = $record->_file->path;
|
$path = $file->path;
|
||||||
return app()->make(\Lucent\Channel\ChannelService::class)->channel->disks[$record->_file->disk] . "/templates/$template/$path";
|
return app()->make(\Lucent\Channel\ChannelService::class)->channel
|
||||||
|
->filesUrl . "/templates/$template/$path";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user