From 42dd0b67d6fe7f95aece1c9062d7c8afb1df7cbf Mon Sep 17 00:00:00 2001 From: Tofandel Date: Sat, 24 Feb 2024 00:20:39 +0100 Subject: [PATCH 1/6] Fix: nested repeaters --- frontend/js/components/Repeater.vue | 4 +- frontend/js/components/blocks/BlocksList.js | 2 +- frontend/js/mixins/block.js | 2 +- frontend/js/mixins/blockEditor.js | 5 + frontend/js/utils/getFormData.js | 117 +++++------ jsconfig.json | 8 + src/Repositories/Behaviors/HandleBlocks.php | 37 ++-- .../Behaviors/HandleRepeaters.php | 191 +++++------------- .../integration/Blocks/BlockChildrenTest.php | 86 +++++++- 9 files changed, 218 insertions(+), 234 deletions(-) create mode 100644 jsconfig.json diff --git a/frontend/js/components/Repeater.vue b/frontend/js/components/Repeater.vue index 63355f90a..4c5066328 100755 --- a/frontend/js/components/Repeater.vue +++ b/frontend/js/components/Repeater.vue @@ -123,6 +123,7 @@ handle: '.block__handle' // drag handle } }, + inject: {inContentEditor: {default: false}}, computed: { triggerVariant: function () { if (this.buttonAsLink) { @@ -136,9 +137,6 @@ blockSize: function () { return this.inContentEditor ? 'small' : '' }, - inContentEditor: function () { - return typeof this.$parent.repeaterName !== 'undefined' - }, hasRemainingBlocks: function () { let max = null if (this.max && this.max > 0) { diff --git a/frontend/js/components/blocks/BlocksList.js b/frontend/js/components/blocks/BlocksList.js index 193b849f2..9cbee6754 100644 --- a/frontend/js/components/blocks/BlocksList.js +++ b/frontend/js/components/blocks/BlocksList.js @@ -20,7 +20,7 @@ export default { return this.blocks(this.editorName) }, allSavedBlocks () { - return this.used && Object.keys(this.used).reduce((acc, editorName) => acc.concat(this.used[editorName]), []) + return this.used && Object.values(this.used).flat() }, hasBlockActive () { return Object.keys(this.activeBlock).length > 0 diff --git a/frontend/js/mixins/block.js b/frontend/js/mixins/block.js index 24702df57..b1315049e 100755 --- a/frontend/js/mixins/block.js +++ b/frontend/js/mixins/block.js @@ -22,7 +22,7 @@ export default { return this.name + '[' + id + ']' // output : nameOfBlock[UniqID][name] }, repeaterName: function (id) { - return this.name.replace('[', '-').replace(']', '') + '|' + id // nameOfBlock-UniqID|name + return this.nestedEditorName(id) }, nestedEditorName: function (id) { return this.name.replace('[', '-').replace(']', '') + '|' + id // nameOfBlock-UniqID|name diff --git a/frontend/js/mixins/blockEditor.js b/frontend/js/mixins/blockEditor.js index 0271c24d6..f91e9a99b 100644 --- a/frontend/js/mixins/blockEditor.js +++ b/frontend/js/mixins/blockEditor.js @@ -15,6 +15,11 @@ export default { default: 0 } }, + provide() { + return { + inContentEditor: true, + } + }, methods: { addAndEditBlock (add, edit, { block, index }) { window[process.env.VUE_APP_NAME].PREVSTATE = cloneDeep(this.$store.state) diff --git a/frontend/js/utils/getFormData.js b/frontend/js/utils/getFormData.js index fd358861a..9050e2907 100755 --- a/frontend/js/utils/getFormData.js +++ b/frontend/js/utils/getFormData.js @@ -31,52 +31,59 @@ export const stripOutBlockNamespace = (name, id) => { return nameWithoutBlock.match(/]/gi).length > 1 ? nameWithoutBlock.replace(']', '') : nameWithoutBlock.slice(0, -1) } -export const buildBlock = (block, rootState, isRepeater = false) => { - const repeaterIds = Object.keys(rootState.repeaters.repeaters); - const repeaters = Object.assign({}, ...repeaterIds.filter(repeaterKey => { - return repeaterKey.startsWith('blocks-' + block.id + '|') +export const buildBlock = (block, rootState, isRepeater = false, childKey) => { + const parentRepeaters = rootState.repeaters.repeaters; + const repeaterIds = Object.keys(parentRepeaters); + const prefix = 'blocks-' + block.id + '|'; + const repeaters = repeaterIds.filter(repeaterKey => { + return repeaterKey.startsWith(prefix) }) - .map(repeaterKey => { - return { - [repeaterKey.replace('blocks-' + block.id + '|', '')]: rootState.repeaters.repeaters[repeaterKey].map(repeaterItem => { - return buildBlock(repeaterItem, rootState, true) - }) - } - })) + .reduce((acc, repeaterKey) => { + acc[repeaterKey.replace(prefix, '')] = parentRepeaters[repeaterKey].map(repeaterItem => { + return buildBlock(repeaterItem, rootState, true) + }) + + return acc + }, {}) const blockIds = Object.keys(rootState.blocks.blocks); - const blocks = Object.assign({}, ...blockIds.filter(blockKey => { - return blockKey.startsWith('blocks-' + block.id) - }).map(blockKey => { + const blocks = blockIds.filter(blockKey => { + return blockKey.startsWith(prefix) + }).reduce((acc, blockKey) => { + acc.push(...rootState.blocks.blocks[blockKey].map(repeaterItem => { + if (isRepeater) { + repeaterItem = {...repeaterItem, name: repeaterItem.name.replace(prefix, '')} + } + return buildBlock(repeaterItem, rootState, false, blockKey.replace(prefix, '')) + })); + return acc; + }, []) + + // retrieve all fields for this block and clean up field names + const content = rootState.form.fields.filter((field) => { + return isBlockField(field.name, block.id) + }).map((field) => { return { - [blockKey.replace('blocks-' + block.id + '|', '')]: rootState.blocks.blocks[blockKey].map(repeaterItem => { - return buildBlock(repeaterItem, rootState) - }) + name: stripOutBlockNamespace(field.name, block.id), + value: field.value } - })) + }).reduce((content, field) => { + content[field.name] = field.value + return content + }, {}); - return { + const base = { id: block.id, - type: block.type, - is_repeater: isRepeater, editor_name: block.name, - // retrieve all fields for this block and clean up field names - content: rootState.form.fields.filter((field) => { - return isBlockField(field.name, block.id) - }).map((field) => { - return { - name: stripOutBlockNamespace(field.name, block.id), - value: field.value - } - }).reduce((content, field) => { - content[field.name] = field.value - return content - }, {}), medias: gatherSelected(rootState.mediaLibrary.selected, block), browsers: gatherSelected(rootState.browser.selected, block), // gather repeater blocks from the repeater store module - blocks: { ...repeaters, ...blocks } + blocks, + repeaters, } + return isRepeater + ? { ...content, ...base, is_repeater: true, repeater_target_id: block.repeater_target_id} + : { ...base, type: block.type, content, child_key: childKey } } export const isBlockEmpty = (blockData) => { @@ -84,30 +91,16 @@ export const isBlockEmpty = (blockData) => { } export const gatherRepeaters = (rootState) => { - return Object.assign({}, ...Object.keys(rootState.repeaters.repeaters).filter(repeaterKey => { + return Object.keys(rootState.repeaters.repeaters).filter(repeaterKey => { // we start by filtering out repeater blocks return !repeaterKey.startsWith('blocks-') - }).map(repeater => { - return { - [repeater]: rootState.repeaters.repeaters[repeater].map(repeaterItem => { - // and for each repeater we build a block for each item - const repeaterBlock = buildBlock(repeaterItem, rootState) - - // we want to inline fields in the repeater object - // and we don't need the type of component used - const fields = repeaterBlock.content - delete repeaterBlock.content - delete repeaterBlock.type - - // and lastly we want to keep the id to update existing items - fields.id = repeaterItem.id - // If the repeater has a target id we are referencing an existing item. - fields.repeater_target_id = repeaterItem.repeater_target_id ?? null - - return Object.assign(repeaterBlock, fields) - }) - } - })) + }).reduce((acc, repeater) => { + acc[repeater] = rootState.repeaters.repeaters[repeater].map(repeaterItem => { + // and for each repeater we build a block for each item + return buildBlock(repeaterItem, rootState, true) + }) + return acc; + }, {}) } export const gatherBlocks = (rootState) => { @@ -124,7 +117,7 @@ export const gatherBlocks = (rootState) => { } export const getFormFields = (rootState) => { - const fields = rootState.form.fields.filter((field) => { + return rootState.form.fields.filter((field) => { // we start by filtering out blocks related form fields return !field.name.startsWith('blocks[') && !field.name.startsWith('mediaMeta[') }).reduce((fields, field) => { @@ -133,12 +126,10 @@ export const getFormFields = (rootState) => { fields[field.name] = field.value return fields }, {}) - - return fields } export const getModalFormFields = (rootState) => { - const fields = rootState.form.modalFields.filter((field) => { + return rootState.form.modalFields.filter((field) => { // we start by filtering out blocks related form fields return !field.name.startsWith('blocks[') && !field.name.startsWith('mediaMeta[') }).reduce((fields, field) => { @@ -147,8 +138,6 @@ export const getModalFormFields = (rootState) => { fields[field.name] = field.value return fields }, {}) - - return fields } export const getFormData = (rootState) => { @@ -159,7 +148,7 @@ export const getFormData = (rootState) => { // - publication properties // - selected medias and browsers // - created blocks and repeaters - const data = Object.assign(fields, { + return Object.assign(fields, { cmsSaveType: rootState.form.type, published: rootState.publication.published, public: rootState.publication.visibility === 'public', @@ -172,6 +161,4 @@ export const getFormData = (rootState) => { blocks: gatherBlocks(rootState), repeaters: gatherRepeaters(rootState) }) - - return data } diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..9ab9f6f5d --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./frontend/js/*"] + } + } +} diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index d453bde92..ebb96a01c 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -13,6 +13,7 @@ use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; use Illuminate\Validation\ValidationException; use Symfony\Component\Routing\Exception\RouteNotFoundException; @@ -272,22 +273,32 @@ private function getChildBlocks($object, $parentBlockFields) { $childBlocksList = Collection::make(); - foreach ($parentBlockFields['blocks'] ?? [] as $childKey => $childBlocks) { - if (strpos($childKey, '|')) { - continue; - } - foreach ($childBlocks as $index => $childBlock) { - $childBlock = $this->buildBlock($childBlock, $object, $childBlock['is_repeater'] ?? true); - $this->validateBlockArray($childBlock, $childBlock['instance'], true); - $childBlock['child_key'] = $childKey; - $childBlock['position'] = $index + 1; - $childBlock['editor_name'] = $parentBlockFields['editor_name'] ?? 'default'; - $childBlock['blocks'] = $this->getChildBlocks($object, $childBlock); - - $childBlocksList->push($childBlock); + if (empty($parentBlockFields['blocks'])) { + return $childBlocksList; + } + + // Fallback if frontend or revision is still on the old schema + if (is_int(key(current($parentBlockFields['blocks'])))) { + foreach ($parentBlockFields['blocks'] as $childKey => $childBlocks) { + foreach ($childBlocks as $childBlock) { + $childBlock['child_key'] = $childKey; + $parentBlockFields['blocks'][] = $childBlock; + } + unset($parentBlockFields['blocks'][$childKey]); } } + foreach ($parentBlockFields['blocks'] as $index => $childBlock) { + $childBlock = $this->buildBlock($childBlock, $object, $childBlock['is_repeater'] ?? false); + $this->validateBlockArray($childBlock, $childBlock['instance'], true); + $childBlock['child_key'] = $childBlock['child_key'] ?? Str::afterLast($childBlock['editor_name'] ?? $index, '|'); + $childBlock['position'] = $index + 1; + $childBlock['editor_name'] = $parentBlockFields['editor_name'] ?? 'default'; + $childBlock['blocks'] = $this->getChildBlocks($object, $childBlock); + + $childBlocksList->push($childBlock); + } + return $childBlocksList; } diff --git a/src/Repositories/Behaviors/HandleRepeaters.php b/src/Repositories/Behaviors/HandleRepeaters.php index 718ba09bb..e76b87a04 100644 --- a/src/Repositories/Behaviors/HandleRepeaters.php +++ b/src/Repositories/Behaviors/HandleRepeaters.php @@ -9,6 +9,9 @@ use A17\Twill\Repositories\ModuleRepository; use Carbon\Carbon; use Exception; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasOneOrMany; +use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -280,13 +283,22 @@ public function updateRepeater( $relationRepository = $this->getModelRepository($relation, $modelOrRepository); + if (method_exists($this->model, $relation)) { + /** @var Relation $relationInstance */ + $relationInstance = $this->model->$relation(); + if ($relationInstance instanceof BelongsTo || $relationInstance instanceof HasOneOrMany) { + $fk = $relationInstance->getForeignKeyName(); + } + } + $fk ??= $this->model->getForeignKey(); + // If no relation field submitted, soft deletes all associated rows. // We only do this when the model is already existing. if (! $relationFields && ! $object->wasRecentlyCreated) { $relationRepository->updateBasic(null, [ 'deleted_at' => Carbon::now(), ], [ - $this->model->getForeignKey() => $object->id, + $fk => $object->id, ]); } @@ -322,7 +334,7 @@ public function updateRepeater( $currentIdList[] = (int)$id; } else { // new row, let's attach to our object and create - $relationField[$this->model->getForeignKey()] = $object->id; + $relationField[$fk] = $object->id; $frontEndId = $relationField['id']; unset($relationField['id']); $newRelation = $relationRepository->create($relationField); @@ -358,9 +370,6 @@ private function encodePivotFields(array $fields): array return $fields; } - /** - * @todo: This is currently a massive duplication, once done, this needs to be cleaned up - */ public function getFormFieldForRepeaterWithPivot( TwillModelContract $object, array $fields, @@ -368,6 +377,17 @@ public function getFormFieldForRepeaterWithPivot( array $pivotFields, null|string|TwillModelContract|ModuleRepository $modelOrRepository = null, ?string $repeaterName = null + ): array { + return $this->getFormFieldsShared($object, $fields, $relation, $pivotFields, $modelOrRepository, $repeaterName); + } + + private function getFormFieldsShared( + TwillModelContract $object, + array $fields, + string $relation, + array $pivotFields, + null|string|TwillModelContract|ModuleRepository $modelOrRepository = null, + ?string $repeaterName = null ): array { if (! $repeaterName) { $repeaterName = $relation; @@ -382,11 +402,15 @@ public function getFormFieldForRepeaterWithPivot( $repeaterType = TwillBlocks::findRepeaterByName($repeaterName); - $pivotFields[] = 'id'; - $objects = $object->$relation()->withPivot($pivotFields)->get(); + if (!empty($pivotFields)) { + $pivotFields[] = 'id'; + $objects = $object->$relation()->withPivot($pivotFields)->get(); + } else { + $objects = $object->$relation; + } foreach ($objects as $relationItem) { - $pivotRowId = $relationItem->pivot->id; + $pivotRowId = !empty($pivotFields) ? $relationItem->pivot->id : $relationItem->id; $repeaters[] = [ 'id' => $relation . '-' . $pivotRowId, 'type' => $repeaterType->component, @@ -409,7 +433,6 @@ public function getFormFieldForRepeaterWithPivot( } } - // @todo: Can we make this work without custom pivot tables? if (isset($relatedItemFormFields['medias'])) { if (config('twill.media_library.translated_form_fields', false)) { Collection::make($relatedItemFormFields['medias'])->each( @@ -430,7 +453,6 @@ function ($medias, $role) use ($locale, $relation, $relationItem) { } } - // @todo: Can we make this work without custom pivot tables? if (isset($relatedItemFormFields['files'])) { Collection::make($relatedItemFormFields['files'])->each( function ($rolesWithFiles, $locale) use (&$repeatersFiles, $relation, $relationItem) { @@ -445,7 +467,6 @@ function ($files, $role) use ($locale, $relation, $relationItem) { ); } - // @todo: Can we make this work without custom pivot tables? if (isset($relatedItemFormFields['browsers'])) { foreach ($relatedItemFormFields['browsers'] as $key => $values) { $repeatersBrowsers["blocks[$relation-$relationItem->id][$key]"] = $values; @@ -471,25 +492,33 @@ function ($files, $role) use ($locale, $relation, $relationItem) { ]; } - // @todo: Can we make this work without custom pivot tables? + foreach ($relatedItemFormFields['blocks'] ?? [] as $key => $block) { + $fields['blocks'][str_contains($key, '|') ? $key : "blocks-$relation-{$relationItem->id}|$key"] = $block; + } + $fields['blocksFields'] = array_merge($fields['blocksFields'] ?? [], $relatedItemFormFields['blocksFields'] ?? []); + if (isset($relatedItemFormFields['repeaters'])) { foreach ($relatedItemFormFields['repeaters'] as $childRepeaterName => $childRepeaterItems) { - $fields['repeaters']["blocks-$relation-{$relationItem->id}_$childRepeaterName"] = $childRepeaterItems; + if (str_contains($childRepeaterName, '|')) { + $fields['repeaters'][$childRepeaterName] = $childRepeaterItems; + continue; + } + $fields['repeaters']["blocks-$relation-{$relationItem->id}|$childRepeaterName"] = $childRepeaterItems; $repeatersFields = array_merge( $repeatersFields, - $relatedItemFormFields['repeaterFields'][$childRepeaterName] + $relatedItemFormFields['repeaterFields'][$childRepeaterName] ?? [] ); $repeatersMedias = array_merge( $repeatersMedias, - $relatedItemFormFields['repeaterMedias'][$childRepeaterName] + $relatedItemFormFields['repeaterMedias'][$childRepeaterName] ?? [] ); $repeatersFiles = array_merge( $repeatersFiles, - $relatedItemFormFields['repeaterFiles'][$childRepeaterName] + $relatedItemFormFields['repeaterFiles'][$childRepeaterName] ?? [] ); $repeatersBrowsers = array_merge( $repeatersBrowsers, - $relatedItemFormFields['repeaterBrowsers'][$childRepeaterName] + $relatedItemFormFields['repeaterBrowsers'][$childRepeaterName] ?? [] ); } } @@ -522,133 +551,7 @@ public function getFormFieldsForRepeater( null|string|TwillModelContract|ModuleRepository $modelOrRepository = null, ?string $repeaterName = null ): array { - if (! $repeaterName) { - $repeaterName = $relation; - } - - $repeaters = []; - $repeatersFields = []; - $repeatersBrowsers = []; - $repeatersMedias = []; - $repeatersFiles = []; - $relationRepository = $this->getModelRepository($relation, $modelOrRepository); - - $repeaterType = TwillBlocks::findRepeaterByName($repeaterName); - - $objects = $object->$relation; - - foreach ($objects as $relationItem) { - $repeaters[] = [ - 'id' => $relation . '-' . $relationItem->id, - 'type' => $repeaterType->component, - 'title' => $repeaterType->title, - 'titleField' => $repeaterType->titleField, - 'hideTitlePrefix' => $repeaterType->hideTitlePrefix, - ]; - - $relatedItemFormFields = $relationRepository->getFormFields($relationItem); - $translatedFields = []; - - if (isset($relatedItemFormFields['translations'])) { - foreach ($relatedItemFormFields['translations'] as $key => $values) { - $repeatersFields[] = [ - 'name' => "blocks[$relation-$relationItem->id][$key]", - 'value' => $values, - ]; - - $translatedFields[] = $key; - } - } - - if (isset($relatedItemFormFields['medias'])) { - if (config('twill.media_library.translated_form_fields', false)) { - Collection::make($relatedItemFormFields['medias'])->each( - function ($rolesWithMedias, $locale) use (&$repeatersMedias, $relation, $relationItem) { - $repeatersMedias[] = Collection::make($rolesWithMedias)->mapWithKeys( - function ($medias, $role) use ($locale, $relation, $relationItem) { - return [ - "blocks[$relation-$relationItem->id][$role][$locale]" => $medias, - ]; - } - )->toArray(); - } - ); - } else { - foreach ($relatedItemFormFields['medias'] as $key => $values) { - $repeatersMedias["blocks[$relation-$relationItem->id][$key]"] = $values; - } - } - } - - if (isset($relatedItemFormFields['files'])) { - Collection::make($relatedItemFormFields['files'])->each( - function ($rolesWithFiles, $locale) use (&$repeatersFiles, $relation, $relationItem) { - $repeatersFiles[] = Collection::make($rolesWithFiles)->mapWithKeys( - function ($files, $role) use ($locale, $relation, $relationItem) { - return [ - "blocks[$relation-$relationItem->id][$role][$locale]" => $files, - ]; - } - )->toArray(); - } - ); - } - - if (isset($relatedItemFormFields['browsers'])) { - foreach ($relatedItemFormFields['browsers'] as $key => $values) { - $repeatersBrowsers["blocks[$relation-$relationItem->id][$key]"] = $values; - } - } - - $itemFields = method_exists($relationItem, 'toRepeaterArray') ? - $relationItem->toRepeaterArray() : - Arr::except($relationItem->attributesToArray(), $translatedFields); - - foreach ($itemFields as $key => $value) { - $repeatersFields[] = [ - 'name' => "blocks[$relation-$relationItem->id][$key]", - 'value' => $value, - ]; - } - - if (isset($relatedItemFormFields['repeaters'])) { - foreach ($relatedItemFormFields['repeaters'] as $childRepeaterName => $childRepeaterItems) { - $fields['repeaters']["blocks-$relation-{$relationItem->id}_$childRepeaterName"] = $childRepeaterItems; - $repeatersFields = array_merge( - $repeatersFields, - $relatedItemFormFields['repeaterFields'][$childRepeaterName] - ); - $repeatersMedias = array_merge( - $repeatersMedias, - $relatedItemFormFields['repeaterMedias'][$childRepeaterName] - ); - $repeatersFiles = array_merge( - $repeatersFiles, - $relatedItemFormFields['repeaterFiles'][$childRepeaterName] - ); - $repeatersBrowsers = array_merge( - $repeatersBrowsers, - $relatedItemFormFields['repeaterBrowsers'][$childRepeaterName] - ); - } - } - } - - if (! empty($repeatersMedias) && config('twill.media_library.translated_form_fields', false)) { - $repeatersMedias = array_merge(...$repeatersMedias); - } - - if (! empty($repeatersFiles)) { - $repeatersFiles = array_merge(...$repeatersFiles); - } - - $fields['repeaters'][$repeaterName] = $repeaters; - $fields['repeaterFields'][$repeaterName] = $repeatersFields; - $fields['repeaterMedias'][$repeaterName] = $repeatersMedias; - $fields['repeaterFiles'][$repeaterName] = $repeatersFiles; - $fields['repeaterBrowsers'][$repeaterName] = $repeatersBrowsers; - - return $fields; + return $this->getFormFieldsShared($object, $fields, $relation, [], $modelOrRepository, $repeaterName); } private function decodePivotField(?string $data): null|array|string diff --git a/tests/integration/Blocks/BlockChildrenTest.php b/tests/integration/Blocks/BlockChildrenTest.php index 870870dea..cf9d4b440 100644 --- a/tests/integration/Blocks/BlockChildrenTest.php +++ b/tests/integration/Blocks/BlockChildrenTest.php @@ -8,7 +8,7 @@ class BlockChildrenTest extends TestCase { - public function testSorting(): void + public function testBCOfBlockSchema(): void { $module = AnonymousModule::make('childrenservers', $this->app) ->boot(); @@ -50,8 +50,80 @@ public function testSorting(): void 'quote' => 'This is the nested quote at position 1.', 'author' => 'This is the nested author at position 1.', ], - 'id' => time() + 1, + 'id' => time() + 2, + ], + ] + ], + 'type' => 'a17-block-quote', + 'content' => [ + 'quote' => 'This is the quote.', + 'author' => 'This is the author.', + ], + 'id' => time(), + ], + ], + ]; + + $update = $repository->update($server->id, $blocks); + + // Check the nested child order. + $this->assertEquals( + 'This is the nested quote at position 2.', + $update->blocks[0]->children[0]->content['quote'] + ); + $this->assertEquals( + 'This is the nested quote at position 1.', + $update->blocks[0]->children[1]->content['quote'] + ); + } + + public function testSorting(): void + { + $module = AnonymousModule::make('childrenservers', $this->app) + ->boot(); + + /** @var ModuleRepository $repository */ + $repository = app()->make($module->getRepositoryClassName()); + + $server = $repository->create([ + 'title' => 'Hello world', + 'published' => true, + ]); + + $blocks = [ + 'blocks' => [ + [ + 'editor_name' => 'description', + 'browsers' => [], + 'medias' => [], + 'blocks' => [ + [ + 'browsers' => [], + 'medias' => [], + 'blocks' => [], + 'child_key' => 'content', + 'editor_name' => 'blocks-yada-1|content', + 'type' => 'a17-block-quote', + 'is_repeater' => false, + 'content' => [ + 'quote' => 'This is the nested quote at position 2.', + 'author' => 'This is the nested author at position 2.', + ], + 'id' => time() + 1, + ], + [ + 'browsers' => [], + 'medias' => [], + 'blocks' => [], + 'child_key' => 'content', + 'editor_name' => 'blocks-yada-1|content', + 'type' => 'a17-block-quote', + 'is_repeater' => false, + 'content' => [ + 'quote' => 'This is the nested quote at position 1.', + 'author' => 'This is the nested author at position 1.', ], + 'id' => time() + 2, ], ], 'type' => 'a17-block-quote', @@ -78,14 +150,14 @@ public function testSorting(): void // Now we update it a second time, but we update the order. $blocksUpdate = $blocks; - $blocksUpdate['blocks'][0]['blocks'][0][0]['id'] = $update->blocks[1]->id; - $blocksUpdate['blocks'][0]['blocks'][0][1]['id'] = $update->blocks[2]->id; + $blocksUpdate['blocks'][0]['blocks'][0]['id'] = $update->blocks[1]->id; + $blocksUpdate['blocks'][0]['blocks'][1]['id'] = $update->blocks[2]->id; // We now swap them so their actual position is correct. - $backup = $blocksUpdate['blocks'][0]['blocks'][0][0]; + $backup = $blocksUpdate['blocks'][0]['blocks'][0]; - $blocksUpdate['blocks'][0]['blocks'][0][0] = $blocksUpdate['blocks'][0]['blocks'][0][1]; - $blocksUpdate['blocks'][0]['blocks'][0][1] = $backup; + $blocksUpdate['blocks'][0]['blocks'][0] = $blocksUpdate['blocks'][0]['blocks'][1]; + $blocksUpdate['blocks'][0]['blocks'][1] = $backup; $update = $repository->update($server->id, $blocksUpdate); From 90fb5821934d82add1f172cb5457f5d4caee67b4 Mon Sep 17 00:00:00 2001 From: Tofandel Date: Wed, 28 Feb 2024 18:48:06 +0100 Subject: [PATCH 2/6] fix: also support nested browsers and medias --- src/Repositories/Behaviors/HandleRepeaters.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Repositories/Behaviors/HandleRepeaters.php b/src/Repositories/Behaviors/HandleRepeaters.php index e76b87a04..92b90036a 100644 --- a/src/Repositories/Behaviors/HandleRepeaters.php +++ b/src/Repositories/Behaviors/HandleRepeaters.php @@ -495,7 +495,11 @@ function ($files, $role) use ($locale, $relation, $relationItem) { foreach ($relatedItemFormFields['blocks'] ?? [] as $key => $block) { $fields['blocks'][str_contains($key, '|') ? $key : "blocks-$relation-{$relationItem->id}|$key"] = $block; } - $fields['blocksFields'] = array_merge($fields['blocksFields'] ?? [], $relatedItemFormFields['blocksFields'] ?? []); + foreach (['Fields', 'Medias', 'Files', 'Browsers'] as $fieldKey) { + if (!empty($relatedItemFormFields['blocks'.$fieldKey])) { + $fields['blocks'.$fieldKey] = array_merge($fields['blocks'.$fieldKey] ?? [], $relatedItemFormFields['blocks'.$fieldKey]); + } + } if (isset($relatedItemFormFields['repeaters'])) { foreach ($relatedItemFormFields['repeaters'] as $childRepeaterName => $childRepeaterItems) { From a809d4248caa69df63fff0597410e5ea558aa587 Mon Sep 17 00:00:00 2001 From: Tofandel Date: Wed, 28 Feb 2024 20:00:19 +0100 Subject: [PATCH 3/6] cleanup --- src/Repositories/Behaviors/HandleBlocks.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index ebb96a01c..82ab8bf02 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -432,20 +432,11 @@ function ($files, $role) use ($locale, $block) { } } - if ($fields['blocksFields'] ?? false) { - $fields['blocksFields'] = call_user_func_array('array_merge', $fields['blocksFields'] ?? []); - } - - if ($fields['blocksMedias'] ?? false) { - $fields['blocksMedias'] = call_user_func_array('array_merge', $fields['blocksMedias'] ?? []); - } - if ($fields['blocksFiles'] ?? false) { - $fields['blocksFiles'] = call_user_func_array('array_merge', $fields['blocksFiles'] ?? []); - } - - if ($fields['blocksBrowsers'] ?? false) { - $fields['blocksBrowsers'] = call_user_func_array('array_merge', $fields['blocksBrowsers'] ?? []); + foreach (['Fields', 'Medias', 'Files', 'Browsers'] as $fieldKey) { + if ($fields['blocks'.$fieldKey] ?? false) { + $fields['blocks'.$fieldKey] = call_user_func_array('array_merge', $fields['blocks'.$fieldKey] ?? []); + } } } From 3002eb5731efae1e31125f35679ef0ed4f93bbcf Mon Sep 17 00:00:00 2001 From: Tofandel Date: Wed, 28 Feb 2024 21:50:00 +0100 Subject: [PATCH 4/6] Fix repeaters inside blocks --- frontend/js/utils/getFormData.js | 7 +++--- src/Repositories/Behaviors/HandleBlocks.php | 25 ++++++++++++------- .../Behaviors/HandleRepeaters.php | 25 ++++++++++++++++--- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/frontend/js/utils/getFormData.js b/frontend/js/utils/getFormData.js index 9050e2907..23a0a8298 100755 --- a/frontend/js/utils/getFormData.js +++ b/frontend/js/utils/getFormData.js @@ -74,16 +74,17 @@ export const buildBlock = (block, rootState, isRepeater = false, childKey) => { const base = { id: block.id, - editor_name: block.name, + type: block.type, medias: gatherSelected(rootState.mediaLibrary.selected, block), browsers: gatherSelected(rootState.browser.selected, block), + content, // gather repeater blocks from the repeater store module blocks, repeaters, } return isRepeater - ? { ...content, ...base, is_repeater: true, repeater_target_id: block.repeater_target_id} - : { ...base, type: block.type, content, child_key: childKey } + ? { ...base, is_repeater: true, repeater_target_id: block.repeater_target_id} + : { ...base, editor_name: block.name, child_key: childKey } } export const isBlockEmpty = (blockData) => { diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index 82ab8bf02..f38d9b386 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -273,22 +273,29 @@ private function getChildBlocks($object, $parentBlockFields) { $childBlocksList = Collection::make(); - if (empty($parentBlockFields['blocks'])) { - return $childBlocksList; + if (!empty($parentBlockFields['blocks'])) { + // Fallback if frontend or revision is still on the old schema + if (is_int(key(current($parentBlockFields['blocks'])))) { + foreach ($parentBlockFields['blocks'] as $childKey => $childBlocks) { + foreach ($childBlocks as $childBlock) { + $childBlock['child_key'] = $childKey; + $parentBlockFields['blocks'][] = $childBlock; + } + unset($parentBlockFields['blocks'][$childKey]); + } + } } - // Fallback if frontend or revision is still on the old schema - if (is_int(key(current($parentBlockFields['blocks'])))) { - foreach ($parentBlockFields['blocks'] as $childKey => $childBlocks) { + if (!empty($parentBlockFields['repeaters'])) { + foreach ($parentBlockFields['repeaters'] as $childKey => $childBlocks) { foreach ($childBlocks as $childBlock) { $childBlock['child_key'] = $childKey; $parentBlockFields['blocks'][] = $childBlock; } - unset($parentBlockFields['blocks'][$childKey]); } } - foreach ($parentBlockFields['blocks'] as $index => $childBlock) { + foreach ($parentBlockFields['blocks'] ?? [] as $index => $childBlock) { $childBlock = $this->buildBlock($childBlock, $object, $childBlock['is_repeater'] ?? false); $this->validateBlockArray($childBlock, $childBlock['instance'], true); $childBlock['child_key'] = $childBlock['child_key'] ?? Str::afterLast($childBlock['editor_name'] ?? $index, '|'); @@ -434,8 +441,8 @@ function ($files, $role) use ($locale, $block) { foreach (['Fields', 'Medias', 'Files', 'Browsers'] as $fieldKey) { - if ($fields['blocks'.$fieldKey] ?? false) { - $fields['blocks'.$fieldKey] = call_user_func_array('array_merge', $fields['blocks'.$fieldKey] ?? []); + if ($fields['blocks' . $fieldKey] ?? false) { + $fields['blocks' . $fieldKey] = call_user_func_array('array_merge', $fields['blocks' . $fieldKey] ?? []); } } } diff --git a/src/Repositories/Behaviors/HandleRepeaters.php b/src/Repositories/Behaviors/HandleRepeaters.php index 92b90036a..5fe225073 100644 --- a/src/Repositories/Behaviors/HandleRepeaters.php +++ b/src/Repositories/Behaviors/HandleRepeaters.php @@ -131,6 +131,12 @@ public function updateRepeaterMorphMany( $relationField[$morphFieldId] = $object->id; $relationField[$morphFieldType] = $object->getMorphClass(); + if (is_array($relationField['content'] ?? null)) { + $content = $relationField['content']; + unset($relationField['content']); + $relationField = array_merge($content, $relationField); + } + if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) { // row already exists, let's update $id = str_replace($relation . '-', '', $relationField['id']); @@ -210,6 +216,12 @@ public function updateRepeaterWithPivot( } } + if (is_array($relationField['content'] ?? null)) { + $content = $relationField['content']; + unset($relationField['content']); + $relationField = array_merge($content, $relationField); + } + if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) { // row already exists, let's update, the $id is the id in the pivot table. $pivotRowId = str_replace($relation . '-', '', $relationField['id']); @@ -307,6 +319,7 @@ public function updateRepeater( foreach ($relationFields as $index => $relationField) { $relationField['position'] = $index + 1; + // If the relation is not an "existing" one try to match it with our session. if ( ! Str::startsWith($relationField['id'], $relation) && @@ -315,6 +328,12 @@ public function updateRepeater( $relationField['id'] = $relation . '-' . $id; } + if (is_array($relationField['content'] ?? null)) { + $content = $relationField['content']; + unset($relationField['content']); + $relationField = array_merge($content, $relationField); + } + // Set the active data based on the parent. if (! isset($relationField['languages']) && isset($relationField['active'])) { foreach (array_keys($relationField['active']) as $langCode) { @@ -495,9 +514,9 @@ function ($files, $role) use ($locale, $relation, $relationItem) { foreach ($relatedItemFormFields['blocks'] ?? [] as $key => $block) { $fields['blocks'][str_contains($key, '|') ? $key : "blocks-$relation-{$relationItem->id}|$key"] = $block; } - foreach (['Fields', 'Medias', 'Files', 'Browsers'] as $fieldKey) { - if (!empty($relatedItemFormFields['blocks'.$fieldKey])) { - $fields['blocks'.$fieldKey] = array_merge($fields['blocks'.$fieldKey] ?? [], $relatedItemFormFields['blocks'.$fieldKey]); + foreach (['Fields', 'Medias', 'Files', 'Browsers', 'Repeaters'] as $fieldKey) { + if (!empty($relatedItemFormFields['blocks' . $fieldKey])) { + $fields['blocks' . $fieldKey] = array_merge($fields['blocks' . $fieldKey] ?? [], $relatedItemFormFields['blocks' . $fieldKey]); } } From 49ecfadcdb20d984729bf20894600de3fd078c51 Mon Sep 17 00:00:00 2001 From: Tofandel Date: Thu, 29 Feb 2024 10:38:27 +0100 Subject: [PATCH 5/6] Fix all BC breaks --- frontend/js/utils/getFormData.js | 53 ++++++------ src/Repositories/Behaviors/HandleBlocks.php | 45 ++++------ .../Behaviors/HandleRepeaters.php | 18 ---- .../integration/Blocks/BlockChildrenTest.php | 85 ++----------------- 4 files changed, 47 insertions(+), 154 deletions(-) diff --git a/frontend/js/utils/getFormData.js b/frontend/js/utils/getFormData.js index 23a0a8298..f08636813 100755 --- a/frontend/js/utils/getFormData.js +++ b/frontend/js/utils/getFormData.js @@ -31,17 +31,17 @@ export const stripOutBlockNamespace = (name, id) => { return nameWithoutBlock.match(/]/gi).length > 1 ? nameWithoutBlock.replace(']', '') : nameWithoutBlock.slice(0, -1) } -export const buildBlock = (block, rootState, isRepeater = false, childKey) => { - const parentRepeaters = rootState.repeaters.repeaters; - const repeaterIds = Object.keys(parentRepeaters); +export const buildBlock = (block, rootState, isRepeater = false, isInsideRepeater = isRepeater) => { + const repeaterIds = Object.keys(rootState.repeaters.repeaters); const prefix = 'blocks-' + block.id + '|'; const repeaters = repeaterIds.filter(repeaterKey => { return repeaterKey.startsWith(prefix) }) .reduce((acc, repeaterKey) => { - acc[repeaterKey.replace(prefix, '')] = parentRepeaters[repeaterKey].map(repeaterItem => { - return buildBlock(repeaterItem, rootState, true) - }) + acc[repeaterKey.replace(prefix, '')] = rootState.repeaters.repeaters[repeaterKey] + .map(repeaterItem => { + return buildBlock(repeaterItem, rootState, true, isRepeater) + }) return acc }, {}) @@ -50,41 +50,38 @@ export const buildBlock = (block, rootState, isRepeater = false, childKey) => { const blocks = blockIds.filter(blockKey => { return blockKey.startsWith(prefix) }).reduce((acc, blockKey) => { - acc.push(...rootState.blocks.blocks[blockKey].map(repeaterItem => { - if (isRepeater) { - repeaterItem = {...repeaterItem, name: repeaterItem.name.replace(prefix, '')} + const key = blockKey.replace(prefix, ''); + rootState.blocks.blocks[blockKey].forEach(blockItem => { + const block = buildBlock(blockItem, rootState, false); + if (isInsideRepeater) { + acc.push(block); + } else { + if (!acc[key]) { + acc[key] = []; + } + acc[key].push(block) } - return buildBlock(repeaterItem, rootState, false, blockKey.replace(prefix, '')) - })); + }) return acc; - }, []) + }, isInsideRepeater ? [] : {}) // retrieve all fields for this block and clean up field names const content = rootState.form.fields.filter((field) => { return isBlockField(field.name, block.id) - }).map((field) => { - return { - name: stripOutBlockNamespace(field.name, block.id), - value: field.value - } - }).reduce((content, field) => { - content[field.name] = field.value - return content - }, {}); + }).reduce((acc, field) => { + acc[stripOutBlockNamespace(field.name, block.id)] = field.value; + return acc; + }, {}) const base = { id: block.id, - type: block.type, medias: gatherSelected(rootState.mediaLibrary.selected, block), browsers: gatherSelected(rootState.browser.selected, block), - content, // gather repeater blocks from the repeater store module - blocks, - repeaters, } - return isRepeater - ? { ...base, is_repeater: true, repeater_target_id: block.repeater_target_id} - : { ...base, editor_name: block.name, child_key: childKey } + return isInsideRepeater + ? { ...content, ...base, repeater_target_id: block.repeater_target_id, blocks, repeaters} + : { ...base, content, is_repeater: isRepeater, type: block.type, editor_name: block.name, blocks: {...blocks, ...repeaters} } } export const isBlockEmpty = (blockData) => { diff --git a/src/Repositories/Behaviors/HandleBlocks.php b/src/Repositories/Behaviors/HandleBlocks.php index f38d9b386..73e7e4110 100644 --- a/src/Repositories/Behaviors/HandleBlocks.php +++ b/src/Repositories/Behaviors/HandleBlocks.php @@ -273,39 +273,22 @@ private function getChildBlocks($object, $parentBlockFields) { $childBlocksList = Collection::make(); - if (!empty($parentBlockFields['blocks'])) { - // Fallback if frontend or revision is still on the old schema - if (is_int(key(current($parentBlockFields['blocks'])))) { - foreach ($parentBlockFields['blocks'] as $childKey => $childBlocks) { - foreach ($childBlocks as $childBlock) { - $childBlock['child_key'] = $childKey; - $parentBlockFields['blocks'][] = $childBlock; - } - unset($parentBlockFields['blocks'][$childKey]); - } + foreach ($parentBlockFields['blocks'] ?? [] as $childKey => $childBlocks) { + if (str_contains($childKey, '|')) { + continue; } - } - - if (!empty($parentBlockFields['repeaters'])) { - foreach ($parentBlockFields['repeaters'] as $childKey => $childBlocks) { - foreach ($childBlocks as $childBlock) { - $childBlock['child_key'] = $childKey; - $parentBlockFields['blocks'][] = $childBlock; - } + foreach ($childBlocks as $index => $childBlock) { + $childBlock = $this->buildBlock($childBlock, $object, $childBlock['is_repeater'] ?? false); + $this->validateBlockArray($childBlock, $childBlock['instance'], true); + $childBlock['child_key'] = $childKey; + $childBlock['position'] = $index + 1; + $childBlock['editor_name'] = $parentBlockFields['editor_name'] ?? 'default'; + $childBlock['blocks'] = $this->getChildBlocks($object, $childBlock); + + $childBlocksList->push($childBlock); } } - foreach ($parentBlockFields['blocks'] ?? [] as $index => $childBlock) { - $childBlock = $this->buildBlock($childBlock, $object, $childBlock['is_repeater'] ?? false); - $this->validateBlockArray($childBlock, $childBlock['instance'], true); - $childBlock['child_key'] = $childBlock['child_key'] ?? Str::afterLast($childBlock['editor_name'] ?? $index, '|'); - $childBlock['position'] = $index + 1; - $childBlock['editor_name'] = $parentBlockFields['editor_name'] ?? 'default'; - $childBlock['blocks'] = $this->getChildBlocks($object, $childBlock); - - $childBlocksList->push($childBlock); - } - return $childBlocksList; } @@ -482,7 +465,7 @@ protected function getBlockBrowsers($block) try { $relationRepository = $this->getModelRepository($relation); $relatedItems = $relationRepository->get([], ['id' => $ids], [], -1); - } catch (\Throwable $th) { + } catch (\Throwable) { $relatedItems = collect(); } $sortedRelatedItems = array_flip($ids); @@ -494,6 +477,7 @@ protected function getBlockBrowsers($block) $items = Collection::make(array_values($sortedRelatedItems))->filter(function ($value) { return is_object($value); })->map(function ($relatedElement) use ($relation) { + // TODO this needs refactoring, it's duplicated from HandleBrowsers return [ 'id' => $relatedElement->id, 'name' => $relatedElement->titleInBrowser ?? $relatedElement->title, @@ -503,6 +487,7 @@ protected function getBlockBrowsers($block) 'edit', $relatedElement->id ), + 'endpointType' => $relatedElement->getMorphClass(), ] + (classHasTrait($relatedElement, HasMedias::class) ? [ 'thumbnail' => $relatedElement->defaultCmsImage(['w' => 100, 'h' => 100]), ] : []); diff --git a/src/Repositories/Behaviors/HandleRepeaters.php b/src/Repositories/Behaviors/HandleRepeaters.php index 5fe225073..dfe785896 100644 --- a/src/Repositories/Behaviors/HandleRepeaters.php +++ b/src/Repositories/Behaviors/HandleRepeaters.php @@ -131,12 +131,6 @@ public function updateRepeaterMorphMany( $relationField[$morphFieldId] = $object->id; $relationField[$morphFieldType] = $object->getMorphClass(); - if (is_array($relationField['content'] ?? null)) { - $content = $relationField['content']; - unset($relationField['content']); - $relationField = array_merge($content, $relationField); - } - if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) { // row already exists, let's update $id = str_replace($relation . '-', '', $relationField['id']); @@ -216,12 +210,6 @@ public function updateRepeaterWithPivot( } } - if (is_array($relationField['content'] ?? null)) { - $content = $relationField['content']; - unset($relationField['content']); - $relationField = array_merge($content, $relationField); - } - if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) { // row already exists, let's update, the $id is the id in the pivot table. $pivotRowId = str_replace($relation . '-', '', $relationField['id']); @@ -328,12 +316,6 @@ public function updateRepeater( $relationField['id'] = $relation . '-' . $id; } - if (is_array($relationField['content'] ?? null)) { - $content = $relationField['content']; - unset($relationField['content']); - $relationField = array_merge($content, $relationField); - } - // Set the active data based on the parent. if (! isset($relationField['languages']) && isset($relationField['active'])) { foreach (array_keys($relationField['active']) as $langCode) { diff --git a/tests/integration/Blocks/BlockChildrenTest.php b/tests/integration/Blocks/BlockChildrenTest.php index cf9d4b440..52eb46846 100644 --- a/tests/integration/Blocks/BlockChildrenTest.php +++ b/tests/integration/Blocks/BlockChildrenTest.php @@ -8,7 +8,7 @@ class BlockChildrenTest extends TestCase { - public function testBCOfBlockSchema(): void + public function testSorting(): void { $module = AnonymousModule::make('childrenservers', $this->app) ->boot(); @@ -24,6 +24,7 @@ public function testBCOfBlockSchema(): void $blocks = [ 'blocks' => [ [ + 'editor_name' => 'description', 'browsers' => [], 'medias' => [], 'blocks' => [ @@ -52,78 +53,6 @@ public function testBCOfBlockSchema(): void ], 'id' => time() + 2, ], - ] - ], - 'type' => 'a17-block-quote', - 'content' => [ - 'quote' => 'This is the quote.', - 'author' => 'This is the author.', - ], - 'id' => time(), - ], - ], - ]; - - $update = $repository->update($server->id, $blocks); - - // Check the nested child order. - $this->assertEquals( - 'This is the nested quote at position 2.', - $update->blocks[0]->children[0]->content['quote'] - ); - $this->assertEquals( - 'This is the nested quote at position 1.', - $update->blocks[0]->children[1]->content['quote'] - ); - } - - public function testSorting(): void - { - $module = AnonymousModule::make('childrenservers', $this->app) - ->boot(); - - /** @var ModuleRepository $repository */ - $repository = app()->make($module->getRepositoryClassName()); - - $server = $repository->create([ - 'title' => 'Hello world', - 'published' => true, - ]); - - $blocks = [ - 'blocks' => [ - [ - 'editor_name' => 'description', - 'browsers' => [], - 'medias' => [], - 'blocks' => [ - [ - 'browsers' => [], - 'medias' => [], - 'blocks' => [], - 'child_key' => 'content', - 'editor_name' => 'blocks-yada-1|content', - 'type' => 'a17-block-quote', - 'is_repeater' => false, - 'content' => [ - 'quote' => 'This is the nested quote at position 2.', - 'author' => 'This is the nested author at position 2.', - ], - 'id' => time() + 1, - ], - [ - 'browsers' => [], - 'medias' => [], - 'blocks' => [], - 'child_key' => 'content', - 'editor_name' => 'blocks-yada-1|content', - 'type' => 'a17-block-quote', - 'is_repeater' => false, - 'content' => [ - 'quote' => 'This is the nested quote at position 1.', - 'author' => 'This is the nested author at position 1.', - ], - 'id' => time() + 2, ], ], 'type' => 'a17-block-quote', @@ -150,14 +79,14 @@ public function testSorting(): void // Now we update it a second time, but we update the order. $blocksUpdate = $blocks; - $blocksUpdate['blocks'][0]['blocks'][0]['id'] = $update->blocks[1]->id; - $blocksUpdate['blocks'][0]['blocks'][1]['id'] = $update->blocks[2]->id; + $blocksUpdate['blocks'][0]['blocks'][0][0]['id'] = $update->blocks[1]->id; + $blocksUpdate['blocks'][0]['blocks'][0][1]['id'] = $update->blocks[2]->id; // We now swap them so their actual position is correct. - $backup = $blocksUpdate['blocks'][0]['blocks'][0]; + $backup = $blocksUpdate['blocks'][0]['blocks'][0][0]; - $blocksUpdate['blocks'][0]['blocks'][0] = $blocksUpdate['blocks'][0]['blocks'][1]; - $blocksUpdate['blocks'][0]['blocks'][1] = $backup; + $blocksUpdate['blocks'][0]['blocks'][0][0] = $blocksUpdate['blocks'][0]['blocks'][0][1]; + $blocksUpdate['blocks'][0]['blocks'][0][1] = $backup; $update = $repository->update($server->id, $blocksUpdate); From ba544dd6726fad0d36a1e33aa75e49328a28ed90 Mon Sep 17 00:00:00 2001 From: Tofandel Date: Mon, 1 Apr 2024 13:27:02 +0200 Subject: [PATCH 6/6] Fix editor name for blocks inside repeaters --- frontend/js/utils/getFormData.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/js/utils/getFormData.js b/frontend/js/utils/getFormData.js index f08636813..cbbebb97c 100755 --- a/frontend/js/utils/getFormData.js +++ b/frontend/js/utils/getFormData.js @@ -27,8 +27,7 @@ export const isBlockField = (name, id) => { } export const stripOutBlockNamespace = (name, id) => { - const nameWithoutBlock = name.replace('blocks[' + id + '][', '') - return nameWithoutBlock.match(/]/gi).length > 1 ? nameWithoutBlock.replace(']', '') : nameWithoutBlock.slice(0, -1) + return name.replace('blocks[' + id + '][', '').replace(']', '') } export const buildBlock = (block, rootState, isRepeater = false, isInsideRepeater = isRepeater) => { @@ -81,7 +80,7 @@ export const buildBlock = (block, rootState, isRepeater = false, isInsideRepeate } return isInsideRepeater ? { ...content, ...base, repeater_target_id: block.repeater_target_id, blocks, repeaters} - : { ...base, content, is_repeater: isRepeater, type: block.type, editor_name: block.name, blocks: {...blocks, ...repeaters} } + : { ...base, content, is_repeater: isRepeater, type: block.type, editor_name: block.name?.split('|').pop(), blocks: {...blocks, ...repeaters} } } export const isBlockEmpty = (blockData) => {