Issues (808)

src/midcom/grid/grid.php (2 issues)

1
<?php
2
/**
3
 * @package midcom.grid
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
namespace midcom\grid;
10
11
use midcom;
12
use midcom_error;
13
14
/**
15
 * Helper class for jqgrid widgets
16
 *
17
 * @package midcom.grid
18
 */
19
class grid
20
{
21
    /**
22
     * The grid's ID
23
     */
24
    private string $_identifier;
25
26
    /**
27
     * The grid's options (converted for use in JS constructor)
28
     */
29
    private array $_options = [];
30
31
    /**
32
     * The grid's options as passed in PHP
33
     */
34
    private array $_raw_options = [];
35
36
    /**
37
     * The grid's columns
38
     *
39
     * They have the following structure
40
     *
41
     * 'key' => [
42
     *     'label' => "Some label",
43
     *     'options' => 'javascript option string'
44
     * ]
45
     */
46
    private array $_columns = [];
47
48
    /**
49
     * Flag that tracks if JS/CSS files have already been added
50
     */
51
    private static bool $_head_elements_added = false;
52
53
    /**
54
     * Data for the table footer
55
     */
56
    private array $_footer_data = [];
57
58
    /**
59
     * Should formatters be applied to footer row
60
     */
61
    private bool $format_footer = true;
62
63
    /**
64
     * The data provider, if any
65
     */
66
    private ?provider $_provider = null;
67
68
    /**
69
     * Javascript code that should be prepended to the widget constructor
70
     */
71
    private string $_prepend_js = '';
72
73
    /**
74
     * Adds the necessary javascript & css files for jqgrid
75
     */
76 38
    public static function add_head_elements()
77
    {
78 38
        if (self::$_head_elements_added) {
79 37
            return;
80
        }
81 1
        $version = '4.15.4';
82 1
        $jqgrid_path = '/midcom.grid/jqGrid-' . $version . '/';
83
84 1
        $head = midcom::get()->head;
85 1
        $head->enable_jquery_ui(['button', 'mouse', 'resizable']);
86
87
        //needed js/css-files for jqgrid
88 1
        $lang = "en";
89 1
        $language = midcom::get()->i18n->get_current_language();
90 1
        if (file_exists(MIDCOM_STATIC_ROOT . $jqgrid_path . 'i18n/grid.locale-' . $language . '.js')) {
91 1
            $lang = $language;
92
        }
93 1
        $head->add_jsfile(MIDCOM_STATIC_URL . $jqgrid_path . 'i18n/grid.locale-'. $lang . '.js');
94 1
        $head->add_jsfile(MIDCOM_STATIC_URL . $jqgrid_path . 'jquery.jqgrid.min.js');
95
96 1
        $head->add_jsfile(MIDCOM_STATIC_URL . '/midcom.grid/jqGrid.custom.js');
97
98 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . $jqgrid_path . 'ui.jqgrid.min.css');
99 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . "/stock-icons/font-awesome-4.7.0/css/font-awesome.min.css");
100 1
        $head->add_stylesheet(MIDCOM_STATIC_URL . '/midcom.grid/jqGrid.custom.css');
101 1
        self::$_head_elements_added = true;
102
    }
103
104
    /**
105
     * Constructor. Add head elements when necessary and the ID column
106
     */
107 37
    public function __construct(string $identifier, string $datatype)
108
    {
109 37
        $this->_identifier = $identifier;
110 37
        $this->set_column('id', 'id', 'hidden:true, key:true');
111 37
        $this->set_option('datatype', $datatype);
112 37
        self::add_head_elements();
113
    }
114
115 32
    public function set_provider(provider $provider)
116
    {
117 32
        $this->_provider = $provider;
118
    }
119
120 3
    public function get_provider()
121
    {
122 3
        return $this->_provider;
123
    }
124
125
    /**
126
     * Returns the grid's ID
127
     */
128 31
    public function get_identifier() : string
129
    {
130 31
        return $this->_identifier;
131
    }
132
133
    /**
134
     * Set an option
135
     */
136 37
    public function set_option(string $key, $value, bool $autoquote_string = true) : self
137
    {
138 37
        $this->_raw_options[$key] = $value;
139 37
        if (   $autoquote_string
140 37
            && is_string($value)) {
141 37
            $value = '"' . str_replace('"', '\\"', $value) . '"';
142 33
        } elseif ($value === true) {
143 32
            $value = 'true';
144 33
        } elseif ($value === false) {
145
            $value = 'false';
146 33
        } elseif (is_array($value)) {
147 19
            $value = json_encode($value);
148
        }
149 37
        $this->_options[$key] = $value;
150 37
        return $this;
151
    }
152
153 26
    public function get_option(string $key)
154
    {
155 26
        if (empty($this->_raw_options[$key])) {
156 25
            return null;
157
        }
158 11
        return $this->_raw_options[$key];
159
    }
160
161
    /**
162
     * Set a column
163
     *
164
     * @param array $selectdata Should the column have a separate index, if so, which sort type
165
     */
166 10
    public function set_select_column(string $name, string $label, string $options, array $selectdata) : self
167
    {
168 10
        $selectstring = implode(';', array_map(
169 10
            function ($key, $value) {
170 10
                return $key . ':' . $value;
171 10
            },
172 10
            array_keys($selectdata),
173 10
            $selectdata
174 10
        ));
175
176 10
        if ($options !== '') {
177 8
            $options .= ', ';
178
        }
179 10
        $options .= 'stype: "select", searchoptions: {sopt: ["eq"], value: ":;' . $selectstring . '"}';
180 10
        $options .= ', edittype:"select", formatter:"select", editoptions:{value:"' . $selectstring . '"}';
181
182 10
        return $this->set_column($name, $label, $options);
183
    }
184
185
    /**
186
     * Set a column
187
     *
188
     * @param string $separate_index Should the column have a separate index, if so, which sort type
189
     */
190 37
    public function set_column(string $name, string $label, string $options = '', $separate_index = false) : self
191
    {
192 37
        if (empty($name)) {
193
            throw new midcom_error('Invalid column name ' . $name);
194
        }
195 37
        $this->_columns[$name] = [
196 37
            'label' => $label,
197 37
            'options' => $options,
198 37
            'separate_index' => $separate_index
199 37
        ];
200 37
        return $this;
201
    }
202
203 2
    public function add_pager(int $rows_per_page = 30) : self
204
    {
205 2
        $this->set_option('pager', '#p_' . $this->_identifier);
206 2
        $this->set_option('rowNum', $rows_per_page);
207 2
        return $this;
208
    }
209
210
    /**
211
     * Removes a column
212
     */
213
    public function remove_column(string $name)
214
    {
215
        if (   empty($name)
216
            || !array_key_exists($name, $this->_columns)) {
217
            throw new midcom_error('Invalid column name ' . $name);
218
        }
219
        if ($this->_columns[$name]['separate_index']) {
220
            unset($this->_columns[$name . '_index']);
221
        }
222
        unset($this->_columns[$name]);
223
    }
224
225
    /**
226
     * Set the grid's footer data
227
     *
228
     * @param mixed $data The data to set as array or the column name
229
     * @param mixed $value The value, if setting individual columns
230
     * @param boolean $formatted Should formatters be applied to footer data
231
     */
232 17
    public function set_footer_data($data, $value = null, bool $formatted = true)
233
    {
234 17
        if (null == $value) {
235 17
            $this->_footer_data = $data;
236
        } else {
237
            $this->_footer_data[$data] = $value;
238
        }
239 17
        $this->format_footer = $formatted;
240 17
        $this->set_option('footerrow', true);
241
    }
242
243
    /**
244
     * Add Javascript code that should be run before the widget constructor
245
     */
246 25
    public function prepend_js(string $string)
247
    {
248 25
        $this->_prepend_js .= $string . "\n";
249
    }
250
251
    /**
252
     * Renders the grid as HTML
253
     */
254 33
    public function render(?array $entries = null)
255
    {
256 33
        if (is_array($entries)) {
257 10
            if (null !== $this->_provider) {
258
                $this->_provider->set_rows($entries);
259
            } else {
260 10
                $this->_provider = new provider($entries, $this->get_option('datatype'));
0 ignored issues
show
It seems like $this->get_option('datatype') can also be of type null; however, parameter $datatype of midcom\grid\provider::__construct() 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

260
                $this->_provider = new provider($entries, /** @scrutinizer ignore-type */ $this->get_option('datatype'));
Loading history...
261 10
                $this->_provider->set_grid($this);
262
            }
263
        }
264 33
        echo (string) $this;
265
    }
266
267 34
    public function __toString()
268
    {
269 34
        if ($this->_provider) {
270 31
            $this->_provider->setup_grid();
271
        }
272
273 34
        $string = '<table id="' . $this->_identifier . '"></table>';
274 34
        $string .= '<div id="p_' . $this->_identifier . '"></div>';
275 34
        $string .= '<script type="text/javascript">//<![CDATA[' . "\n";
276 34
        $string .= $this->_prepend_js;
277 34
        $string .= 'midcom_grid_helper.setup_grid("' . $this->_identifier . '", {';
278
279 34
        $colnames = [];
280 34
        foreach ($this->_columns as $name => $column) {
281 34
            if ($column['separate_index']) {
282 32
                $colnames[] = 'index_' . $name;
283
            }
284 34
            $colnames[] = $column['label'];
285
        }
286 34
        $string .= "\ncolNames: " . json_encode($colnames) . ",\n";
287
288 34
        $string .= $this->_render_colmodel();
289
290 34
        $total_options = count($this->_options);
291 34
        $i = 0;
292 34
        foreach ($this->_options as $name => $value) {
293 34
            $string .= $name . ': ' . $value;
294 34
            if (++$i < $total_options) {
295 33
                $string .= ',';
296
            }
297 34
            $string .= "\n";
298
        }
299 34
        $string .= "});\n";
300
301 34
        if ($this->_footer_data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_footer_data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
302 17
            $format = $this->format_footer ? 'true' : 'false';
303 17
            $string .= 'jQuery("#' . $this->_identifier . '").jqGrid("footerData", "set", ' . json_encode($this->_footer_data) . ", " . $format . ");\n";
304
        }
305
306 34
        $string .= '//]]></script>';
307 34
        return $string;
308
    }
309
310 34
    private function _render_colmodel() : string
311
    {
312 34
        $string = "colModel: [\n";
313 34
        $total_columns = count($this->_columns);
314 34
        $i = 0;
315 34
        foreach ($this->_columns as $name => $column) {
316 34
            if ($column['separate_index']) {
317 32
                $string .= '{name: "index_' . $name . '", index: "index_' . $name . '", ';
318 32
                $string .= 'sorttype: "' . $column['separate_index'] . '", hidden: true}' . ",\n";
319
            }
320
321 34
            $string .= '{name: "' . $name . '", ';
322 34
            if ($column['separate_index']) {
323 32
                $string .= 'index: "index_' . $name . '"';
324
            } else {
325 34
                $string .= 'index: "' . $name . '"';
326
            }
327 34
            if (!empty($column['options'])) {
328 34
                $string .= ', ' . $column['options'];
329
            }
330 34
            $string .= '}';
331 34
            if (++$i < $total_columns) {
332 33
                $string .= ",\n";
333
            }
334
        }
335 34
        $string .= "\n],\n";
336 34
        return $string;
337
    }
338
}
339