Completed
Pull Request — master (#34)
by Klochok
11:26
created

IndexPage::renderBulkDeleteButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 4
crap 2
1
<?php
2
/**
3
 * HiPanel core package.
4
 *
5
 * @link      https://hipanel.com/
6
 * @package   hipanel-core
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2014-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\widgets;
12
13
use hipanel\grid\RepresentationCollectionFinder;
14
use hipanel\helpers\ArrayHelper;
15
use hipanel\helpers\StringHelper;
16
use hipanel\models\IndexPageUiOptions;
17
use hiqdev\higrid\representations\RepresentationCollectionInterface;
18
use hiqdev\yii2\export\widgets\IndexPageExportLinks;
19
use Yii;
20
use yii\base\InvalidParamException;
21
use yii\base\Model;
22
use yii\base\Widget;
23
use yii\bootstrap\ButtonDropdown;
24
use yii\data\DataProviderInterface;
25
use yii\helpers\Html;
26
use yii\helpers\Inflector;
27
use yii\helpers\Json;
28
use yii\helpers\Url;
29
use yii\web\JsExpression;
30
use yii\web\View;
31
32
class IndexPage extends Widget
33
{
34
    /**
35
     * @var string
36
     */
37
    protected $_layout;
38
39
    /**
40
     * @var Model the search model
41
     */
42
    public $model;
43
44
    /**
45
     * @var IndexPageUiOptions
46
     */
47
    private $uiModel;
48
49
    /**
50
     * @var object original view context.
51
     * It is used to render sub-views with the same context, as IndexPage
52
     */
53
    public $originalContext;
54
55
    /**
56
     * @var DataProviderInterface
57
     */
58
    public $dataProvider;
59
60
    /**
61
     * @var array Hash of document blocks, that can be rendered later in the widget's views
62
     * Blocks can be set explicitly on widget initialisation, or by calling [[beginContent]] and
63
     * [[endContent]]
64
     *
65
     * @see beginContent
66
     * @see endContent
67
     */
68
    public $contents = [];
69
70
    /**
71
     * @var string the name of current content block, that is under the render
72
     * @see beginContent
73
     * @see endContent
74
     */
75
    protected $_current = null;
76
77
    /**
78
     * @var array
79
     */
80
    public $searchFormData = [];
81
82
    /**
83
     * @var array
84
     */
85
    public $searchFormOptions = [];
86
87
    /**
88
     * @var string the name of view file that contains search fields for the index page. Defaults to `_search`
89
     * @see renderSearchForm()
90
     */
91
    public $searchView = '_search';
92
93
    /** {@inheritdoc} */
94
    public function init()
95
    {
96
        parent::init();
97
        $searchFormId = Json::htmlEncode("#{$this->getBulkFormId()}");
98
        $this->originalContext = Yii::$app->view->context;
99
        $view = $this->getView();
100
        // Fix a very narrow select2 input in the search tables
101
        $view->registerCss('#content-pjax .select2-dropdown--below { min-width: 170px!important; }');
102
        $view->registerJs(<<<"JS"
103
        // Checkbox
104
        var bulkcontainer = $('.box-bulk-actions fieldset');
105
        $($searchFormId).on('change', 'input[type="checkbox"]', function(event) {
106
            var checkboxes = $('input.grid-checkbox');
107
            if (checkboxes.filter(':checked').length > 0) {
108
                bulkcontainer.prop('disabled', false);
109
            } else if (checkboxes.filter(':checked').length === 0) {
110
                bulkcontainer.prop('disabled', true);
111
            }
112
        });
113
        // On/Off Actions TODO: reduce scope
114
        $(document).on('click', '.box-bulk-actions a', function (event) {
115
            var link = $(this);
116
            var action = link.data('action');
117
            var form = $($searchFormId);
118
            if (action) {
119
                form.attr({'action': action, method: 'POST'}).submit();
120
            }
121
        });
122
123
        // Do not open select2 when it is clearing
124
        var comboSelector = 'div[role=grid] :input[data-combo-field], .advanced-search :input[data-combo-field]';
125
        $(document).on('select2:unselecting', comboSelector, function(e) {
126
            $(e.target).data('unselecting', true);
127
        }).on('select2:open', comboSelector, function(e) { // note the open event is important
128
            var el = $(e.target);
129
            if (el.data('unselecting')) {
130
                el.removeData('unselecting'); // you need to unset this before close
131
                el.select2('close');
132
            }
133
        });
134
JS
135
        );
136
    }
137
138
    public function getUiModel()
139
    {
140
        if ($this->uiModel === null) {
141
            $this->uiModel = $this->originalContext->indexPageUiOptionsModel;
142
        }
143
144
        return $this->uiModel;
145
    }
146
147
    /**
148
     * Begins output buffer capture to save data in [[contents]] with the $name key.
149
     * Must not be called nested. See [[endContent]] for capture terminating.
150
     * @param string $name
151
     */
152
    public function beginContent($name)
153
    {
154
        if ($this->_current) {
155
            throw new InvalidParamException('Output buffer capture is already running for ' . $this->_current);
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated with message: since 2.0.14. Use [[InvalidArgumentException]] instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
156
        }
157
        $this->_current = $name;
158
        ob_start();
159
        ob_implicit_flush(false);
160
    }
161
162
    /**
163
     * Terminates output buffer capture started by [[beginContent()]].
164
     * @see beginContent
165
     */
166
    public function endContent()
167
    {
168
        if (!$this->_current) {
169
            throw new InvalidParamException('Outout buffer capture is not running. Call beginContent() first');
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated with message: since 2.0.14. Use [[InvalidArgumentException]] instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
170
        }
171
        $this->contents[$this->_current] = ob_get_contents();
172
        ob_end_clean();
173
        $this->_current = null;
174
    }
175
176
    /**
177
     * Returns content saved in [[content]] by $name.
178
     * @param string $name
179
     * @return string
180
     */
181
    public function renderContent($name)
182
    {
183
        return $this->contents[$name];
184
    }
185
186
    public function run()
187
    {
188
        $layout = $this->getLayout();
189
        if ($layout === 'horizontal') {
190
            $this->horizontalClientScriptInit();
191
        }
192
193
        return $this->render($layout);
194
    }
195
196
    private function horizontalClientScriptInit()
197
    {
198
        $view = $this->getView();
199
        $view->registerCss('
200
            .affix {
201
                top: 5px;
202
            }
203
            .affix-bottom {
204
                position: fixed!important;
205
            }
206
            @media (min-width: 768px) {
207
                .affix {
208
                    position: fixed;
209
                }
210
            }
211
            @media (max-width: 768px) {
212
                .affix {
213
                    position: static;
214
                }
215
            }
216
            .advanced-search[min-width~="150px"] form > div {
217
                width: 100%;
218
            }
219
            #scrollspy * {
220
              /* Свойства изменение которых необходимо отслеживать */
221
              transition-property: all;
222
223
              /* Устанавливаем "незаметную для глаза" длительность перехода */
224
              transition-duration: 1ms;
225
            }
226
        ');
227
        $view->registerJs("
228
            function affixInit() {
229
                $('#scrollspy').affix({
230
                    offset: {
231
                        top: ($('header.main-header').outerHeight(true) + $('section.content-header').outerHeight(true)) + 15,
232
                        bottom: ($('footer').outerHeight(true)) + 15
233
                    }
234
                });
235
            }
236
            $(document).on('pjax:end', function() {
237
                $('.advanced-search form > div').css({'width': '100%'});
238
                
239
                // Fix left search block position
240
                $(window).trigger('scroll');
241
            });
242
            if ($(window).height() > $('#scrollspy').outerHeight(true) && $(window).width() > 991) {
243
                if ( $('#scrollspy').outerHeight(true) < $('.horizontal-view .col-md-9 > .box').outerHeight(true) ) {
244
                    var fixAffixWidth = function() {
245
                        $('#scrollspy').each(function() {
246
                            $(this).width( $(this).parent().width() );
247
                        });
248
                    }
249
                    fixAffixWidth();
250
                    $(window).resize(fixAffixWidth);
251
                    affixInit();
252
                    $('a.sidebar-toggle').click(function() {
253
                        setTimeout(function(){
254
                            fixAffixWidth();
255
                        }, 500);
256
                    });
257
                }
258
            }
259
        ", View::POS_LOAD);
260
    }
261
262
    public function detectLayout()
263
    {
264
        return $this->getUiModel()->orientation;
265
    }
266
267
    /**
268
     * @param array $data
269
     * @void
270
     */
271
    public function setSearchFormData($data = [])
272
    {
273
        $this->searchFormData = $data;
274
    }
275
276
    /**
277
     * @param array $options
278
     * @void
279
     */
280
    public function setSearchFormOptions($options = [])
281
    {
282
        $this->searchFormOptions = $options;
283
    }
284
285
    public function renderSearchForm($advancedSearchOptions = [])
286
    {
287
        $advancedSearchOptions = array_merge($advancedSearchOptions, $this->searchFormOptions);
288
        ob_start();
289
        ob_implicit_flush(false);
290
        try {
291
            $search = $this->beginSearchForm($advancedSearchOptions);
292
            echo Yii::$app->view->render($this->searchView, array_merge(compact('search'), $this->searchFormData), $this->originalContext);
293
            $search->end();
294
        } catch (\Exception $e) {
295
            ob_end_clean();
296
            throw $e;
297
        }
298
299
        return ob_get_clean();
300
    }
301
302
    public function beginSearchForm($options = [])
303
    {
304
        return AdvancedSearch::begin(array_merge(['model' => $this->model], $options));
305
    }
306
307
    public function renderSearchButton()
308
    {
309
        return AdvancedSearch::renderButton() . "\n";
310
    }
311
312
    public function renderLayoutSwitcher()
313
    {
314
        return IndexLayoutSwitcher::widget(['uiModel' => $this->getUiModel()]);
315
    }
316
317
    public function renderPerPage()
318
    {
319
        $items = [];
320
        foreach ([25, 50, 100, 200, 500] as $pageSize) {
321
            $items[] = ['label' => $pageSize, 'url' => Url::current(['per_page' => $pageSize])];
322
        }
323
324
        return ButtonDropdown::widget([
325
            'label' => Yii::t('hipanel', 'Per page') . ': ' . $this->getUiModel()->per_page,
326
            'options' => ['class' => 'btn-default btn-sm'],
327
            'dropdown' => [
328
                'items' => $items,
329
            ],
330
        ]);
331
    }
332
333
    /**
334
     * Renders button to choose representation.
335
     * Returns empty string when nothing to choose (less then 2 representations available).
336
     *
337
     * @param RepresentationCollectionInterface $collection
338
     * @return string rendered HTML
339
     */
340
    public function renderRepresentations($collection)
341
    {
342
        $current = $this->getUiModel()->representation;
343
344
        $representations = $collection->getAll();
345
        if (count($representations) < 2) {
346
            return '';
347
        }
348
349
        $items = [];
350
        foreach ($representations as $name => $representation) {
351
            $items[] = [
352
                'label' => $representation->getLabel(),
353
                'url' => Url::current(['representation' => $name]),
354
            ];
355
        }
356
357
        return ButtonDropdown::widget([
358
            'label' => Yii::t('hipanel:synt', 'View') . ': ' . $collection->getByName($current)->getLabel(),
359
            'options' => ['class' => 'btn-default btn-sm'],
360
            'dropdown' => [
361
                'items' => $items,
362
            ],
363
        ]);
364
    }
365
366
    public function renderSorter(array $options)
367
    {
368
        return LinkSorter::widget(array_merge([
369
            'show' => true,
370
            'uiModel' => $this->getUiModel(),
371
            'dataProvider' => $this->dataProvider,
372
            'sort' => $this->dataProvider->getSort(),
373
            'buttonClass' => 'btn btn-default dropdown-toggle btn-sm',
374
        ], $options));
375
    }
376
377
    public function renderExport()
378
    {
379
        $isGridExportActionExists = (bool) Yii::$app->controller->createAction('export');
380
        /** @var RepresentationCollectionFinder $repColFinder */
381
        $repColFinder = Yii::createObject(RepresentationCollectionFinder::class);
382
        $collection = $repColFinder->findOrFallback();
383
        $isRepresentationExists = count($collection->getAll()) > 0;
384
        if ($isGridExportActionExists && $isRepresentationExists) {
385
            return IndexPageExportLinks::widget();
386
        }
387
    }
388
389
    public function getViewPath()
390
    {
391
        return parent::getViewPath() . DIRECTORY_SEPARATOR . (new \ReflectionClass($this))->getShortName();
392
    }
393
394
    public function getBulkFormId()
395
    {
396
        return 'bulk-' . Inflector::camel2id($this->model->formName());
397
    }
398
399
    public function beginBulkForm($action = '')
400
    {
401
        echo Html::beginForm($action, 'POST', ['id' => $this->getBulkFormId()]);
402
    }
403
404
    public function endBulkForm()
405
    {
406
        echo Html::endForm();
407
    }
408
409
    /**
410
     * @param $action
411
     * @param $text
412
     * @param array $options
413
     * @return string
414
     */
415
    public function renderBulkButton($action, $text, array $options = []): string
416
    {
417
        $color = ArrayHelper::remove($options, 'color', 'default');
418
        $confirm = ArrayHelper::remove($options, 'confirm', false);
419
        if ($confirm) {
420
            $options['onclick'] = new JsExpression("return confirm('{$confirm}');");
421
        }
422
        $defaultOptions = [
423
            'class' => "btn btn-$color btn-sm",
424
            'form' => $this->getBulkFormId(),
425
            'formmethod' => 'POST',
426
            'formaction' => Url::toRoute($action),
427
        ];
428
429
        return Html::submitButton($text, array_merge($defaultOptions, $options));
430
    }
431
432
    /**
433
     * @param $action
434
     * @param $text
435
     * @param $confirm
436
     * @param array $options
437
     * @return string
438
     */
439
    public function renderBulkDeleteButton($action, $text = null, $confirm = null, array $options = []): string
440
    {
441
        $text = $text ?? Yii::t('hipanel', 'Delete');
442
        $options['color'] = 'danger';
443
        $options['confirm'] = $confirm ?? Yii::t('hipanel', 'Are you sure you want to delete these items?');
444
445
        return $this->renderBulkConfirmButton($action, $text, $confirm, $options);
0 ignored issues
show
Documentation Bug introduced by
The method renderBulkConfirmButton does not exist on object<hipanel\widgets\IndexPage>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
446
    }
447
448
    /**
449
     * @return string
450
     */
451
    public function getLayout()
452
    {
453
        if ($this->_layout === null) {
454
            $this->_layout = $this->detectLayout();
455
        }
456
457
        return $this->_layout;
458
    }
459
460
    /**
461
     * @param string $layout
462
     */
463
    public function setLayout($layout)
464
    {
465
        $this->_layout = $layout;
466
    }
467
}
468