Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.73% covered (success)
97.73%
43 / 44
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
RichText
97.73% covered (success)
97.73%
43 / 44
80.00% covered (warning)
80.00%
4 / 5
13
0.00% covered (danger)
0.00%
0 / 1
 control
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 value
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 structure
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 properties
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 shape
96.67% covered (success)
96.67%
29 / 30
0.00% covered (danger)
0.00%
0 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Field;
6
7use Celema\Sire\Shape;
8use Cosray\Richtext\Envelope;
9use Cosray\Validation\RichtextDoc;
10use Cosray\Validation\Shapes;
11use Cosray\Value\RichText as RichTextValue;
12use Override;
13
14class RichText extends Text implements Capability\ToolsAware
15{
16    use Capability\IsToolsAware;
17
18    #[Override]
19    public function control(): Control
20    {
21        return Control::richtext();
22    }
23
24    #[Override]
25    public function value(): RichTextValue
26    {
27        return new RichTextValue($this->owner, $this, $this->valueContext);
28    }
29
30    #[Override]
31    public function structure(mixed $value = null): array
32    {
33        if (is_array($value) && array_key_exists('value', $value)) {
34            $value = $value['value'];
35        }
36
37        $result = $this->getTranslatableStructure('richtext', $value);
38        $result['format'] = Envelope::FORMAT;
39        $result['version'] = Envelope::VERSION;
40
41        return $result;
42    }
43
44    #[Override]
45    public function properties(): array
46    {
47        $richtext = $this->owner->config()->richtext;
48
49        return array_merge(parent::properties(), [
50            'richtextClasses' => (object) $richtext->classes,
51            'richtextStyles' => (object) $richtext->styles,
52            'tools' => $this->getTools(),
53        ]);
54    }
55
56    /**
57     * Saves are writer-strict: the panel submits the structured
58     * envelope (format, version, per-locale documents); legacy HTML
59     * strings are rejected. See docs/richtext-format.md.
60     */
61    #[Override]
62    public function shape(): Shape
63    {
64        $shape = Shapes::create();
65        $this->addType($shape);
66        $shape->add('format', 'string')->rules('required', 'in:' . Envelope::FORMAT);
67        // `in` compares strictly against string args, so pin the int
68        // version with a min/max pair instead.
69        $shape->add('version', 'int')->rules(
70            'required',
71            'min:' . Envelope::VERSION,
72            'max:' . Envelope::VERSION,
73        );
74
75        $richtext = $this->owner->config()->richtext;
76        $doc = new RichtextDoc($richtext->classes, $richtext->styles);
77
78        if ($this->isTranslatable()) {
79            $locales = $this->owner->locales();
80            $defaultLocale = $locales->getDefault()->id;
81            $i18nShape = Shapes::create();
82
83            foreach ($locales as $locale) {
84                $localeValidators = [];
85
86                if ($this->isRequired() && $locale->id === $defaultLocale) {
87                    $localeValidators[] = 'required';
88                }
89
90                $localeField = $i18nShape
91                    ->add($locale->id, $doc)
92                    ->label($this->valueLabel($locale))
93                    ->rules(...$localeValidators);
94
95                if (!in_array('required', $localeValidators, true)) {
96                    $localeField->optional()->nullable();
97                }
98            }
99
100            $value = $shape->add('value', $i18nShape)->rules(...$this->validators);
101        } else {
102            $value = $shape->add('value', $this->zxxShape($doc, $this->validators));
103        }
104
105        if (!$this->isRequired()) {
106            $value->optional()->nullable();
107        }
108
109        $this->addMeta($shape);
110
111        return $shape;
112    }
113}