Completed
Push — master ( eb085f...9445ad )
by Klochok
13:40
created

IndexPage::withPermission()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 6
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
                position: inherit;
219
            }
220
            #scrollspy * {
221
              /* Свойства изменение которых необходимо отслеживать */
222
              transition-property: all;
223
224
              /* Устанавливаем "незаметную для глаза" длительность перехода */
225
              transition-duration: 1ms;
226
            }
227
        ');
228
        $view->registerJs("
229
            function affixInit() {
230
                $('#scrollspy').affix({
231
                    offset: {
232
                        top: ($('header.main-header').outerHeight(true) + $('section.content-header').outerHeight(true)) + 15,
233
                        bottom: ($('footer').outerHeight(true)) + 15
234
                    }
235
                });
236
            }
237
            $(document).on('pjax:end', function() {
238
                $('.advanced-search form > div').css({'width': '100%'});
239
                
240
                // Fix left search block position
241
                $(window).trigger('scroll');
242
            });
243
            if ($(window).height() > $('#scrollspy').outerHeight(true) && $(window).width() > 991) {
244
                if ( $('#scrollspy').outerHeight(true) < $('.horizontal-view .col-md-9 > .box').outerHeight(true) ) {
245
                    var fixAffixWidth = function() {
246
                        $('#scrollspy').each(function() {
247
                            $(this).width( $(this).parent().width() );
248
                        });
249
                    }
250
                    fixAffixWidth();
251
                    $(window).resize(fixAffixWidth);
252
                    affixInit();
253
                    $('a.sidebar-toggle').click(function() {
254
                        setTimeout(function(){
255
                            fixAffixWidth();
256
                        }, 500);
257
                    });
258
                }
259
            }
260
        ", View::POS_LOAD);
261
    }
262
263
    public function detectLayout()
264
    {
265
        return $this->getUiModel()->orientation;
266
    }
267
268
    /**
269
     * @param array $data
270
     * @void
271
     */
272
    public function setSearchFormData($data = [])
273
    {
274
        $this->searchFormData = $data;
275
    }
276
277
    /**
278
     * @param array $options
279
     * @void
280
     */
281
    public function setSearchFormOptions($options = [])
282
    {
283
        $this->searchFormOptions = $options;
284
    }
285
286
    public function renderSearchForm($advancedSearchOptions = [])
287
    {
288
        $advancedSearchOptions = array_merge($advancedSearchOptions, $this->searchFormOptions);
289
        ob_start();
290
        ob_implicit_flush(false);
291
        try {
292
            $search = $this->beginSearchForm($advancedSearchOptions);
293
            echo Yii::$app->view->render($this->searchView, array_merge(compact('search'), $this->searchFormData), $this->originalContext);
294
            $search->end();
295
        } catch (\Exception $e) {
296
            ob_end_clean();
297
            throw $e;
298
        }
299
300
        return ob_get_clean();
301
    }
302
303
    public function beginSearchForm($options = [])
304
    {
305
        return AdvancedSearch::begin(array_merge(['model' => $this->model], $options));
306
    }
307
308
    public function renderSearchButton()
309
    {
310
        return AdvancedSearch::renderButton() . "\n";
311
    }
312
313
    public function renderLayoutSwitcher()
314
    {
315
        return IndexLayoutSwitcher::widget(['uiModel' => $this->getUiModel()]);
316
    }
317
318
    public function renderPerPage()
319
    {
320
        $items = [];
321
        foreach ([25, 50, 100, 200, 500] as $pageSize) {
322
            $items[] = ['label' => $pageSize, 'url' => Url::current(['per_page' => $pageSize])];
323
        }
324
325
        return ButtonDropdown::widget([
326
            'label' => Yii::t('hipanel', 'Per page') . ': ' . $this->getUiModel()->per_page,
327
            'options' => ['class' => 'btn-default btn-sm'],
328
            'dropdown' => [
329
                'items' => $items,
330
            ],
331
        ]);
332
    }
333
334
    /**
335
     * Renders button to choose representation.
336
     * Returns empty string when nothing to choose (less then 2 representations available).
337
     *
338
     * @param RepresentationCollectionInterface $collection
339
     * @return string rendered HTML
340
     */
341
    public function renderRepresentations($collection)
342
    {
343
        $current = $this->getUiModel()->representation;
344
345
        $representations = $collection->getAll();
346
        if (count($representations) < 2) {
347
            return '';
348
        }
349
350
        $items = [];
351
        foreach ($representations as $name => $representation) {
352
            $items[] = [
353
                'label' => $representation->getLabel(),
354
                'url' => Url::current(['representation' => $name]),
355
            ];
356
        }
357
358
        return ButtonDropdown::widget([
359
            'label' => Yii::t('hipanel:synt', 'View') . ': ' . $collection->getByName($current)->getLabel(),
360
            'options' => ['class' => 'btn-default btn-sm'],
361
            'dropdown' => [
362
                'items' => $items,
363
            ],
364
        ]);
365
    }
366
367
    public function renderSorter(array $options)
368
    {
369
        return LinkSorter::widget(array_merge([
370
            'show' => true,
371
            'uiModel' => $this->getUiModel(),
372
            'dataProvider' => $this->dataProvider,
373
            'sort' => $this->dataProvider->getSort(),
374
            'buttonClass' => 'btn btn-default dropdown-toggle btn-sm',
375
        ], $options));
376
    }
377
378
    public function renderExport()
379
    {
380
        $isGridExportActionExists = (bool) Yii::$app->controller->createAction('export');
381
        /** @var RepresentationCollectionFinder $repColFinder */
382
        $repColFinder = Yii::createObject(RepresentationCollectionFinder::class);
383
        $collection = $repColFinder->findOrFallback();
384
        $isRepresentationExists = count($collection->getAll()) > 0;
385
        if ($isGridExportActionExists && $isRepresentationExists) {
386
            return IndexPageExportLinks::widget();
387
        }
388
    }
389
390
    public function getViewPath()
391
    {
392
        return parent::getViewPath() . DIRECTORY_SEPARATOR . (new \ReflectionClass($this))->getShortName();
393
    }
394
395
    public function getBulkFormId()
396
    {
397
        return 'bulk-' . Inflector::camel2id($this->model->formName());
398
    }
399
400
    public function beginBulkForm($action = '')
401
    {
402
        echo Html::beginForm($action, 'POST', ['id' => $this->getBulkFormId()]);
403
    }
404
405
    public function endBulkForm()
406
    {
407
        echo Html::endForm();
408
    }
409
410
    /**
411
     * @param string|array $action
412
     * @param string $text
413
     * @param array $options
414
     * @return string
415
     */
416
    public function renderBulkButton($action, $text, array $options = []): string
417
    {
418
        $color = ArrayHelper::remove($options, 'color', 'default');
419
        $confirm = ArrayHelper::remove($options, 'confirm', false);
420
        if ($confirm) {
421
            $options['onclick'] = new JsExpression("return confirm('{$confirm}');");
422
        }
423
        $defaultOptions = [
424
            'class' => "btn btn-$color btn-sm",
425
            'form' => $this->getBulkFormId(),
426
            'formmethod' => 'POST',
427
            'formaction' => Url::toRoute($action),
428
        ];
429
430
        return Html::submitButton($text, array_merge($defaultOptions, $options));
431
    }
432
433
    /**
434
     * @param string $permission
435
     * @param string $button
436
     * @return string|null
437
     */
438
    public function withPermission(string $permission, string $button): ?string
439
    {
440
        if (Yii::$app->user->can($permission)) {
441
            return $button;
442
        }
443
    }
444
445
    /**
446
     * @param string|array $action
447
     * @param string|null $text
448
     * @param array $options
449
     * @return string
450
     */
451
    public function renderBulkDeleteButton($action, $text = null, array $options = []): string
452
    {
453
        $text = $text ?? Yii::t('hipanel', 'Delete');
454
        $options['color'] = $options['color'] ?? 'danger';
455
        $options['confirm'] = $options['confirm'] ?? Yii::t('hipanel', 'Are you sure you want to delete these items?');
456
457
        return $this->renderBulkButton($action, $text, $options);
458
    }
459
460
    /**
461
     * @return string
462
     */
463
    public function getLayout()
464
    {
465
        if ($this->_layout === null) {
466
            $this->_layout = $this->detectLayout();
467
        }
468
469
        return $this->_layout;
470
    }
471
472
    /**
473
     * @param string $layout
474
     */
475
    public function setLayout($layout)
476
    {
477
        $this->_layout = $layout;
478
    }
479
}
480