Completed
Pull Request — master (#34)
by Klochok
08:05
created

IndexPage::renderConfirmBulkButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 5
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\StringHelper;
15
use hipanel\models\IndexPageUiOptions;
16
use hiqdev\higrid\representations\RepresentationCollectionInterface;
17
use hiqdev\yii2\export\widgets\IndexPageExportLinks;
18
use Yii;
19
use yii\base\InvalidParamException;
20
use yii\base\Model;
21
use yii\base\Widget;
22
use yii\bootstrap\ButtonDropdown;
23
use yii\data\DataProviderInterface;
24
use yii\helpers\Html;
25
use yii\helpers\Inflector;
26
use yii\helpers\Json;
27
use yii\helpers\Url;
28
use yii\web\JsExpression;
29
use yii\web\View;
30
31
class IndexPage extends Widget
32
{
33
    /**
34
     * @var string
35
     */
36
    protected $_layout;
37
38
    /**
39
     * @var Model the search model
40
     */
41
    public $model;
42
43
    /**
44
     * @var IndexPageUiOptions
45
     */
46
    private $uiModel;
47
48
    /**
49
     * @var object original view context.
50
     * It is used to render sub-views with the same context, as IndexPage
51
     */
52
    public $originalContext;
53
54
    /**
55
     * @var DataProviderInterface
56
     */
57
    public $dataProvider;
58
59
    /**
60
     * @var array Hash of document blocks, that can be rendered later in the widget's views
61
     * Blocks can be set explicitly on widget initialisation, or by calling [[beginContent]] and
62
     * [[endContent]]
63
     *
64
     * @see beginContent
65
     * @see endContent
66
     */
67
    public $contents = [];
68
69
    /**
70
     * @var string the name of current content block, that is under the render
71
     * @see beginContent
72
     * @see endContent
73
     */
74
    protected $_current = null;
75
76
    /**
77
     * @var array
78
     */
79
    public $searchFormData = [];
80
81
    /**
82
     * @var array
83
     */
84
    public $searchFormOptions = [];
85
86
    /**
87
     * @var string the name of view file that contains search fields for the index page. Defaults to `_search`
88
     * @see renderSearchForm()
89
     */
90
    public $searchView = '_search';
91
92
    /** {@inheritdoc} */
93
    public function init()
94
    {
95
        parent::init();
96
        $searchFormId = Json::htmlEncode("#{$this->getBulkFormId()}");
97
        $this->originalContext = Yii::$app->view->context;
98
        $view = $this->getView();
99
        // Fix a very narrow select2 input in the search tables
100
        $view->registerCss('#content-pjax .select2-dropdown--below { min-width: 170px!important; }');
101
        $view->registerJs(<<<"JS"
102
        // Checkbox
103
        var bulkcontainer = $('.box-bulk-actions fieldset');
104
        $($searchFormId).on('change', 'input[type="checkbox"]', function(event) {
105
            var checkboxes = $('input.grid-checkbox');
106
            if (checkboxes.filter(':checked').length > 0) {
107
                bulkcontainer.prop('disabled', false);
108
            } else if (checkboxes.filter(':checked').length === 0) {
109
                bulkcontainer.prop('disabled', true);
110
            }
111
        });
112
        // On/Off Actions TODO: reduce scope
113
        $(document).on('click', '.box-bulk-actions a', function (event) {
114
            var link = $(this);
115
            var action = link.data('action');
116
            var form = $($searchFormId);
117
            if (action) {
118
                form.attr({'action': action, method: 'POST'}).submit();
119
            }
120
        });
121
122
        // Do not open select2 when it is clearing
123
        var comboSelector = 'div[role=grid] :input[data-combo-field], .advanced-search :input[data-combo-field]';
124
        $(document).on('select2:unselecting', comboSelector, function(e) {
125
            $(e.target).data('unselecting', true);
126
        }).on('select2:open', comboSelector, function(e) { // note the open event is important
127
            var el = $(e.target);
128
            if (el.data('unselecting')) {
129
                el.removeData('unselecting'); // you need to unset this before close
130
                el.select2('close');
131
            }
132
        });
133
JS
134
        );
135
    }
136
137
    public function getUiModel()
138
    {
139
        if ($this->uiModel === null) {
140
            $this->uiModel = $this->originalContext->indexPageUiOptionsModel;
141
        }
142
143
        return $this->uiModel;
144
    }
145
146
    /**
147
     * Begins output buffer capture to save data in [[contents]] with the $name key.
148
     * Must not be called nested. See [[endContent]] for capture terminating.
149
     * @param string $name
150
     */
151
    public function beginContent($name)
152
    {
153
        if ($this->_current) {
154
            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...
155
        }
156
        $this->_current = $name;
157
        ob_start();
158
        ob_implicit_flush(false);
159
    }
160
161
    /**
162
     * Terminates output buffer capture started by [[beginContent()]].
163
     * @see beginContent
164
     */
165
    public function endContent()
166
    {
167
        if (!$this->_current) {
168
            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...
169
        }
170
        $this->contents[$this->_current] = ob_get_contents();
171
        ob_end_clean();
172
        $this->_current = null;
173
    }
174
175
    /**
176
     * Returns content saved in [[content]] by $name.
177
     * @param string $name
178
     * @return string
179
     */
180
    public function renderContent($name)
181
    {
182
        return $this->contents[$name];
183
    }
184
185
    public function run()
186
    {
187
        $layout = $this->getLayout();
188
        if ($layout === 'horizontal') {
189
            $this->horizontalClientScriptInit();
190
        }
191
192
        return $this->render($layout);
193
    }
194
195
    private function horizontalClientScriptInit()
196
    {
197
        $view = $this->getView();
198
        $view->registerCss('
199
            .affix {
200
                top: 5px;
201
            }
202
            .affix-bottom {
203
                position: fixed!important;
204
            }
205
            @media (min-width: 768px) {
206
                .affix {
207
                    position: fixed;
208
                }
209
            }
210
            @media (max-width: 768px) {
211
                .affix {
212
                    position: static;
213
                }
214
            }
215
            .advanced-search[min-width~="150px"] form > div {
216
                width: 100%;
217
            }
218
            #scrollspy * {
219
              /* Свойства изменение которых необходимо отслеживать */
220
              transition-property: all;
221
222
              /* Устанавливаем "незаметную для глаза" длительность перехода */
223
              transition-duration: 1ms;
224
            }
225
        ');
226
        $view->registerJs("
227
            function affixInit() {
228
                $('#scrollspy').affix({
229
                    offset: {
230
                        top: ($('header.main-header').outerHeight(true) + $('section.content-header').outerHeight(true)) + 15,
231
                        bottom: ($('footer').outerHeight(true)) + 15
232
                    }
233
                });
234
            }
235
            $(document).on('pjax:end', function() {
236
                $('.advanced-search form > div').css({'width': '100%'});
237
                
238
                // Fix left search block position
239
                $(window).trigger('scroll');
240
            });
241
            if ($(window).height() > $('#scrollspy').outerHeight(true) && $(window).width() > 991) {
242
                if ( $('#scrollspy').outerHeight(true) < $('.horizontal-view .col-md-9 > .box').outerHeight(true) ) {
243
                    var fixAffixWidth = function() {
244
                        $('#scrollspy').each(function() {
245
                            $(this).width( $(this).parent().width() );
246
                        });
247
                    }
248
                    fixAffixWidth();
249
                    $(window).resize(fixAffixWidth);
250
                    affixInit();
251
                    $('a.sidebar-toggle').click(function() {
252
                        setTimeout(function(){
253
                            fixAffixWidth();
254
                        }, 500);
255
                    });
256
                }
257
            }
258
        ", View::POS_LOAD);
259
    }
260
261
    public function detectLayout()
262
    {
263
        return $this->getUiModel()->orientation;
264
    }
265
266
    /**
267
     * @param array $data
268
     * @void
269
     */
270
    public function setSearchFormData($data = [])
271
    {
272
        $this->searchFormData = $data;
273
    }
274
275
    /**
276
     * @param array $options
277
     * @void
278
     */
279
    public function setSearchFormOptions($options = [])
280
    {
281
        $this->searchFormOptions = $options;
282
    }
283
284
    public function renderSearchForm($advancedSearchOptions = [])
285
    {
286
        $advancedSearchOptions = array_merge($advancedSearchOptions, $this->searchFormOptions);
287
        ob_start();
288
        ob_implicit_flush(false);
289
        try {
290
            $search = $this->beginSearchForm($advancedSearchOptions);
291
            echo Yii::$app->view->render($this->searchView, array_merge(compact('search'), $this->searchFormData), $this->originalContext);
292
            $search->end();
293
        } catch (\Exception $e) {
294
            ob_end_clean();
295
            throw $e;
296
        }
297
298
        return ob_get_clean();
299
    }
300
301
    public function beginSearchForm($options = [])
302
    {
303
        return AdvancedSearch::begin(array_merge(['model' => $this->model], $options));
304
    }
305
306
    public function renderSearchButton()
307
    {
308
        return AdvancedSearch::renderButton() . "\n";
309
    }
310
311
    public function renderLayoutSwitcher()
312
    {
313
        return IndexLayoutSwitcher::widget(['uiModel' => $this->getUiModel()]);
314
    }
315
316
    public function renderPerPage()
317
    {
318
        $items = [];
319
        foreach ([25, 50, 100, 200, 500] as $pageSize) {
320
            $items[] = ['label' => $pageSize, 'url' => Url::current(['per_page' => $pageSize])];
321
        }
322
323
        return ButtonDropdown::widget([
324
            'label' => Yii::t('hipanel', 'Per page') . ': ' . $this->getUiModel()->per_page,
325
            'options' => ['class' => 'btn-default btn-sm'],
326
            'dropdown' => [
327
                'items' => $items,
328
            ],
329
        ]);
330
    }
331
332
    /**
333
     * Renders button to choose representation.
334
     * Returns empty string when nothing to choose (less then 2 representations available).
335
     *
336
     * @param RepresentationCollectionInterface $collection
337
     * @return string rendered HTML
338
     */
339
    public function renderRepresentations($collection)
340
    {
341
        $current = $this->getUiModel()->representation;
342
343
        $representations = $collection->getAll();
344
        if (count($representations) < 2) {
345
            return '';
346
        }
347
348
        $items = [];
349
        foreach ($representations as $name => $representation) {
350
            $items[] = [
351
                'label' => $representation->getLabel(),
352
                'url' => Url::current(['representation' => $name]),
353
            ];
354
        }
355
356
        return ButtonDropdown::widget([
357
            'label' => Yii::t('hipanel:synt', 'View') . ': ' . $collection->getByName($current)->getLabel(),
358
            'options' => ['class' => 'btn-default btn-sm'],
359
            'dropdown' => [
360
                'items' => $items,
361
            ],
362
        ]);
363
    }
364
365
    public function renderSorter(array $options)
366
    {
367
        return LinkSorter::widget(array_merge([
368
            'show' => true,
369
            'uiModel' => $this->getUiModel(),
370
            'dataProvider' => $this->dataProvider,
371
            'sort' => $this->dataProvider->getSort(),
372
            'buttonClass' => 'btn btn-default dropdown-toggle btn-sm',
373
        ], $options));
374
    }
375
376
    public function renderExport()
377
    {
378
        $isGridExportActionExists = (bool) Yii::$app->controller->createAction('export');
379
        /** @var RepresentationCollectionFinder $repColFinder */
380
        $repColFinder = Yii::createObject(RepresentationCollectionFinder::class);
381
        $collection = $repColFinder->findOrFallback();
382
        $isRepresentationExists = count($collection->getAll()) > 0;
383
        if ($isGridExportActionExists && $isRepresentationExists) {
384
            return IndexPageExportLinks::widget();
385
        }
386
    }
387
388
    public function getViewPath()
389
    {
390
        return parent::getViewPath() . DIRECTORY_SEPARATOR . (new \ReflectionClass($this))->getShortName();
391
    }
392
393
    public function getBulkFormId()
394
    {
395
        return 'bulk-' . Inflector::camel2id($this->model->formName());
396
    }
397
398
    public function beginBulkForm($action = '')
399
    {
400
        echo Html::beginForm($action, 'POST', ['id' => $this->getBulkFormId()]);
401
    }
402
403
    public function endBulkForm()
404
    {
405
        echo Html::endForm();
406
    }
407
408
    /**
409
     * @param $text
410
     * @param $action
411
     * @param string $color
412
     * @param array $options
413
     * @return string
414
     */
415
    public function renderBulkButton($text, $action, $color = 'default', array $options = []): string
416
    {
417
        $defaultOptions = [
418
            'class' => "btn btn-$color btn-sm",
419
            'form' => $this->getBulkFormId(),
420
            'formmethod' => 'POST',
421
            'formaction' => Url::toRoute($action),
422
        ];
423
424
        return Html::submitButton($text, array_merge($defaultOptions, $options));
425
    }
426
427
    /**
428
     * @param $text
429
     * @param $action
430
     * @param $color
431
     * @param $confirm
432
     * @param array $options
433
     * @return string
434
     */
435
    public function renderConfirmBulkButton($text, $action, $color, $confirm, array $options = []): string
436
    {
437
        $options['onclick'] = new JsExpression("return confirm('{$confirm}');");
438
439
        return $this->renderBulkButton($text, $action, $color, $options);
440
    }
441
442
    /**
443
     * @param $action
444
     * @return string
445
     */
446
    public function renderDeleteBulkButton($action): string
447
    {
448
        $message = Yii::t('hipanel', 'Are you sure you want to delete these items?');
449
450
        return $this->renderConfirmBulkButton(Yii::t('hipanel', 'Delete'), $action, 'danger', $message);
451
    }
452
453
    /**
454
     * @return string
455
     */
456
    public function getLayout()
457
    {
458
        if ($this->_layout === null) {
459
            $this->_layout = $this->detectLayout();
460
        }
461
462
        return $this->_layout;
463
    }
464
465
    /**
466
     * @param string $layout
467
     */
468
    public function setLayout($layout)
469
    {
470
        $this->_layout = $layout;
471
    }
472
}
473