From 5a13ddb2ec08a26eb6458d747fe246613abc5e96 Mon Sep 17 00:00:00 2001 From: lexx Date: Fri, 16 Aug 2024 17:38:26 +0300 Subject: [PATCH] backlinks --- front/js/svelte/content/tools/Tools.svelte | 5 + front/js/svelte/records/FormField.svelte | 19 -- front/js/svelte/records/Graph.svelte | 147 +++------------ front/js/svelte/records/block/Block.svelte | 37 ---- .../svelte/records/block/BlockButtons.svelte | 61 ------- .../svelte/records/block/BlockElements.svelte | 158 ---------------- front/js/svelte/records/block/block.js | 25 --- .../svelte/records/block/elements/File.svelte | 105 ----------- .../records/block/elements/Heading.svelte | 13 -- .../records/block/elements/Markdown.svelte | 15 -- .../records/block/elements/Reference.svelte | 71 -------- .../svelte/records/block/elements/Rich.svelte | 10 -- .../records/block/elements/Textarea.svelte | 39 ---- .../svelte/records/elements/Reference.svelte | 1 - .../records/elements/ReferenceTable.svelte | 170 ------------------ .../svelte/records/header/ContentTabs.svelte | 2 +- front/sass/_dropdown.scss | 4 +- src/Http/Controller/RecordController.php | 1 + src/Query/Query.php | 25 ++- src/Query/QueryOptions.php | 3 +- 20 files changed, 60 insertions(+), 851 deletions(-) delete mode 100644 front/js/svelte/records/block/Block.svelte delete mode 100644 front/js/svelte/records/block/BlockButtons.svelte delete mode 100644 front/js/svelte/records/block/BlockElements.svelte delete mode 100644 front/js/svelte/records/block/block.js delete mode 100644 front/js/svelte/records/block/elements/File.svelte delete mode 100644 front/js/svelte/records/block/elements/Heading.svelte delete mode 100644 front/js/svelte/records/block/elements/Markdown.svelte delete mode 100644 front/js/svelte/records/block/elements/Reference.svelte delete mode 100644 front/js/svelte/records/block/elements/Rich.svelte delete mode 100644 front/js/svelte/records/block/elements/Textarea.svelte delete mode 100644 front/js/svelte/records/elements/ReferenceTable.svelte diff --git a/front/js/svelte/content/tools/Tools.svelte b/front/js/svelte/content/tools/Tools.svelte index ad8d260..a8dc298 100644 --- a/front/js/svelte/content/tools/Tools.svelte +++ b/front/js/svelte/content/tools/Tools.svelte @@ -121,6 +121,11 @@ href="{channel.lucentUrl}/content/{schema.name}?filter[status_in]=trashed" >View trashed records + View unlinked records {/if} diff --git a/front/js/svelte/records/FormField.svelte b/front/js/svelte/records/FormField.svelte index 492203e..8d991c8 100644 --- a/front/js/svelte/records/FormField.svelte +++ b/front/js/svelte/records/FormField.svelte @@ -3,7 +3,6 @@ import Slug from "./elements/Slug.svelte"; import Reference from "./elements/Reference.svelte"; import ReferenceInline from "./elements/ReferenceInline.svelte"; - import Block from "./block/Block.svelte"; import Color from "./elements/Color.svelte"; import Checkbox from "./elements/Checkbox.svelte"; import Number from "./elements/Number.svelte"; @@ -17,7 +16,6 @@ import Json from "./elements/JSON.svelte"; import Markdown from "./elements/Markdown.svelte"; import FieldHeader from "./elements/FieldHeader.svelte"; - import ReferenceTable from "./elements/ReferenceTable.svelte"; import ReferenceTags from "./elements/ReferenceTags.svelte"; const formElements = { @@ -56,14 +54,6 @@ {field} {validationErrors} /> - {:else if field.info.name === "reference" && field.layout === "table"} - {:else if field.info.name === "reference" && field.layout === "tags"} {:else if field.info.name === "file"} - {:else if field.info.name === "block"} - {:else if field.info.name === "text"} - import PreviewCardSmall from "./PreviewCardSmall.svelte"; - import PreviewCard from "./PreviewCard.svelte"; - import Icon from "../common/Icon.svelte"; - import Preview from "../files/Preview.svelte"; import {getContext} from "svelte"; - import {uniqBy} from "lodash"; + import PreviewReference from "./previews/PreviewReference.svelte"; const channel = getContext("channel"); export let graph; - export let record; - function findEdgeField(schema, edgeField){ if(edgeField.includes(":")){ let edgeFieldAr = edgeField.split(":"); @@ -18,121 +12,32 @@ return schema.fields.find((f) => f.name === edgeField); } - let parentEdgesByField = graph.parentEdges - .filter((edge) => edge.source !== record.id && edge.depth === 1) - .reduce((carry, edge) => { - let schema = channel.schemas.find((s) => s.name === edge.sourceSchema); - let edgeField = findEdgeField(schema,edge.field); - let schemaField = edge.sourceSchema + edgeField; - - let arecord = graph.records.find((n) => { - return n.id === edge.source; - }); - if (!carry[schemaField]) { - carry[schemaField] = { - field: edgeField, - schema: schema, - nodes: [], - }; - } - if (arecord) { - carry[schemaField].nodes.push(arecord); - carry[schemaField].nodes = uniqBy(carry[schemaField].nodes,"id"); - } - return carry; - }, {}); - - - let childrenEdgesByField = graph.edges - .filter((edge) => edge.source === record.id && edge.depth === 1) - .reduce((carry, edge) => { - - let schema = channel.schemas.find((s) => s.name === record.schema); - let edgeField = findEdgeField(schema,edge.field); - - // let schemaField = edge.targetSchema + edgeField; - let schemaField = edgeField.name + edge.targetSchema; - - if (!carry[schemaField]) { - carry[schemaField] = { - field: edgeField, - nodes: [], - }; - } - - let arecord = graph.records.find((n) => { - return n.id === edge.target; - }); - if (arecord) { - carry[schemaField].nodes.push(arecord); - carry[schemaField].nodes = uniqBy(carry[schemaField].nodes,"id"); - } - return carry; - }, {}); + let backlinks = graph.parentEdges.map(edge => { + let schema = channel.schemas.find((s) => s.name === edge.sourceSchema); + let edgeField = findEdgeField(schema,edge.field); + return { + field: edgeField.label, + record: graph.records.find( record => record.id === edge.source) + } + }) -{#each Object.entries(parentEdgesByField) as [fieldName, fieldData]} -
-
- {fieldData.schema.label} - - {fieldData.field.label} -
-
- {#each fieldData.nodes as node} - {#if node._file?.path} -
- -
- {:else} -
- -
- {/if} - {/each} -
- +{#each backlinks as backlink} +
+ In {backlink.field} +
{/each} -{#if Object.entries(parentEdgesByField).length > 0} -
- -
-{/if} -
- -
-{#if Object.entries(childrenEdgesByField).length > 0} -
- -
-{/if} - -{#each Object.entries(childrenEdgesByField) as [fieldName, fieldData]} -
-
{fieldData.field.label}
-
- {#each fieldData.nodes as node} - {#if fieldData.field.info.ui === "file"} -
- -
- {:else} -
- -
- {/if} - {/each} -
-
-{/each} - - diff --git a/front/js/svelte/records/block/Block.svelte b/front/js/svelte/records/block/Block.svelte deleted file mode 100644 index d96ed2c..0000000 --- a/front/js/svelte/records/block/Block.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - - -
-
- -
- {#each value as blockItemData (blockItemData.id)} -
- - -
- {/each} - -
\ No newline at end of file diff --git a/front/js/svelte/records/block/BlockButtons.svelte b/front/js/svelte/records/block/BlockButtons.svelte deleted file mode 100644 index 73a2d44..0000000 --- a/front/js/svelte/records/block/BlockButtons.svelte +++ /dev/null @@ -1,61 +0,0 @@ - -
- - {#if showOptions} -
- {#each blockSchema.fields as validUi} -
- -
- {/each} -
- - {/if} -
- diff --git a/front/js/svelte/records/block/BlockElements.svelte b/front/js/svelte/records/block/BlockElements.svelte deleted file mode 100644 index 1868ea6..0000000 --- a/front/js/svelte/records/block/BlockElements.svelte +++ /dev/null @@ -1,158 +0,0 @@ - - -
-
- {block.meta.label} - -
- {#if block.meta.info.name === "heading"} - - - - {:else if block.meta.info.name === "textarea"} - - -
- diff --git a/front/js/svelte/records/elements/Reference.svelte b/front/js/svelte/records/elements/Reference.svelte index 58e20fb..cbf5ffe 100644 --- a/front/js/svelte/records/elements/Reference.svelte +++ b/front/js/svelte/records/elements/Reference.svelte @@ -63,7 +63,6 @@ {#each references as reference (reference.id)}
- import {getContext} from "svelte"; - import {previewTitle} from "../Preview"; - import {getErrorMessage} from "./errorMessage"; - import {sortByField} from "../../edges/sortEdges"; - import ReferenceInlineButtons from "./ReferenceInlineButtons.svelte"; - import Sortable from "../../libs/Sortable.svelte"; - import RenderField from "../../content/RenderField.svelte"; - import Icon from "../../common/Icon.svelte"; - import {insertEdges} from "./reference.js"; - - const channel = getContext("channel"); - export let field; - export let record; - export let graph; - export let validationErrors; - $: errorMessage = getErrorMessage(validationErrors, field.name); - - $: references = graph.edges - .filter((edge) => edge.field === field.name) - .map((edge) => { - return graph.records.find((increc) => increc.id === edge.target && record.id === edge.source); - }).filter((rec) => (rec?.id ? true : false)) ?? []; - - let collections = channel.schemas.filter((aschema) => - field.collections.includes(aschema.name) - ); - - let collection = channel.schemas.filter((aschema) => - field.collections.includes(aschema.name) - )[0]; - - function removeReference(e, recordId) { - e.preventDefault(); - graph.edges = graph.edges.filter( - (edge) => !(edge.source === record.id && edge.target === recordId && edge.field === field.name) - ); - } - - function sendToTop(e, recordId) { - e.preventDefault(); - let ref = graph.edges.find( - (edge) => edge.source === record.id && edge.target === recordId && edge.field === field.name - ); - removeReference(e, recordId); - graph.edges = [ref, ...graph.edges]; - } - - function sendToBottom(e, recordId) { - e.preventDefault(); - let ref = graph.edges.find( - (edge) => edge.source === record.id && edge.target === recordId && edge.field === field.name - ); - removeReference(e, recordId); - graph.edges = [...graph.edges, ref]; - } - - - function reorder(e) { - graph.edges = sortByField(e.detail.source, e.detail.target, graph.edges, field.name, references); - } - - - function insert(e) { - e.preventDefault(); - graph = insertEdges(graph, record, e.detail.records, field.name, e.detail.action); - } - - $:visibleColumns = []; - // $: visibleColumns = collection.fields - // .filter((f) => f.ui !== "tab" && !f.trashed) - // .filter((f) => { - // return collection.visible.includes(f.name); - // }); - - -{#if errorMessage} -
- {errorMessage} -
-{/if} -
- -
-{#if references.length > 0} -
- - - - - {/each} - - - - {#each references as record,index (record.id)} - - - {#each visibleColumns as field, index} - - {/each} - - - {/each} - -
- - {#each visibleColumns as field} - {field.label} -
- - - - - - {#if references.length > 30 && index > 0} - - {/if} - {#if references.length > 30 && index + 1 < references.length} - - {/if} -
-
-{/if} diff --git a/front/js/svelte/records/header/ContentTabs.svelte b/front/js/svelte/records/header/ContentTabs.svelte index d8fcead..2f9b701 100644 --- a/front/js/svelte/records/header/ContentTabs.svelte +++ b/front/js/svelte/records/header/ContentTabs.svelte @@ -12,7 +12,7 @@ name: "", }; let graphTab = { - label: "Graph", + label: "Backlinks", name: "_graph", }; if (isCreateMode) { diff --git a/front/sass/_dropdown.scss b/front/sass/_dropdown.scss index 0cf4209..0250233 100644 --- a/front/sass/_dropdown.scss +++ b/front/sass/_dropdown.scss @@ -26,11 +26,11 @@ top: 35px; min-width: max-content; - & .orientation-right { + &.orientation-right { right: 0; } - & .orientation-left { + &.orientation-left { left: 0; } diff --git a/src/Http/Controller/RecordController.php b/src/Http/Controller/RecordController.php index 6c5c663..d796b82 100644 --- a/src/Http/Controller/RecordController.php +++ b/src/Http/Controller/RecordController.php @@ -64,6 +64,7 @@ class RecordController extends Controller $graphArray = null; $graph = $this->query ->filter($arguments) + ->notLinked($request->input("notlinked") ?? "") ->limit($limit) ->status(explode(",", $arguments["status_in"])) ->skip($skip) diff --git a/src/Query/Query.php b/src/Query/Query.php index 3d8a941..ff556e7 100644 --- a/src/Query/Query.php +++ b/src/Query/Query.php @@ -115,7 +115,6 @@ final class Query })->sortBy("rank")->values()->toArray(); - return new Graph( new Collection($queryRecords), new Collection($queryEdges), @@ -145,19 +144,35 @@ final class Query { $query = DB::table("records"); $query = $this->parseFilters($query); + $query = $this->findNotLinked($query); if ($this->options->limit > 0) { $query->limit($this->options->limit); $query->offset($this->options->skip); } $query = $this->orderByQuery($query); - return $query->get()->map(function ($r) { $r->isRoot = true; return $r; })->toArray(); } + + private function findNotLinked(Builder $query): Builder + { + if(empty($this->options->notLinked)){ + return $query; + } + + // This returns only records that have no parents + $query + ->select("records.*") + ->join('edges', 'records.id', '=', 'edges.target', 'left outer') + ->whereNull("edges.target") + ; + return $query; + } + private function getChildren(array $ids): array { @@ -256,4 +271,10 @@ final class Query } return $query; } + + public function notLinked(string $value): Query + { + $this->options->notLinked = $value; + return $this; + } } diff --git a/src/Query/QueryOptions.php b/src/Query/QueryOptions.php index 4c03aa0..ced5a8c 100644 --- a/src/Query/QueryOptions.php +++ b/src/Query/QueryOptions.php @@ -16,7 +16,8 @@ final class QueryOptions public array $childrenFields = [], public array $parentFields = [], public array $sort = [], - public array $status = ["published", "draft"] + public array $status = ["published", "draft"], + public string $notLinked = "" ) {