'object', 'properties' => array_merge( DiviSchema::write(), ['id' => ['type' => 'string', 'description' => 'Pfad-ID des zu verschiebenden Knotens.']], DiviSchema::position() ), 'required' => ['post_id', 'tree_rev', 'id'], 'additionalProperties' => false, ]; } public function annotations(): array { return Annotations::write(false); } /** * No client value enters the tree (no DynamicSources): load and save (PageRead, PageWrite), StructureRules * need the catalog, the quality warnings of the result read the design system (DesignRead). */ protected function baseFeatures(): array { return [DiviFeature::PageRead, DiviFeature::PageWrite, DiviFeature::Catalog, DiviFeature::DesignRead]; } protected function change(DiviPage $page, array $input, DiviComposition $composition): DiviChange|WP_Error { $id = is_string($input['id'] ?? null) ? $input['id'] : ''; $node = $page->index->find($id); if ($node === null) { return DiviErrors::unknownNode($id, $page->post); } $editor = $composition->editor(); $position = $editor->position($page->index, $input); if (is_string($position)) { return DiviErrors::position($position); } // Both scopes from the tree before the move: the new parent never lies inside $node (TreeEditor::move()). $from = self::loopAt($page->index, $page->index->parentBlock($node)); $to = self::loopAt($page->index, $position['parent']); $problem = $editor->move($page->index, $node, $position); if ($problem !== null) { return Errors::invalidInput( 'Verschieben nicht möglich; nichts wurde gespeichert.', ['position' => $problem] ); } $change = (new DiviChange(sprintf('Knoten %s verschoben', $id)))->record($node, 'moved'); $names = self::loopContent($node); if ($names !== [] && $from->kind() !== $to->kind()) { $change->warning(self::loopWarning($names, $from, $to)); } return $change; } /** * The loop_* options $block and its descendants show in the scope around $block; a block that runs its own * loop keeps its content's scope, read-only blocks are not entered. * * @return list */ private static function loopContent(Block $block): array { if ($block->isOpaque() || LoopContext::enabled($block->attrs())) { return []; } $names = []; $attrs = $block->attrs(); array_walk_recursive($attrs, static function (mixed $value) use (&$names): void { $name = is_string($value) ? VariableRef::nameOf($value) : null; if ($name !== null && str_starts_with($name, 'loop_')) { $names[] = $name; } }); foreach ($block->children() as $child) { array_push($names, ...self::loopContent($child)); } return array_values(array_unique($names)); } /** @param list $names */ private static function loopWarning(array $names, LoopScope $from, LoopScope $to): string { $where = $to->active() ? sprintf( 'jetzt in einer Divi-Schleife vom Typ: %s (vorher: %s); passen sie nicht zu dieser Art, zeigt Divi ' . 'sie leer oder mit Daten eines anderen Objekts mit derselben ID', $to->label(), $from->active() ? $from->label() : 'keine Schleife' ) : 'jetzt außerhalb jeder Divi-Schleife; Divi zeigt sie dort leer'; return sprintf( 'Schleifen-Inhalte (%s) liegen %s. Zurückverschieben oder die Inhalte anpassen.', implode(', ', $names), $where ); } }