Passed
Push — master ( bad1ac...1cdfe7 )
by Robbie
15:25 queued 05:45
created

HTMLEditorField   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 180
rs 10
c 0
b 0
f 0
wmc 16

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setValue() 0 5 1
A __construct() 0 9 2
A saveInto() 0 20 4
A setEditorConfig() 0 4 1
A performReadonlyTransformation() 0 3 1
A getAttributes() 0 15 2
A Field() 0 5 1
A performDisabledTransformation() 0 3 1
A getEditorConfig() 0 9 2
A getSchemaStateDefaults() 0 6 1
1
<?php
2
3
namespace SilverStripe\Forms\HTMLEditor;
4
5
use SilverStripe\Assets\Shortcodes\ImageShortcodeProvider;
6
use SilverStripe\Forms\FormField;
7
use SilverStripe\Forms\TextareaField;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\DataObjectInterface;
10
use Exception;
11
use SilverStripe\View\Parsers\HTMLValue;
12
13
/**
14
 * A TinyMCE-powered WYSIWYG HTML editor field with image and link insertion and tracking capabilities. Editor fields
15
 * are created from `<textarea>` tags, which are then converted with JavaScript.
16
 *
17
 * Caution: The form field does not include any JavaScript or CSS when used outside of the CMS context,
18
 * since the required frontend dependencies are included through CMS bundling.
19
 */
20
class HTMLEditorField extends TextareaField
21
{
22
23
    private static $casting = [
24
        'Value' => 'HTMLText',
25
    ];
26
27
    protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_HTML;
28
29
    protected $schemaComponent = 'HtmlEditorField';
30
31
    /**
32
     * Use TinyMCE's GZIP compressor
33
     *
34
     * @config
35
     * @var bool
36
     */
37
    private static $use_gzip = true;
38
39
    /**
40
     * @config
41
     * @var string Default alignment for Images and Media. Options: leftAlone|center|left|right
42
     */
43
    private static $media_alignment = 'leftAlone';
44
45
    /**
46
     * Should we check the valid_elements (& extended_valid_elements) rules from HTMLEditorConfig server side?
47
     *
48
     * @config
49
     * @var bool
50
     */
51
    private static $sanitise_server_side = false;
52
53
    /**
54
     * Number of rows
55
     *
56
     * @config
57
     * @var int
58
     */
59
    private static $default_rows = 20;
60
61
    /**
62
     * Extra height per row
63
     *
64
     * @var int
65
     */
66
    private static $fixed_row_height = 20;
67
68
    /**
69
     * ID or instance of editorconfig
70
     *
71
     * @var string|HTMLEditorConfig
72
     */
73
    protected $editorConfig = null;
74
75
    /**
76
     * Gets the HTMLEditorConfig instance
77
     *
78
     * @return HTMLEditorConfig
79
     */
80
    public function getEditorConfig()
81
    {
82
        // Instance override
83
        if ($this->editorConfig instanceof HTMLEditorConfig) {
84
            return $this->editorConfig;
85
        }
86
87
        // Get named / active config
88
        return HTMLEditorConfig::get($this->editorConfig);
89
    }
90
91
    /**
92
     * Assign a new configuration instance or identifier
93
     *
94
     * @param string|HTMLEditorConfig $config
95
     * @return $this
96
     */
97
    public function setEditorConfig($config)
98
    {
99
        $this->editorConfig = $config;
100
        return $this;
101
    }
102
103
    /**
104
     * Creates a new HTMLEditorField.
105
     * @see TextareaField::__construct()
106
     *
107
     * @param string $name The internal field name, passed to forms.
108
     * @param string $title The human-readable field label.
109
     * @param mixed $value The value of the field.
110
     * @param string $config HTMLEditorConfig identifier to be used. Default to the active one.
111
     */
112
    public function __construct($name, $title = null, $value = '', $config = null)
113
    {
114
        parent::__construct($name, $title, $value);
115
116
        if ($config) {
117
            $this->setEditorConfig($config);
118
        }
119
120
        $this->setRows(HTMLEditorField::config()->default_rows);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
Bug Best Practice introduced by
The property default_rows does not exist on SilverStripe\Core\Config\Config_ForClass. Since you implemented __get, consider adding a @property annotation.
Loading history...
121
    }
122
123
    public function getAttributes()
124
    {
125
        // Fix CSS height based on rows
126
        $rowHeight = $this->config()->get('fixed_row_height');
127
        $attributes = [];
128
        if ($rowHeight) {
129
            $height = $this->getRows() * $rowHeight;
130
            $attributes['style'] = sprintf('height: %dpx;', $height);
131
        }
132
133
        // Merge attributes
134
        return array_merge(
135
            $attributes,
136
            parent::getAttributes(),
137
            $this->getEditorConfig()->getAttributes()
138
        );
139
    }
140
141
    /**
142
     * @param DataObject|DataObjectInterface $record
143
     * @throws Exception
144
     */
145
    public function saveInto(DataObjectInterface $record)
146
    {
147
        if ($record->hasField($this->name) && $record->escapeTypeForField($this->name) != 'xml') {
0 ignored issues
show
Bug introduced by
The method escapeTypeForField() does not exist on SilverStripe\ORM\DataObjectInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to SilverStripe\ORM\DataObjectInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

147
        if ($record->hasField($this->name) && $record->/** @scrutinizer ignore-call */ escapeTypeForField($this->name) != 'xml') {
Loading history...
Bug introduced by
The method hasField() does not exist on SilverStripe\ORM\DataObjectInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to SilverStripe\ORM\DataObjectInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

147
        if ($record->/** @scrutinizer ignore-call */ hasField($this->name) && $record->escapeTypeForField($this->name) != 'xml') {
Loading history...
148
            throw new Exception(
149
                'HTMLEditorField->saveInto(): This field should save into a HTMLText or HTMLVarchar field.'
150
            );
151
        }
152
153
        // Sanitise if requested
154
        $htmlValue = HTMLValue::create($this->Value());
155
        if (HTMLEditorField::config()->sanitise_server_side) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
Bug Best Practice introduced by
The property sanitise_server_side does not exist on SilverStripe\Core\Config\Config_ForClass. Since you implemented __get, consider adding a @property annotation.
Loading history...
156
            $santiser = HTMLEditorSanitiser::create(HTMLEditorConfig::get_active());
157
            $santiser->sanitise($htmlValue);
158
        }
159
160
        // optionally manipulate the HTML after a TinyMCE edit and prior to a save
161
        $this->extend('processHTML', $htmlValue);
162
163
        // Store into record
164
        $record->{$this->name} = $htmlValue->getContent();
165
    }
166
167
    public function setValue($value, $data = null)
168
    {
169
        // Regenerate links prior to preview, so that the editor can see them.
170
        $value = ImageShortcodeProvider::regenerate_html_links($value);
171
        return parent::setValue($value);
172
    }
173
174
    /**
175
     * @return HTMLEditorField_Readonly
176
     */
177
    public function performReadonlyTransformation()
178
    {
179
        return $this->castedCopy(HTMLEditorField_Readonly::class);
180
    }
181
182
    public function performDisabledTransformation()
183
    {
184
        return $this->performReadonlyTransformation();
185
    }
186
187
    public function Field($properties = array())
188
    {
189
        // Include requirements
190
        $this->getEditorConfig()->init();
191
        return parent::Field($properties);
192
    }
193
194
    public function getSchemaStateDefaults()
195
    {
196
        $stateDefaults = parent::getSchemaStateDefaults();
197
        $config = $this->getEditorConfig();
198
        $stateDefaults['data'] = $config->getConfigSchemaData();
199
        return $stateDefaults;
200
    }
201
}
202