Passed
Push — develop-v3 ( eac407...d3dd0d )
by Andrew
17:00
created

Code::getFieldRenderVariables()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 38
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 27
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 38
rs 8.8657
1
<?php
2
/**
3
 * Code Field plugin for Craft CMS
4
 *
5
 * Provides a Code Field that has a full-featured code editor with syntax highlighting & autocomplete
6
 *
7
 * @link      https://nystudio107.com
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @copyright tag
Loading history...
8
 * @copyright Copyright (c) 2022 nystudio107
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
9
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
Coding Style introduced by
Missing @package tag in file comment
Loading history...
Coding Style introduced by
Missing @author tag in file comment
Loading history...
Coding Style introduced by
Missing @license tag in file comment
Loading history...
10
11
namespace nystudio107\codefield\fields;
12
13
use Craft;
14
use craft\base\ElementInterface;
15
use craft\base\Field;
16
use craft\base\PreviewableFieldInterface;
17
use craft\helpers\Json;
18
use craft\validators\ArrayValidator;
19
use nystudio107\codefield\gql\types\generators\CodeDataGenerator;
20
use nystudio107\codefield\models\CodeData;
21
use nystudio107\codefield\validators\JsonValidator;
22
use yii\db\Schema;
23
24
/**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
25
 * @author    nystudio107
0 ignored issues
show
Coding Style introduced by
The tag in position 1 should be the @package tag
Loading history...
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
Coding Style introduced by
Tag value for @author tag indented incorrectly; expected 2 spaces but found 4
Loading history...
26
 * @package   CodeField
0 ignored issues
show
Coding Style introduced by
Tag value for @package tag indented incorrectly; expected 1 spaces but found 3
Loading history...
27
 * @since     3.0.0
0 ignored issues
show
Coding Style introduced by
The tag in position 3 should be the @author tag
Loading history...
Coding Style introduced by
Tag value for @since tag indented incorrectly; expected 3 spaces but found 5
Loading history...
28
 */
0 ignored issues
show
Coding Style introduced by
Missing @category tag in class comment
Loading history...
Coding Style introduced by
Missing @license tag in class comment
Loading history...
Coding Style introduced by
Missing @link tag in class comment
Loading history...
29
class Code extends Field implements PreviewableFieldInterface
30
{
31
    // Public Properties
32
    // =========================================================================
33
34
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
35
     * @var string The theme to use for the Code Editor field.
36
     */
37
    public $theme = 'auto';
38
39
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
40
     * @var string The language to use for the Code Editor field.
41
     */
42
    public $language = 'javascript';
43
44
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
45
     * @var bool Whether the Code Editor field display as a single line
46
     */
47
    public $singleLineEditor = false;
48
49
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
50
     * @var int The font size to use for the Code Editor field
51
     */
52
    public $fontSize = 14;
53
54
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
55
     * @var bool Whether line numbers should be displayed in the Code Editor field
56
     */
57
    public $lineNumbers = false;
58
59
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
60
     * @var bool Whether code folding should be used in the Code Editor field
61
     */
62
    public $codeFolding = false;
63
64
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
65
     * @var string The text that will be shown if the code field is empty.
66
     */
67
    public $placeholder = '';
68
69
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
70
     * @var string The default value the Code Field will be populated with
71
     */
72
    public $defaultValue = '';
73
74
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
75
     * @var bool Whether the language selector dropdown menu should be displayed.
76
     */
77
    public $showLanguageDropdown = true;
78
79
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
80
     * @var array The languages that should be listed in the language selector dropdown menu.
81
     */
82
    public $availableLanguages = [
83
        'css',
84
        'graphql',
85
        'html',
86
        'javascript',
87
        'json',
88
        'markdown',
89
        'mysql',
90
        'php',
91
        'shell',
92
        'twig',
93
        'typescript',
94
        'yaml',
95
    ];
96
97
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
98
     * @var string|null The type of database column the field should have in the content table
99
     */
100
    public $columnType = Schema::TYPE_TEXT;
101
102
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
103
     * @var string JSON blob of Monaco [EditorOptions](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IEditorOptions.html) that will override the default settings
104
     */
105
    public $monacoEditorOptions = '';
106
107
    // Static Methods
108
    // =========================================================================
109
110
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
111
     * @inheritdoc
112
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
113
    public static function displayName(): string
114
    {
115
        return Craft::t('codefield', 'Code');
116
    }
117
118
    // Public Methods
119
    // =========================================================================
120
121
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
122
     * @inheritdoc
123
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
124
    public function normalizeValue($value, ElementInterface $element = null)
125
    {
126
        if ($value instanceof CodeData) {
127
            return $value;
128
        }
129
        // Default config
130
        $config = [
131
            'value' => $this->defaultValue,
132
            'language' => $this->language,
133
        ];
134
        // Handle incoming values potentially being JSON or an array
135
        if (!empty($value)) {
136
            // Handle JSON-encoded values coming in
137
            if (\is_string($value)) {
138
                $jsonValue = Json::decodeIfJson($value);
139
                // If this is still a string (meaning it's not valid JSON), treat it as the value
140
                if (\is_string($jsonValue)) {
141
                    $config['value'] = $jsonValue;
142
                }
143
                if (\is_array($jsonValue)) {
144
                    // Check to make sure the array returned is an encoded `CodeData` config, with exactly
145
                    // the same expected key/value pairs
146
                    if (!array_diff_key($config, $jsonValue) && !array_diff_key($jsonValue, $config)) {
147
                        $value = $jsonValue;
148
                    } else {
149
                        // Otherwise treat it as JSON data
150
                        $value = [
151
                            'value' => $value,
152
                            'language' => 'json',
153
                        ];
154
                    }
155
                }
156
            }
157
            if (\is_array($value)) {
158
                $config = array_merge($config, array_filter($value));
159
            }
160
        }
161
        // Create and validate the model
162
        $codeData = new CodeData($config);
163
        if (!$codeData->validate()) {
164
            Craft::error(
165
                Craft::t('codefield', 'CodeData failed validation: ')
166
                . print_r($codeData->getErrors(), true),
0 ignored issues
show
Bug introduced by
Are you sure print_r($codeData->getErrors(), true) of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

166
                . /** @scrutinizer ignore-type */ print_r($codeData->getErrors(), true),
Loading history...
167
                __METHOD__
168
            );
169
        }
170
171
        return $codeData;
172
    }
173
174
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
175
     * @inheritdoc
176
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
177
    public function getSettingsHtml()
178
    {
179
        $monacoLanguages = require(__DIR__ . '/MonacoLanguages.php');
0 ignored issues
show
Coding Style introduced by
"require" is a statement not a function; no parentheses are required
Loading history...
Coding Style introduced by
File is being conditionally included; use "include" instead
Loading history...
180
        $schemaFilePath = Craft::getAlias('@nystudio107/codefield/resources/IEditorOptionsSchema.json');
181
        $optionsSchema = @file_get_contents($schemaFilePath) ?: '';
0 ignored issues
show
Bug introduced by
It seems like $schemaFilePath can also be of type false; however, parameter $filename of file_get_contents() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

181
        $optionsSchema = @file_get_contents(/** @scrutinizer ignore-type */ $schemaFilePath) ?: '';
Loading history...
182
        // Render the settings template
183
        return Craft::$app->getView()->renderTemplate(
184
            'codefield/_components/fields/Code_settings',
185
            [
186
                'field' => $this,
187
                'monacoLanguages' => $monacoLanguages,
188
                'optionsSchema' => $optionsSchema,
189
            ]
190
        );
191
    }
192
193
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
194
     * @inheritdoc
195
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
196
    public function getInputHtml($value, ElementInterface $element = null): string
197
    {
198
        $twigVariables = $this->getFieldRenderVariables($value, $element, true);
0 ignored issues
show
Bug introduced by
It seems like $element can also be of type null; however, parameter $element of nystudio107\codefield\fi...tFieldRenderVariables() does only seem to accept craft\base\ElementInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

198
        $twigVariables = $this->getFieldRenderVariables($value, /** @scrutinizer ignore-type */ $element, true);
Loading history...
199
        // Render the input template
200
        return Craft::$app->getView()->renderTemplate(
201
            'codefield/_components/fields/Code_input',
202
            $twigVariables
203
        );
204
    }
205
206
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $element should have a doc-comment as per coding-style.
Loading history...
207
     * @inheritdoc
208
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
209
    public function getStaticHtml($value, ElementInterface $element): string
210
    {
211
        $twigVariables = $this->getFieldRenderVariables($value, $element, false);
212
        // Render the input template
213
        return Craft::$app->getView()->renderTemplate(
214
            'codefield/_components/fields/Code_input',
215
            $twigVariables
216
        );
217
    }
218
219
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
220
     * @inheritdoc
221
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
222
    public function getContentGqlType()
223
    {
224
        $typeArray = CodeDataGenerator::generateTypes($this);
225
226
        return [
227
            'name' => $this->handle,
228
            'description' => 'Code Editor field',
229
            'type' => array_shift($typeArray),
230
        ];
231
    }
232
233
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
234
     * @inheritdoc
235
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
236
    public function rules()
237
    {
238
        $rules = parent::rules();
239
        $rules = array_merge($rules, [
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
240
            ['theme', 'in', 'range' => ['auto', 'vs', 'vs-dark', 'hc-black']],
241
            ['theme', 'default', 'value' => 'auto'],
242
            ['language', 'string'],
243
            ['language', 'default', 'value' => 'javascript'],
244
            [['singleLineEditor', 'showLanguageDropdown', 'lineNumbers', 'codeFolding'], 'boolean'],
245
            ['placeholder', 'string'],
246
            ['placeholder', 'default', 'value' => ''],
247
            ['fontSize', 'integer'],
248
            ['fontSize', 'default', 'value' => 14],
249
            ['availableLanguages', ArrayValidator::class],
250
            ['monacoEditorOptions', JsonValidator::class],
251
        ]);
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
252
        return $rules;
253
    }
254
255
    /**
0 ignored issues
show
Coding Style introduced by
Missing short description in doc comment
Loading history...
256
     * @inheritdoc
257
     */
0 ignored issues
show
Coding Style introduced by
Missing @return tag in function comment
Loading history...
258
    public function getContentColumnType(): string
259
    {
260
        if ($this->columnType) {
261
            return $this->columnType;
262
        }
263
264
        return Schema::TYPE_TEXT;
265
    }
266
267
    // Protected Methods
268
    // =========================================================================
269
270
    /**
271
     * Return the Twig variables for rendering the field
272
     *
273
     * @param $value
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
274
     * @param ElementInterface $element
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
275
     * @param bool $enabled Whether the field is enabled or not
0 ignored issues
show
Coding Style introduced by
Expected 13 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Tag value for @param tag indented incorrectly; expected 2 spaces but found 1
Loading history...
276
     * @return array[]
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
277
     */
278
    protected function getFieldRenderVariables($value, ElementInterface $element, bool $enabled): array
279
    {
280
        // Get our id and namespace
281
        $id = Craft::$app->getView()->formatInputId($this->handle);
282
        $namespacedId = Craft::$app->getView()->namespaceInputId($id);
283
284
        // Extract just the languages that have been selected for display
285
        $displayLanguages = [];
286
        if ($this->showLanguageDropdown) {
287
            $monacoLanguages = require(__DIR__ . '/MonacoLanguages.php');
0 ignored issues
show
Coding Style introduced by
"require" is a statement not a function; no parentheses are required
Loading history...
Coding Style introduced by
File is being conditionally included; use "include" instead
Loading history...
288
            $decomposedLanguages = array_column($monacoLanguages, 'label', 'value');
289
            $displayLanguages = array_intersect_key($decomposedLanguages, array_flip($this->availableLanguages));
290
            // Handle "all" checkbox
291
            if ($this->availableLanguages[0] === '*') {
292
                $displayLanguages = $decomposedLanguages;
293
            }
294
            $displayLanguages = array_map(static function ($k, $v) {
0 ignored issues
show
Coding Style introduced by
The opening parenthesis of a multi-line function call should be the last content on the line.
Loading history...
295
                return ['value' => $k, 'label' => $v];
296
            }, array_keys($displayLanguages), array_values($displayLanguages));
0 ignored issues
show
Coding Style introduced by
For multi-line function calls, the closing parenthesis should be on a new line.

If a function call spawns multiple lines, the coding standard suggests to move the closing parenthesis to a new line:

someFunctionCall(
    $firstArgument,
    $secondArgument,
    $thirdArgument
); // Closing parenthesis on a new line.
Loading history...
297
        }
298
        $monacoOptionsOverride = Json::decodeIfJson($this->monacoEditorOptions);
299
        if ($monacoOptionsOverride === null || is_string($monacoOptionsOverride)) {
300
            $monacoOptionsOverride = [];
301
        }
302
        // Disable the Monaco editor
303
        if (!$enabled) {
304
            $monacoOptionsOverride['domReadOnly'] = true;
305
            $monacoOptionsOverride['readOnly'] = true;
306
        }
307
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('name' => $...$monacoOptionsOverride) returns an array which contains values of type nystudio107\codefield\fields\Code|string which are incompatible with the documented value type array.
Loading history...
308
            'name' => $this->handle,
309
            'value' => $value,
310
            'field' => $this,
311
            'orientation' => $this->getOrientation($element),
312
            'id' => $id,
313
            'namespacedId' => $namespacedId,
314
            'displayLanguages' => $displayLanguages,
315
            'monacoOptionsOverride' => $monacoOptionsOverride,
316
        ];
317
    }
318
}
319