diff --git a/src/Modifiers/CoreModifiers.php b/src/Modifiers/CoreModifiers.php index 9329b2978a..6167ae3b29 100644 --- a/src/Modifiers/CoreModifiers.php +++ b/src/Modifiers/CoreModifiers.php @@ -23,6 +23,8 @@ use Statamic\Facades\YAML; use Statamic\Fields\Value; use Statamic\Fields\Values; +use Statamic\Fieldtypes\Bard; +use Statamic\Fieldtypes\Bard\Augmentor; use Statamic\Support\Arr; use Statamic\Support\Html; use Statamic\Support\Str; @@ -175,6 +177,80 @@ public function backspace($value, $params) return substr($value, 0, -$params[0]); } + /** + * Converts a bard value to a flat array of nodes and marks. + * + * @param $value + * @return array + */ + public function bardItems($value) + { + if ($value instanceof Value) { + $value = $value->raw(); + } + if (Arr::isAssoc($value)) { + $value = [$value]; + } + + $items = []; + while (count($value)) { + $items[] = $item = array_shift($value); + // Marks are children of the text they apply to, but having access to that node + // would be useful when working with marks, so we add the node to the mark data + array_unshift($value, ...array_map(fn ($m) => $m + ['node' => $item], $item['marks'] ?? [])); + array_unshift($value, ...($item['content'] ?? [])); + } + + return $items; + } + + /** + * Converts a bard value to plain text (excluding sets). + * + * @param $value + * @return string + */ + public function bardText($value) + { + if ($value instanceof Value) { + $value = $value->raw(); + } + if (Arr::isAssoc($value)) { + $value = [$value]; + } + + $text = ''; + while (count($value)) { + $item = array_shift($value); + if ($item['type'] === 'text') { + $text .= ' '.($item['text'] ?? ''); + } + array_unshift($value, ...($item['content'] ?? [])); + } + + return Stringy::collapseWhitespace($text); + } + + /** + * Converts a bard value to HTML (excluding sets). + * + * @param $value + * @return string + */ + public function bardHtml($value) + { + if ($value instanceof Value) { + $value = $value->raw(); + } + if (Arr::isAssoc($value)) { + $value = [$value]; + } + + $items = array_values(Arr::where($value, fn ($item) => $item['type'] !== 'set')); + + return (new Augmentor(new Bard()))->augment($items); + } + public function boolString($value) { if ($value == true) { diff --git a/tests/Modifiers/BardHtmlTest.php b/tests/Modifiers/BardHtmlTest.php new file mode 100644 index 0000000000..0dc8cd5ac0 --- /dev/null +++ b/tests/Modifiers/BardHtmlTest.php @@ -0,0 +1,88 @@ + 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph with '], + ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'text', 'text' => ' and '], + ['type' => 'text', 'marks' => [['type' => 'italic']], 'text' => 'italic'], + ['type' => 'text', 'text' => ' text.'], + ], + ], + [ + 'type' => 'paragraph', + ], + [ + 'type' => 'set', + 'attrs' => [ + 'values' => [ + 'type' => 'image', + 'image' => 'test.jpg', + 'caption' => 'test', + ], + ], + ], + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'Another paragraph.'], + ], + ], + ]; + + $expected = '

This is a paragraph with bold and italic text.

Another paragraph.

'; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_html_from_single_node() + { + $data = [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ]; + + $expected = '

This is a paragraph.

'; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_html_from_value_object() + { + $data = new Value([ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ], + ], 'content', new Bard()); + + $expected = '

This is a paragraph.

'; + + $this->assertEquals($expected, $this->modify($data)); + } + + public function modify($arr, ...$args) + { + return Modify::value($arr)->bard_html($args)->fetch(); + } +} diff --git a/tests/Modifiers/BardItemsTest.php b/tests/Modifiers/BardItemsTest.php new file mode 100644 index 0000000000..c88484dc14 --- /dev/null +++ b/tests/Modifiers/BardItemsTest.php @@ -0,0 +1,134 @@ + 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph with '], + ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'text', 'text' => ' text.'], + ], + ], + [ + 'type' => 'set', + 'attrs' => [ + 'values' => [ + 'type' => 'image', + 'image' => 'test.jpg', + 'caption' => 'test', + ], + ], + ], + ]; + + $expected = [ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph with '], + ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'text', 'text' => ' text.'], + ], + ], + ['type' => 'text', 'text' => 'This is a paragraph with '], + ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'bold', 'node' => ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold']], + ['type' => 'text', 'text' => ' text.'], + [ + 'type' => 'set', + 'attrs' => [ + 'values' => [ + 'type' => 'image', + 'image' => 'test.jpg', + 'caption' => 'test', + ], + ], + ], + ]; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_items_from_single_node() + { + $data = [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ]; + + $expected = [ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ], + ['type' => 'text', 'text' => 'This is a paragraph.'], + ]; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_items_from_value_object() + { + $data = new Value([ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ], + ], 'content', new Bard()); + + $expected = [ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ], + ['type' => 'text', 'text' => 'This is a paragraph.'], + ]; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_items_with_nodes_appended_to_marks() + { + $data = [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph with '], + $node = ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'text', 'text' => ' text.'], + ], + ]; + + $items = $this->modify($data); + $mark = Arr::first(Arr::where($items, fn ($item) => $item['type'] === 'bold')); + $this->assertEquals($node, $mark['node']); + } + + public function modify($arr, ...$args) + { + return Modify::value($arr)->bard_items($args)->fetch(); + } +} diff --git a/tests/Modifiers/BardTextTest.php b/tests/Modifiers/BardTextTest.php new file mode 100644 index 0000000000..496d3ea9a8 --- /dev/null +++ b/tests/Modifiers/BardTextTest.php @@ -0,0 +1,88 @@ + 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph with '], + ['type' => 'text', 'marks' => [['type' => 'bold']], 'text' => 'bold'], + ['type' => 'text', 'text' => ' and '], + ['type' => 'text', 'marks' => [['type' => 'italic']], 'text' => 'italic'], + ['type' => 'text', 'text' => ' text.'], + ], + ], + [ + 'type' => 'paragraph', + ], + [ + 'type' => 'set', + 'attrs' => [ + 'values' => [ + 'type' => 'image', + 'image' => 'test.jpg', + 'caption' => 'test', + ], + ], + ], + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'Another paragraph.'], + ], + ], + ]; + + $expected = 'This is a paragraph with bold and italic text. Another paragraph.'; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_text_from_single_node() + { + $data = [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ]; + + $expected = 'This is a paragraph.'; + + $this->assertEquals($expected, $this->modify($data)); + } + + /** @test */ + public function it_extracts_bard_text_from_value_object() + { + $data = new Value([ + [ + 'type' => 'paragraph', + 'content' => [ + ['type' => 'text', 'text' => 'This is a paragraph.'], + ], + ], + ], 'content', new Bard()); + + $expected = 'This is a paragraph.'; + + $this->assertEquals($expected, $this->modify($data)); + } + + public function modify($arr, ...$args) + { + return Modify::value($arr)->bard_text($args)->fetch(); + } +}