Completed
Push — master ( 1f4754...7f6945 )
by Dmitry
02:43
created

JsonEditor::run()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 0
crap 2
1
<?php
2
3
namespace kdn\yii2;
4
5
use kdn\yii2\assets\JsonEditorFullAsset;
6
use kdn\yii2\assets\JsonEditorMinimalistAsset;
7
use yii\helpers\ArrayHelper;
8
use yii\helpers\Html;
9
use yii\helpers\Inflector;
10
use yii\helpers\Json;
11
use yii\web\JsExpression;
12
use yii\widgets\InputWidget;
13
14
/**
15
 * Class JsonEditor.
16
 * @package kdn\yii2
17
 */
18
class JsonEditor extends InputWidget
19
{
20
    /**
21
     * @var array options which will be passed to JSON editor
22
     * @see https://github.com/josdejong/jsoneditor/blob/master/docs/api.md#configuration-options
23
     */
24
    public $clientOptions = [];
25
26
    /**
27
     * @var string[] list of JSON editor modes for which all fields should be collapsed automatically;
28
     * allowed modes 'tree', 'view', and 'form'
29
     * @see https://github.com/josdejong/jsoneditor/blob/master/docs/api.md#jsoneditorcollapseall
30
     */
31
    public $collapseAll = [];
32
33
    /**
34
     * @var array HTML attributes to be applied to the JSON editor container tag
35
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered
36
     */
37
    public $containerOptions = [];
38
39
    /**
40
     * @var string default value
41
     */
42
    public $defaultValue = '{}';
43
44
    /**
45
     * @var string[] list of JSON editor modes for which all fields should be expanded automatically;
46
     * allowed modes 'tree', 'view', and 'form'
47
     * @see https://github.com/josdejong/jsoneditor/blob/master/docs/api.md#jsoneditorexpandall
48
     */
49
    public $expandAll = [];
50
51
    /**
52
     * @var null|boolean whether to use minimalist version of JSON editor;
53
     * note that "minimalist" is not the same as "minimized";
54
     * if property is not set then extension will try to determine automatically whether full version is needed,
55
     * if full version is not required then minimalist version will be used;
56
     * you can explicitly set this property to true or false if automatic detection does not fit for you application
57
     * @see https://github.com/josdejong/jsoneditor/blob/master/dist/which%20files%20do%20I%20need.md
58
     */
59
    public $minimalist;
60
61
    /**
62
     * @var string default JSON editor mode
63
     */
64
    private $mode = 'tree';
65
66
    /**
67
     * @var string[] available JSON editor modes
68
     */
69
    private $modes = [];
70
71
    /**
72
     * @inheritdoc
73
     */
74 6
    public function init()
75
    {
76 6
        parent::init();
77 6
        if (!isset($this->containerOptions['id'])) {
78 6
            $this->containerOptions['id'] = $this->options['id'] . '-json-editor';
79 6
        }
80 6
        if (!array_key_exists('style', $this->containerOptions)) {
81 6
            $this->containerOptions['style'] = 'height: 250px;';
82 6
        }
83 6
        if ($this->hasModel()) {
84 2
            $this->value = Html::getAttributeValue($this->model, $this->attribute);
1 ignored issue
show
Documentation Bug introduced by
It seems like \yii\helpers\Html::getAt...odel, $this->attribute) can also be of type array. However, the property $value is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
85 2
        }
86 6
        if (empty($this->value)) {
87 4
            $this->value = $this->defaultValue;
88 4
        }
89 6
        foreach (['mode', 'modes'] as $parameterName) {
90 6
            $this->$parameterName = ArrayHelper::getValue($this->clientOptions, $parameterName, $this->$parameterName);
91 6
        }
92
        // make sure that "mode" is specified, otherwise JavaScript error can occur in some situations
93 6
        $this->clientOptions['mode'] = $this->mode;
94 6
        if (!isset($this->minimalist)) {
95 6
            $this->minimalist = $this->mode != 'code' && !in_array('code', $this->modes);
96 6
        }
97 6
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102 6
    public function run()
103
    {
104 6
        $this->registerClientScript();
105 6
        if ($this->hasModel()) {
106 2
            echo Html::activeHiddenInput($this->model, $this->attribute, $this->options);
107 2
        } else {
108 4
            echo Html::hiddenInput($this->name, $this->value, $this->options);
109
        }
110 6
        echo Html::tag('div', '', $this->containerOptions);
111 6
    }
112
113
    /**
114
     * Initializes client options.
115
     */
116 6
    protected function initClientOptions()
117
    {
118 6
        $options = $this->clientOptions;
119 6
        $jsExpressionOptions = ['ace', 'ajv', 'onChange', 'onEditable', 'onError', 'onModeChange', 'schema'];
120 6
        foreach ($options as $key => $value) {
121 6
            if (!$value instanceof JsExpression && in_array($key, $jsExpressionOptions)) {
122 1
                $options[$key] = new JsExpression($value);
123 1
            }
124 6
        }
125 6
        $this->clientOptions = $options;
126 6
    }
127
128
    /**
129
     * Registers the needed client script.
130
     */
131 6
    public function registerClientScript()
132
    {
133 6
        $this->initClientOptions();
134 6
        $view = $this->getView();
135
136 6
        if ($this->minimalist) {
137 5
            JsonEditorMinimalistAsset::register($view);
138 5
        } else {
139 3
            JsonEditorFullAsset::register($view);
140
        }
141
142 6
        $hiddenInputId = $this->options['id'];
143 6
        $editorName = Inflector::variablize($hiddenInputId) . 'JsonEditor_' . hash('crc32', $hiddenInputId);
144 6
        $this->options['data-json-editor-name'] = $editorName;
145
146 6
        $jsUpdateHiddenField = "jQuery('#$hiddenInputId').val($editorName.getText());";
147
148 6
        if (isset($this->clientOptions['onChange'])) {
149 1
            $userFunction = " var userFunction = {$this->clientOptions['onChange']}; userFunction.call(this);";
150 1
        } else {
151 5
            $userFunction = '';
152
        }
153 6
        $this->clientOptions['onChange'] = new JsExpression("function() {{$jsUpdateHiddenField}$userFunction}");
154
155 6
        if (!empty($this->collapseAll) || !empty($this->expandAll)) {
156 2
            if (isset($this->clientOptions['onModeChange'])) {
157 1
                $userFunction = " var userFunction = {$this->clientOptions['onModeChange']}; " .
158 1
                    "userFunction.call(this, newMode, oldMode);";
159 1
            } else {
160 1
                $userFunction = '';
161
            }
162 2
            $jsOnModeChange = "function(newMode, oldMode) {";
163 2
            foreach (['collapseAll', 'expandAll'] as $property) {
164 2
                if (!empty($this->$property)) {
165 2
                    $jsOnModeChange .= "if (" . Json::htmlEncode($this->$property) . ".indexOf(newMode) !== -1) " .
166 2
                        "{{$editorName}.$property();}";
167 2
                }
168 2
            }
169 2
            $jsOnModeChange .= "$userFunction}";
170 2
            $this->clientOptions['onModeChange'] = new JsExpression($jsOnModeChange);
171 2
        }
172
173 6
        $encodedValue = Json::htmlEncode(Json::decode($this->value, false));
174 6
        $jsCode = "$editorName = new JSONEditor(document.getElementById('{$this->containerOptions['id']}'), " .
175 6
            Json::htmlEncode($this->clientOptions) . ", $encodedValue);\n" .
176 6
            "jQuery('#$hiddenInputId').parents('form').submit(function() {{$jsUpdateHiddenField}});";
177 6
        if (in_array($this->mode, $this->collapseAll)) {
178 1
            $jsCode .= "\n$editorName.collapseAll();";
179 1
        }
180 6
        if (in_array($this->mode, $this->expandAll)) {
181 1
            $jsCode .= "\n$editorName.expandAll();";
182 1
        }
183 6
        $view->registerJs($jsCode);
184 6
    }
185
}
186