Passed
Branchfeature/minorImprovements (4046cc)
by Robin
04:29
created

AppCtrlCustomContainer   F

Complexity

Total Complexity 78

Size/Duplication

Total Lines 948
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
wmc 78
eloc 548
c 2
b 1
f 1
dl 0
loc 948
rs 2.16

19 Methods

Rating   Name   Duplication   Size   Complexity  
A delete() 0 7 1
B editContainer() 0 82 3
B saveContainers() 0 52 8
A deleteContainers() 0 20 1
D editVisibility() 0 92 18
A editContainers() 0 38 1
B containers() 0 68 5
B availableDisplayFields() 0 64 5
C saveVisibility() 0 104 14
A preSave() 0 12 3
B exportContainers() 0 55 5
A saveDisplayField() 0 11 1
A editDisplayField() 0 73 3
A importContainersConfirm() 0 39 1
A addDisplayField() 0 11 1
B importContainers() 0 61 5
A removeDisplayField() 0 8 1
A confirmDeleteContainers() 0 30 1
A save() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like AppCtrlCustomContainer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AppCtrlCustomContainer, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/************************************************************************
4
 * COFY https://www.siforyou.com                                         *
5
 ************************************************************************
6
 * Copyright (c) 2023 by Si-4You ( https://www.siforyou.com )            *
7
 *                                                                      *
8
 * This file is part of Cofy.                                           *
9
 *                                                                      *
10
 * Cofy is free software; you can redistribute it and/or modify         *
11
 * it under the terms of the GNU General Public License as published by *
12
 * the Free Software Foundation; either version 2, or (at your option)  *
13
 * any later version.                                                   *
14
 *                                                                      *
15
 * This program is distributed in the hope that it will be useful, but  *
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of           *
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                 *
18
 * See the  GNU General Public License for more details.                *
19
 *                                                                      *
20
 * You should have received a copy of the GNU General Public License    *
21
 * along with this program; if not, write to the Free Software          *
22
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,*
23
 * USA.                                                                 *
24
 ************************************************************************/
25
/**
26
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
27
 * @copyright Copyright (c) 2023 by SI-4YOU ({@link https://www.siforyou.com})
28
 */
29
30
namespace Capwelton\LibApp\Ctrl;
31
32
use Capwelton\LibApp\FuncApp;
33
use Capwelton\Widgets\Widgets\Form\WidgetForm;
0 ignored issues
show
Bug introduced by
The type Capwelton\Widgets\Widgets\Form\WidgetForm was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
use Capwelton\Widgets\Widgets\Layout\WidgetVBoxLayout;
0 ignored issues
show
Bug introduced by
The type Capwelton\Widgets\Widgets\Layout\WidgetVBoxLayout was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
35
use Capwelton\Widgets\Widgets\Item\WidgetLink;
0 ignored issues
show
Bug introduced by
The type Capwelton\Widgets\Widgets\Item\WidgetLink was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
use Capwelton\LibApp\Ui\Page\AppPage;
37
use Capwelton\LibApp\Set\AppCustomContainer;
38
use Capwelton\LibApp\Set\AppCustomContainerSet;
39
use Capwelton\LibOrm\ORMRecord;
0 ignored issues
show
Bug introduced by
The type Capwelton\LibOrm\ORMRecord was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
40
use Capwelton\LibOrm\Interfaces\ORMBoolInterface;
0 ignored issues
show
Bug introduced by
The type Capwelton\LibOrm\Interfaces\ORMBoolInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
41
use Capwelton\Widgets\Widgets\Helpers\WidgetAction;
0 ignored issues
show
Bug introduced by
The type Capwelton\Widgets\Widgets\Helpers\WidgetAction was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
42
use Cofy\Utilities\Addon\BabCharset;
0 ignored issues
show
Bug introduced by
The type Cofy\Utilities\Addon\BabCharset was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
43
use Cofy\Utilities\Addon\BabSort;
0 ignored issues
show
Bug introduced by
The type Cofy\Utilities\Addon\BabSort was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
44
use Cofy\Utilities\Func\Icons\FuncIcons;
0 ignored issues
show
Bug introduced by
The type Cofy\Utilities\Func\Icons\FuncIcons was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
45
46
use function Cofy\Utilities\Addon\bab_convertStringFromDatabase;
0 ignored issues
show
introduced by
The function Cofy\Utilities\Addon\bab_convertStringFromDatabase was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
47
use function Cofy\Utilities\Addon\bab_isAjaxRequest;
0 ignored issues
show
introduced by
The function Cofy\Utilities\Addon\bab_isAjaxRequest was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
48
use function Cofy\Utilities\Addon\bab_toHtml;
0 ignored issues
show
introduced by
The function Cofy\Utilities\Addon\bab_toHtml was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
49
use function Cofy\Utilities\Addon\bab_Widgets;
0 ignored issues
show
introduced by
The function Cofy\Utilities\Addon\bab_Widgets was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
50
use function Cofy\Utilities\bab_json_encode;
0 ignored issues
show
introduced by
The function Cofy\Utilities\bab_json_encode was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
51
52
/**
53
 * This controller manages actions that can be performed on custom sections
54
 *
55
 * @method FuncApp App()
56
 * @method self proxy()
57
 * @method AppCustomContainerSet getRecordSet()
58
 */
59
class AppCtrlCustomContainer extends AppCtrlRecord
60
{
61
62
    /**
63
     * @param string $itemId
64
     * @return WidgetVBoxLayout
65
     */
66
    public function availableDisplayFields($section = null, $itemId = null)
67
    {
68
        $W = bab_Widgets();
69
        $App = $this->App();
70
71
        $customSectionSet = $App->CustomContainerSet();
72
73
        $customSection = $customSectionSet->request($customSectionSet->id->is($section));
74
75
        $object = $customSection->object;
76
        $objectCtrl = $App->Controller()->$object(false);
77
78
        $allViewSections = $customSectionSet->select(
79
            $customSectionSet->all(
80
                $customSectionSet->view->is($customSection->view),
81
                $customSectionSet->object->is($customSection->object)
82
            )
83
        );
84
85
        $allViewFieldNames = array();
86
        foreach ($allViewSections as $viewSection) {
87
            $fields = $viewSection->getFields();
88
            foreach ($fields as $field) {
89
                $allViewFieldNames[$field['fieldname']] = $viewSection->view . ',' . $viewSection->rank . ',' . $viewSection->id;
90
            }
91
        }
92
93
        $box = $W->VBoxLayout($itemId);
94
95
        $box->addItem($W->Link($App->translate('Create new field'), $App->Controller()
96
            ->CustomField()
97
            ->add($customSection->object))
98
            ->setOpenMode(WidgetLink::OPEN_DIALOG_AND_RELOAD)
99
            ->addClass('widget-actionbutton', 'icon', FuncIcons::ACTIONS_LIST_ADD)
100
            ->setSizePolicy(FuncIcons::ICON_LEFT_SYMBOLIC));
101
102
        $availableFields = $objectCtrl->getAvailableDisplayFields();
103
104
        BabSort::asort($availableFields, 'description', BabSort::CASE_INSENSITIVE);
105
106
        foreach ($availableFields as $field) {
107
            $fieldName = $field['name'];
108
            $fieldDescription = $field['description'];
109
110
            $used = isset($allViewFieldNames[$fieldName]);
111
            $box->addItem($W->VBoxItems($W->Link($fieldDescription, $this->proxy()
0 ignored issues
show
Bug introduced by
$this->proxy()->saveDisp...d($section, $fieldName) of type true is incompatible with the type Widget_Action|bab_url|string expected by parameter $url of Func_Widgets::Link(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
            $box->addItem($W->VBoxItems($W->Link($fieldDescription, /** @scrutinizer ignore-type */ $this->proxy()
Loading history...
112
                ->saveDisplayField($section, $fieldName))
113
                ->addClass('widget-close-dialog', ($used ? 'widget-strong' : ''))
114
                ->setTitle($fieldName)
115
                ->setAjaxAction(), $W->Hidden()
116
                ->setName(array(
117
                    'sections',
118
                    $field['name']
119
                )))
120
                ->setSizePolicy('widget-list-element'));
121
        }
122
123
        $box->addClass('widget-3columns');
124
125
        /** @var WidgetAction $reloadAction */
126
        $reloadAction = $this->proxy()->availableDisplayFields($section, $box->getId());
127
        $box->setReloadAction($reloadAction);
128
129
        return $box;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $box returns the type Widget_VBoxLayout which is incompatible with the documented return type Capwelton\Widgets\Widgets\Layout\WidgetVBoxLayout.
Loading history...
130
    }
131
132
    /**
133
     */
134
    public function addDisplayField($section = null, $filter = null)
135
    {
136
        $App = $this->App();
137
138
        $page = $App->Ui()->Page();
139
        $page->setTitle($App->translate('Available fields'));
140
141
        $box = $this->availableDisplayFields($section);
142
143
        $page->addItem($box);
144
        return $page;
145
    }
146
147
    /**
148
     * @param int $section
149
     * @param string $fieldName
150
     * @return AppPage
151
     */
152
    public function editDisplayField($section, $fieldName)
153
    {
154
        $App = $this->App();
155
        $customSectionSet = $App->CustomContainerSet();
156
157
        $customSection = $customSectionSet->request($customSectionSet->id->is($section));
158
159
        $field = $customSection->getField($fieldName);
160
161
        if (! isset($field)) {
162
            return null;
163
        }
164
165
        $W = bab_Widgets();
166
167
        $object = $customSection->object;
168
        $objectCtrl = $App->Controller()->$object(false);
169
170
        $fieldName = $field['fieldname'];
171
        $params = $field['parameters'];
172
173
        $availableFields = $objectCtrl->getAvailableDisplayFields();
174
175
        $field = $availableFields[$fieldName];
176
        $fieldDescription = $field['description'];
177
        if (substr($fieldName, 0, 1) !== '_') {
178
            $fieldDescription = $App->translate($fieldDescription);
179
        }
180
181
        $Ui = $App->Ui();
182
        $page = $Ui->Page();
183
        $page->setTitle($fieldDescription);
184
185
        $editor = $Ui->BasicEditor();
186
        $editor->setHiddenValue('section', $section);
187
        $editor->setHiddenValue('fieldName', $fieldName);
188
189
        $box = $W->VBoxItems($W->LabelledWidget($App->translate('Type'), $W->Select()
190
            ->setName(array(
191
                'parameters',
192
                'type'
193
            ))
194
            ->addOption('', '')
195
            ->addOption('title1', $App->translate('Title 1'))
196
            ->addOption('title2', $App->translate('Title 2'))
197
            ->addOption('title3', $App->translate('Title 3'))), $W->LabelledWidget($App->translate('Label'), $W->LineEdit()
198
            ->setName(array(
199
                'parameters',
200
                'label'
201
            ))), $W->LabelledWidget($App->translate('Class'), $W->LineEdit()
202
            ->setName(array(
203
                'parameters',
204
                'classname'
205
            ))), $W->LabelledWidget($App->translate('Size policy'), $W->LineEdit()
206
            ->setName(array(
207
                'parameters',
208
                'sizePolicy'
209
            ))))
210
            ->setVerticalSpacing(1, 'em');
211
212
        $editor->setValues($params, array(
213
            'parameters'
214
        ));
215
216
        $editor->addItem($box);
217
218
        $editor->setSaveAction($this->proxy()->saveDisplayField());
219
220
        $editor->isAjax = true;
221
222
        $page->addItem($editor);
223
224
        return $page;
225
    }
226
227
    /**
228
     * @param int $section
229
     * @param string $fieldName
230
     * @return boolean
231
     */
232
    public function saveDisplayField($section = null, $fieldName = null, $parameters = null)
233
    {
234
        $set = $this->getRecordSet();
235
        $record = $set->request($set->id->is($section));
236
        $record->removeField($fieldName);
237
        $record->addField($fieldName, $parameters);
238
        $record->save();
239
240
        $this->addReloadSelector('.depends-custom-sections');
241
242
        return true;
243
    }
244
245
    /**
246
     * @param int $section
247
     * @param string $fieldName
248
     * @return boolean
249
     */
250
    public function removeDisplayField($section, $fieldName)
251
    {
252
        $set = $this->getRecordSet();
253
        $record = $set->request($set->id->is($section));
254
        $record->removeField($fieldName);
255
        $record->save();
256
        $this->addReloadSelector('.depends-custom-sections');
257
        return true;
258
    }
259
260
    /**
261
     * @param string $view
262
     * @param int $id
263
     * @param string $object
264
     * @return AppPage
265
     */
266
    public function editContainer($view, $id = null, $object = null)
267
    {
268
        $App = $this->App();
269
        $W = bab_Widgets();
270
271
        $customSectionSet = $this->getRecordSet();
272
        if (isset($id)) {
273
            $record = $customSectionSet->request($id);
274
        } else {
275
            $record = $customSectionSet->newRecord();
276
            $record->object = $object;
277
        }
278
279
        $Ui = $App->Ui();
280
        $page = $Ui->Page();
281
282
        $page->setTitle($App->translate('Container'));
283
        $page->addClass('app-page-editor');
284
285
        $editor = $Ui->BasicEditor();
286
        $editor->setHiddenValue('data[view]', $view);
287
        if ($record->id) {
288
            $editor->setHiddenValue('data[id]', $record->id);
289
        }
290
        $editor->setName('data');
291
        $editor->addItem($W->Hidden()
292
            ->setName('object'));
293
294
        $editor->addItem($W->LabelledWidget($App->translate('Name'), $W->LineEdit()
295
            ->setName('name')));
296
297
        $editor->addItem($W->LabelledWidget($App->translate('Element position'), $W->NumberEdit()
298
            ->setMin(0)
299
            ->setName('rank')));
300
301
        $sizePolicyClassnames = array(
302
            'col-md-1' => '1',
303
            'col-md-2' => '2',
304
            'col-md-3' => '3',
305
            'col-md-4' => '4',
306
            'col-md-5' => '5',
307
            'col-md-6' => '6',
308
            'col-md-7' => '7',
309
            'col-md-8' => '8',
310
            'col-md-9' => '9',
311
            'col-md-10' => '10',
312
            'col-md-11' => '11',
313
            'col-md-12' => '12',
314
            'left-panel' => 'left',
315
            'right-panel' => 'right'
316
        );
317
318
        $editor->addItem($W->LabelledWidget($App->translate('Size policy'), $W->Select()
319
            ->addOptions($sizePolicyClassnames)
320
            ->setName('sizePolicy')));
321
        $editor->addItem($W->LabelledWidget($App->translate('Layout'), $W->Select()
322
            ->addOptions(AppCustomContainer::getLayouts())
323
            ->setName('layout')));
324
325
        $editor->addItem($W->LabelledWidget($App->translate('Class'), $W->LineEdit()
326
            ->setName('classname')));
327
328
        $editor->setValues($record->getFormOutputValues(), array(
329
            'data'
330
        ));
331
332
        /** @var WidgetAction $saveAction */
333
        $saveAction = $this->proxy()->save();
334
335
        $editor->addButton($W->SubmitButton(/*'save'*/)
336
            ->validate(true)
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $validate of Widget_SubmitButton::validate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

336
            ->validate(/** @scrutinizer ignore-type */ true)
Loading history...
337
            ->setAction($saveAction)
338
            ->setAjaxAction()
339
            ->setLabel($App->translate('Save')));
340
341
        $editor->addButton($W->SubmitButton(/*'cancel'*/)
342
            ->addClass('widget-close-dialog')
343
            ->setLabel($App->translate('Cancel')));
344
345
        $page->addItem($editor);
346
347
        return $page;
348
    }
349
350
    /**
351
     * @param int $id
352
     * @return AppPage
353
     */
354
    public function editVisibility($id = null)
355
    {
356
        $App = $this->App();
357
        $Ui = $App->Ui();
358
359
        $page = $Ui->Page();
360
361
        $page->setTitle($App->translate('Section visibility'));
362
        $page->addClass('app-page-editor');
363
364
        $recordSet = $this->getRecordSet();
365
        $record = $recordSet->request($id);
366
367
        $object = $record->object;
368
        if (empty($object)) {
369
            $object = $record->container()->object;
370
        }
371
        if (strpos($object, $App->classPrefix) !== false) {
372
            $object = str_replace($App->classPrefix, '', $object);
373
        }
374
        $object .= 'Set';
375
376
        /* @var $objectRecordSet AppRecordSet */
377
        $objectRecordSet = $App->$object();
378
379
        $editor = $Ui->CriteriaEditor($objectRecordSet->all());
380
        $editor->setHiddenValue('data[id]', $record->id);
381
        $editor->setName('data');
382
383
        $manySets = $objectRecordSet->getHasManyRelations();
384
        $oneSets = $objectRecordSet->getHasOneRelations();
385
386
        if (! empty($record->visibilityCriteria)) {
387
            $arrCriteria = json_decode($record->visibilityCriteria, true);
388
389
            $data = array();
390
            foreach ($arrCriteria as $fieldName => $operation) {
391
                $data['field'] = $fieldName;
392
                foreach ($operation as $condition => $value) {
393
                    $data['condition']['default'] = $condition;
394
                    $data['condition']['bool'] = $condition;
395
                    if (is_array($value)) {
396
                        foreach ($value as $key => $subValue) {
397
                            foreach ($manySets as $manyFieldName => $manyRelation) {
398
                                if ($key . 'Set' == $manyRelation->getForeignSetClassName()) {
399
                                    foreach ($subValue as $subFieldName => $subConditions) {
400
                                        if ($subFieldName === '_foreignField') {
401
                                            continue;
402
                                        }
403
                                        $data['field'] = "{$manyFieldName}/{$subFieldName}";
404
                                        foreach ($subConditions as $subCondition => $subConditionValue) {
405
                                            $data['condition']['default'] = $subCondition;
406
                                            $data['condition']['bool'] = $subCondition;
407
                                            $value = $subConditionValue;
408
                                        }
409
                                    }
410
                                    break;
411
                                }
412
                            }
413
                            foreach ($oneSets as $oneFieldName => $oneRelation) {
414
                                if ($key . 'Set' == $oneRelation->getForeignSetClassName()) {
415
                                    foreach ($subValue as $subFieldName => $subConditions) {
416
                                        if ($subFieldName === '_foreignField') {
417
                                            continue;
418
                                        }
419
                                        $data['field'] = "{$oneFieldName}/{$subFieldName}";
420
                                        foreach ($subConditions as $subCondition => $subConditionValue) {
421
                                            $data['condition']['default'] = $subCondition;
422
                                            $data['condition']['bool'] = $subCondition;
423
                                            $value = $subConditionValue;
424
                                        }
425
                                    }
426
                                    break;
427
                                }
428
                            }
429
                        }
430
                    }
431
                    $data['value'] = $value;
432
                }
433
            }
434
435
            $editor->setValues($data, array(
436
                'data'
437
            ));
438
        }
439
440
        $editor->setSaveAction($this->proxy()->saveVisibility());
441
        $editor->isAjax = bab_isAjaxRequest();
442
443
        $page->addItem($editor);
444
445
        return $page;
446
    }
447
448
    /**
449
     * {@inheritdoc}
450
     * @see AppController::save()
451
     */
452
    public function save($data = null)
453
    {
454
        parent::save($data);
455
456
        $this->addReloadSelector('.depends-custom-sections');
457
458
        return true;
459
    }
460
461
    protected function preSave(ORMRecord  &$record, $data)
462
    {
463
        parent::preSave($record, $data);
464
        if (! isset($record->id) || empty($record->id)) {
465
            $set = $record->getParentSet();
466
            $nbContainers = $set->select(
467
                $set->all(
468
                    $set->object->is($record->object),
469
                    $set->view->is($record->view)
470
                )
471
            )->count();
472
            $record->rank = $nbContainers + 1;
473
        }
474
    }
475
476
    /**
477
     * {@inheritdoc}
478
     * @see AppCtrlRecord::delete()
479
     */
480
    public function delete($id)
481
    {
482
        parent::delete($id);
483
484
        $this->addReloadSelector('.depends-custom-sections');
485
486
        return true;
487
    }
488
489
    /**
490
     * @param array $data
491
     */
492
    public function saveVisibility($data = null)
493
    {
494
        $App = $this->App();
495
496
        $recordSet = $this->getRecordSet();
497
        $record = $recordSet->request($data['id']);
498
499
        $object = $record->object;
500
        if (empty($object)) {
501
            $object = $record->container()->object;
502
        }
503
        if (strpos($object, $App->classPrefix) !== false) {
504
            $object = str_replace($App->classPrefix, '', $object);
505
        }
506
        $object .= 'Set';
507
508
        /* @var $objectRecordSet AppRecordSet */
509
        $objectRecordSet = $App->$object();
510
511
        $field = $data['field'];
512
        $condition = $data['condition'];
513
514
        if (strpos($field, '/') !== false) {
515
            // The selected field is a hasMany or a hasOne relation
516
            list($selectedRelation, $selectedField) = explode('/', $field);
517
            $classPrefix = $App->classPrefix;
518
519
            $manyRelations = $objectRecordSet->getHasManyRelations();
520
            foreach ($manyRelations as $manyRelation) {
521
                if ($selectedRelation != $manyRelation->getOwnerSetFieldName()) {
522
                    continue;
523
                }
524
                $foreignSetName = str_replace($classPrefix, '', $manyRelation->getForeignSetClassName());
525
                $foreignSet = $App->$foreignSetName();
526
527
                $visibilityCriteria = '';
528
529
                if ($foreignSet) {
530
                    $selectedManyField = $foreignSet->$selectedField;
531
532
                    if ($selectedManyField instanceof ORMBoolInterface) {
533
                        $condition = $condition['bool'];
534
                    } else {
535
                        $condition = $condition['default'];
536
                    }
537
538
                    $criteria = $objectRecordSet->id->in($selectedManyField->{$condition}($data['value']), $manyRelation->getForeignFieldName());
539
540
                    $visibilityCriteria = $criteria->toJson();
541
                }
542
                break;
543
            }
544
545
            $oneRelations = $objectRecordSet->getHasOneRelations();
546
            foreach ($oneRelations as $oneRelation) {
547
                /* @var $oneRelation ORMOneRelation*/
548
                if ($selectedRelation != $oneRelation->getOwnerSetFieldName()) {
549
                    continue;
550
                }
551
                $foreignSetClassName = $oneRelation->getForeignSetClassName();
552
                $foreignSetName = str_replace($classPrefix, '', $foreignSetClassName);
553
                $foreignSet = $App->$foreignSetName();
554
555
                $visibilityCriteria = '';
556
557
                if ($foreignSet) {
558
                    $selectedOneField = $foreignSet->$selectedField;
559
560
                    if ($selectedOneField instanceof ORMBoolInterface) {
561
                        $condition = $condition['bool'];
562
                    } else {
563
                        $condition = $condition['default'];
564
                    }
565
566
                    $criteria = $objectRecordSet->$selectedRelation()->$selectedField->{$condition}($data['value']);
567
                    $visibilityCriteria = $criteria->toJson();
568
                }
569
                break;
570
            }
571
        } else {
572
573
            $field = $objectRecordSet->$field;
574
575
            if ($field instanceof ORMBoolInterface) {
576
                $condition = $condition['bool'];
577
            } else {
578
                $condition = $condition['default'];
579
            }
580
581
            $visibilityCriteria = '';
582
583
            if (! empty($condition)) {
584
                $criteria = $field->{$condition}($data['value']);
585
                $visibilityCriteria = $criteria->toJson();
586
            }
587
        }
588
589
        $record->visibilityCriteria = $visibilityCriteria;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $visibilityCriteria does not seem to be defined for all execution paths leading up to this point.
Loading history...
590
591
        $record->save();
592
593
        $this->addReloadSelector('.depends-custom-sections');
594
595
        return true;
596
    }
597
598
    /**
599
     * @param string $view
600
     * @param string $itemId
601
     * @return WidgetForm
602
     */
603
    public function containers($object, $view, $itemId = null)
604
    {
605
        $W = bab_Widgets();
606
        $App = $this->App();
607
608
        $customContainerCtrl = $App->Controller()->CustomContainer();
609
        $customSectionCtrlNoProxy = $App->Controller()->CustomSection(false);
610
        $customSectionCtrl = $customSectionCtrlNoProxy->proxy();
611
612
        $customContainerSet = $App->CustomContainerSet();
613
        $customContainers = $customContainerSet->select(
614
            $customContainerSet->all(
615
                $customContainerSet->object->is($object), 
616
                $customContainerSet->view->is($view)
617
            )
618
        );
619
        $customContainers->orderAsc($customContainerSet->rank);
620
621
        $containerBoxes = $W->Items();
622
        if(isset($itemId)){
623
            $containerBoxes->setId($itemId);
624
        }
625
626
        foreach ($customContainers as $customContainer) {
627
628
            $menu = $W->HBoxItems($W->Link('', $customSectionCtrl->edit($customContainer->id))
629
                ->addClass('icon', FuncIcons::ACTIONS_LIST_ADD)
630
                ->setOpenMode(WidgetLink::OPEN_DIALOG), $W->Link('', $customContainerCtrl->editContainer($view, $customContainer->id))
631
                ->addClass('icon', FuncIcons::ACTIONS_DOCUMENT_EDIT)
632
                ->setOpenMode(WidgetLink::OPEN_DIALOG), $W->Link('', $customContainerCtrl->editVisibility($customContainer->id))
633
                ->addClass('icon', FuncIcons::ACTIONS_SET_ACCESS_RIGHTS)
634
                ->setOpenMode(WidgetLink::OPEN_DIALOG), $W->Link('', $customContainerCtrl->confirmDelete($customContainer->id))
635
                ->addClass('icon', FuncIcons::ACTIONS_EDIT_DELETE)
636
                ->setOpenMode(WidgetLink::OPEN_DIALOG)
637
                ->setDialogClass('box red'));
0 ignored issues
show
Bug introduced by
The method setDialogClass() does not exist on Widget_Link. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

637
                ->/** @scrutinizer ignore-call */ setDialogClass('box red'));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
638
            $menu->setSizePolicy(FuncIcons::ICON_LEFT_16);
639
640
            $hasVisibilityBox = $W->FlowItems()->addClass('widget-100');
641
            if (! empty($customContainer->visibilityCriteria)) {
642
                $hasVisibilityBox->addItem(
643
                    $W->HBoxItems(
644
                        $W->Label($App->translate('This container has a visibility condition'))
645
                    )->setSizePolicy('alert alert-warning widget-100pc')
646
                );
647
            }
648
649
            $containerBox = $W->VBoxItems(
650
                $menu, 
651
                $hasVisibilityBox, 
652
                $customSectionCtrlNoProxy->sections($customContainer->id)
653
            );
654
655
            $containerBoxes->addItem(
656
                $containerBox->setSizePolicy($customContainer->sizePolicy)->addClass($customContainer->classname)
657
            );
658
        }
659
660
        if (isset($itemId)) {
661
            $containerBoxes->setId($itemId);
662
        }
663
664
        /** @var WidgetAction $reloadAction */
665
        $reloadAction = $this->proxy()->containers($object, $view, $containerBoxes->getId());
666
        $containerBoxes->setReloadAction($reloadAction);
667
668
        $containerBoxes->addClass('depends-custom-sections');
669
670
        return $containerBoxes;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $containerBoxes returns the type Widget_Layout which is incompatible with the documented return type Capwelton\Widgets\Widgets\Form\WidgetForm.
Loading history...
671
    }
672
673
    /**
674
     * @param string $view
675
     */
676
    public function exportContainers($object, $view)
677
    {
678
        $App = $this->App();
679
        $customContainerSet = $this->getRecordSet();
680
681
        $customContainers = $customContainerSet->select(
682
            $customContainerSet->all(
683
                $customContainerSet->view->is($view),
684
                $customContainerSet->object->is($object)
685
            )
686
        );
687
688
        $customSectionSet = $App->CustomSectionSet();
689
690
        $containersValues = array();
691
        foreach ($customContainers as $customContainer) {
692
            $values = $customContainer->getValues();
693
            unset($values['id']);
694
            unset($values['uuid']);
695
            unset($values['createdBy']);
696
            unset($values['createdOn']);
697
            unset($values['modifiedBy']);
698
            unset($values['modifiedOn']);
699
            unset($values['deletedBy']);
700
            unset($values['deletedOn']);
701
            unset($values['deleted']);
702
            $values['customSections'] = array();
703
            $customSections = $customSectionSet->select($customSectionSet->container->is($customContainer->id));
704
            foreach ($customSections as $customSection) {
705
                $customSectionValues = $customSection->getValues();
706
                unset($customSectionValues['id']);
707
                unset($customSectionValues['uuid']);
708
                unset($customSectionValues['createdBy']);
709
                unset($customSectionValues['createdOn']);
710
                unset($customSectionValues['modifiedBy']);
711
                unset($customSectionValues['modifiedOn']);
712
                unset($customSectionValues['deletedBy']);
713
                unset($customSectionValues['deletedOn']);
714
                unset($customSectionValues['deleted']);
715
                unset($customSectionValues['container']);
716
                $customSectionValues['fields'] = $customSection->getFields();
717
                $values['customSections'][] = $customSectionValues;
718
            }
719
            $containersValues[] = $values;
720
        }
721
722
        header('Content-type: application/json');
723
        header('Content-Disposition: attachment; filename="' . $object . '.' . ($view === '' ? 'default' : $view) . '.json"' . "\n");
724
        if (BabCharset::getIso() != BabCharset::UTF_8) {
725
            $containersValues = app_utf8Encode($containersValues);
726
        }
727
        $json = bab_json_encode($containersValues);
728
        $json = bab_convertStringFromDatabase($json, BabCharset::UTF_8);
729
730
        die($json);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
731
    }
732
733
    /**
734
     * @param string $view
735
     * @return AppPage
736
     */
737
    public function importContainersConfirm($object, $view = '')
738
    {
739
        $App = $this->App();
740
        $Ui = $App->Ui();
741
        $W = bab_Widgets();
742
743
        $page = $Ui->Page();
744
745
        $page->setTitle($App->translate('Import containers layout'));
746
747
        $editor = $Ui->BasicEditor();
748
        $editor->setHiddenValue('object', $object);
749
        $editor->isAjax = true;
750
751
        $editor->addItem(
752
            $W->Label($App->translate('The existing view will be replaced'))->setSizePolicy('alert alert-danger')
753
        );
754
755
        $editor->addItem(
756
            $W->LabelledWidget(
757
                $App->translate('View layout name'), 
758
                $W->LineEdit()->setValue($view), 
759
                'view'
760
            )
761
        );
762
763
        $editor->addItem(
764
            $W->LabelledWidget(
765
                $App->translate('File'), 
766
                $W->FilePicker(), 
767
                'containersfile'
768
            )
769
        );
770
771
        $editor->setSaveAction($this->proxy()->importContainers());
772
773
        $page->addItem($editor);
774
775
        return $page;
776
    }
777
778
    /**
779
     * @param string $view
780
     * @param string $containersfile
781
     * @return bool
782
     */
783
    public function importContainers($object = null, $view = null, $containersfile = null)
784
    {
785
        $this->requireSaveMethod();
786
        $App = $this->App();
787
        $W = bab_Widgets();
788
789
        $files = $W->FilePicker()->getFolder();
790
        $files->push('containersfile');
791
        $files->push($containersfile['uid']);
792
793
        $data = null;
794
        foreach ($files as $f) {
795
            $data = file_get_contents($f->toString());
796
            break;
797
        }
798
799
        if (! isset($data)) {
800
            $this->addError($App->translate('Unable to get file content'));
801
            return true;
802
        }
803
804
        $containersValues = json_decode($data, true);
805
806
        $customContainerSet = $App->CustomContainerSet();
807
        $customSectionSet = $App->CustomSectionSet();
808
809
        $customSectionSet->delete(
810
            $customSectionSet->container->in(
811
                $customSectionSet->all(
812
                    $customContainerSet->view->is($view),
813
                    $customContainerSet->object->is($object)
814
                ), 
815
                'id'
816
            )
817
        );
818
        $customContainerSet->delete(
819
            $customContainerSet->all(
820
                $customContainerSet->view->is($view),
821
                $customContainerSet->object->is($object)
822
            )
823
        );
824
825
        foreach ($containersValues as $containerValues) {
826
            $customContainer = $customContainerSet->newRecord();
827
            $customContainer->setValues($containerValues);
828
            $customContainer->object = $object;
829
            $customContainer->view = $view;
830
            $customContainer->save();
831
            foreach ($containerValues['customSections'] as $sectionValues) {
832
                $customSection = $customSectionSet->newRecord();
833
                $sectionValues['fields'] = json_encode($sectionValues['fields']);
834
                $customSection->setValues($sectionValues);
835
                $customSection->object = $object;
836
                $customSection->view = $view;
837
                $customSection->container = $customContainer->id;
838
                $customSection->save();
839
            }
840
        }
841
842
        $this->addReloadSelector('.depends-custom-sections');
843
        return true;
844
    }
845
846
    /**
847
     * @param string $view
848
     * @return bool
849
     */
850
    public function confirmDeleteContainers($object, $view)
851
    {
852
        $W = bab_Widgets();
853
        $App = $this->App();
854
        $Ui = $App->Ui();
855
856
        $page = $Ui->Page();
857
858
        $page->addClass('app-page-editor');
859
        $page->setTitle($App->translate('Delete view layout'));
860
861
        $form = $Ui->BasicEditor();
862
        $form->setName('data');
863
        $form->isAjax = true;
864
        $form->addItem($W->Title($App->translate('Confirm delete?'), 6));
865
        $form->addItem($W->Html(bab_toHtml($App->translate('It will not be possible to undo this deletion.'))));
866
867
        /** @var WidgetAction $confirmedAction */
868
        $confirmedAction = $this->proxy()->deleteContainers();
869
        $form->addItem($W->Hidden(null, 'object', $object));
870
        $form->addItem($W->Hidden(null, 'view', $view));
871
        $form->addButton($W->SubmitButton()
872
            ->setAjaxAction($confirmedAction)
873
            ->setLabel($App->translate('Delete')));
874
        $form->addButton($W->SubmitButton()
875
            ->setLabel($App->translate('Cancel'))
876
            ->addClass('widget-close-dialog'));
877
        $page->addItem($form);
878
879
        return $page;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $page returns the type Capwelton\LibApp\Ui\Page\AppPage which is incompatible with the documented return type boolean.
Loading history...
880
    }
881
882
    /**
883
     * @param string $view
884
     * @return bool
885
     */
886
    public function deleteContainers($data = null)
887
    {
888
        $this->requireDeleteMethod();
889
        $App = $this->App();
890
        $customContainerSet = $App->CustomContainerSet();
891
        $customSectionSet = $App->CustomSectionSet();
892
893
        $containerCriteria = array(
894
            $customContainerSet->view->is($data['view']),
895
            $customContainerSet->object->is($data['object'])
896
        );
897
898
        $customSectionSet->delete($customSectionSet->container->in($customContainerSet->all($containerCriteria), 'id'));
899
900
        $customContainerSet->delete($customContainerSet->all($containerCriteria));
901
902
        $this->addToast($App->translate('The view layout has been deleted'));
903
        $this->addReloadSelector('.depends-custom-sections');
904
905
        return true;
906
    }
907
908
    /**
909
     * Display an user interface to edit containers associated to a specific object.
910
     *
911
     * @param string $object
912
     * @param string $view
913
     * @return AppPage
914
     */
915
    public function editContainers($object, $view)
916
    {
917
        $App = $this->App();
918
        $W = bab_Widgets();
919
        $Ui = $App->Ui();
920
921
        $page = $Ui->Page();
922
923
        $page->setTitle(sprintf($App->translate('Edit view layout %s'), $view));
924
925
        $toolbar = $Ui->Toolbar();
926
927
        $toolbar->addItem($W->Link($App->translate('Delete view layout'), $this->proxy()
928
            ->confirmDeleteContainers($object, $view))
929
            ->addClass('widget-actionbutton', 'icon', FuncIcons::ACTIONS_EDIT_DELETE)
930
            ->setOpenMode(WidgetLink::OPEN_DIALOG)
931
            ->setDialogClass('box red'));
932
933
        $toolbar->addItem($W->Link($App->translate('Export view layout'), $this->proxy()
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->proxy()->exportContainers($object, $view) targeting Capwelton\LibApp\Ctrl\Ap...ner::exportContainers() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
934
            ->exportContainers($object, $view))
935
            ->addClass('widget-actionbutton', 'icon', FuncIcons::ACTIONS_DOCUMENT_UPLOAD));
936
937
        $toolbar->addItem($W->Link($App->translate('Import view layout'), $this->proxy()
938
            ->importContainersConfirm($object, $view))
939
            ->addClass('widget-actionbutton', 'icon', FuncIcons::ACTIONS_DOCUMENT_DOWNLOAD)
940
            ->setOpenMode(WidgetLink::OPEN_DIALOG));
941
942
        $toolbar->addItem($W->Link($App->translate('Add a block'), $this->proxy()
943
            ->editContainer($view, null, $object))
944
            ->addClass('widget-actionbutton', 'icon', FuncIcons::ACTIONS_LIST_ADD)
945
            ->setOpenMode(WidgetLink::OPEN_DIALOG));
946
947
        $form = $this->containers($object, $view);
948
949
        $page->addItem($toolbar);
950
        $page->addItem($form);
951
952
        return $page;
953
    }
954
955
    public function saveContainers($containers = null)
956
    {
957
        $customContainerSet = $this->getRecordSet();
958
959
        $containersFields = array();
960
        $containersRawFields = array();
961
962
        $rank = 0;
963
        $currentContainerId = 0;
964
        foreach (array_keys($containers) as $containerFieldId) {
965
966
            if (substr($containerFieldId, 0, 1) === '#') {
967
                $currentContainerId = substr($containerFieldId, 1);
968
969
                $customContainer = $customContainerSet->request($customContainerSet->id->is($currentContainerId));
970
971
                $containersFields[$currentContainerId] = array();
972
                $containersRawFields[$currentContainerId] = $customContainer->getRawFields();
973
974
                $customContainer->rank = $rank;
975
                $rank++;
976
                $customContainer->save();
977
                continue;
978
            }
979
980
            $containersFields[$currentContainerId][] = $containerFieldId;
981
        }
982
983
        foreach ($containersFields as $containerId => $containerFieldIds) {
984
            foreach ($containerFieldIds as $containerFieldId) {
985
                list($srcContainerId, $srcfieldId) = explode('.', $containerFieldId);
986
                if (! isset($containersRawFields[$srcContainerId][$srcfieldId])) {
987
                    return true;
988
                }
989
            }
990
        }
991
992
        foreach ($containersFields as $containerId => $containerFieldIds) {
993
            $newRawFields = array();
994
            foreach ($containerFieldIds as $containerFieldId) {
995
                list($srcContainerId, $srcfieldId) = explode('.', $containerFieldId);
996
                $newRawFields[] = $containersRawFields[$srcContainerId][$srcfieldId];
997
            }
998
999
            $customContainer = $customContainerSet->request($customContainerSet->id->is($containerId));
1000
            $customContainer->fields = implode(',', $newRawFields);
1001
            $customContainer->save();
1002
        }
1003
1004
        $this->addReloadSelector('.depends-custom-containers');
1005
1006
        return true;
1007
    }
1008
}
1009