Passed
Push — master ( 9aa0bb...ef47f7 )
by Laurent
01:52
created

app_fileAttachementIcon()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 14
c 1
b 0
f 0
nc 3
nop 3
dl 0
loc 20
rs 9.7998
1
<?php
2
//-------------------------------------------------------------------------
3
// OVIDENTIA http://www.ovidentia.org
4
// Ovidentia is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation; either version 2, or (at your option)
7
// any later version.
8
//
9
// This program is distributed in the hope that it will be useful, but
10
// WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
// See the GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program; if not, write to the Free Software
16
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17
// USA.
18
//-------------------------------------------------------------------------
19
/**
20
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
21
 * @copyright Copyright (c) 2009 by CANTICO ({@link http://www.cantico.fr})
22
 */
23
24
bab_Widgets()->includePhpClass('widget_Form');
25
bab_Widgets()->includePhpClass('widget_Frame');
26
bab_Widgets()->includePhpClass('widget_InputWidget');
27
bab_Widgets()->includePhpClass('widget_TableModelView');
28
29
require_once dirname(__FILE__) . '/ui.helpers.php';
30
31
/**
32
 * @return Widget_Form
33
 *
34
 *
35
 * @method self     setReadOnly($flag = true)
36
 * @method boolean  isReadOnly()
37
 * @method boolean  getReadOnly()
38
 * @method self     colon($colon = true)
39
 * @method boolean  isColon()
40
 * @method self     setHiddenValue($name, $value)
41
 * @method string   getHiddenValue($name)
42
 * @method array    getHiddenFields()
43
 * @method self     setHiddenValues($basename, $values)
44
 * @method Widget_InputWidget[] getFields($parent = null)
45
 * @method mixed    getValue($namePath)
46
 * @method self     setValue($namePath, $value)
47
 * @method self     setValues($row, $namePathBase = array())
48
 * @method array    getValues()
49
 * @method self     setAnchor($anchorname)
50
 * @method self     setSelfPageHiddenFields()
51
 * @method boolean  testMandatory()
52
 * @method self     checkUnsaved($check, $unsavedMessage)
53
 * @method self     setValidateAction(Widget_Action $action = null)
54
 * @method array    getClasses()
55
 * @method array    getFullName()
56
 * @method string   getName()
57
 * @method string   display(Widget_Canvas $canvas)
58
 * @method self     addClass($className)
59
 */
60
class app_Editor extends app_UiObject
61
{
62
    /**
63
     * @var Widget_Form $item
64
     */
65
    protected $item = null;
66
67
    /**
68
     * @var app_Record
69
     */
70
    protected $record;
71
72
    protected $ctrl;
73
74
    protected $saveAction;
75
    protected $saveLabel;
76
77
    protected $cancelAction;
78
    protected $cancelLabel;
79
80
    /**
81
     * @var Func_Widgets
82
     */
83
    protected $widgets;
84
85
    protected $buttonsLayout;
86
    protected $innerLayout;
87
88
    protected $failedAction = null;
89
    protected $successAction = null;
90
91
    /**
92
     * @var Widget_Section[]
93
     */
94
    protected $sections = array();
95
96
97
    public $isAjax = false;
98
99
    /**
100
     *
101
     * @var array
102
     */
103
    private $tmp_values;
104
105
106
    /**
107
     * @param	Func_App		$app
108
     * @param 	Widget_Layout 	$layout		The layout that will manage how widgets are displayed in this form.
109
     * @param 	string 			$id			The item unique id.
110
     */
111
    public function __construct(Func_App $app, $id = null, Widget_Layout $layout = null)
112
    {
113
        parent::__construct($app);
114
         // We simulate inheritance from Widget_VBoxLayout.
115
        $W = $this->widgets = bab_Widgets();
116
117
118
        $this->innerLayout = $layout;
119
        $this->buttonsLayout = $this->buttonsLayout();
120
121
        if (!isset($this->innerLayout)) {
122
            $this->innerLayout = $W->VBoxLayout();
123
            $this->innerLayout->setVerticalSpacing(1, 'em');
124
        }
125
126
        $this->innerLayout->addClass('app-loading-box'); // javascript can add the app-loading-on class to this container to show ajax loading progress in the form
0 ignored issues
show
Bug introduced by
The method addClass() does not exist on null. ( Ignorable by Annotation )

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

126
        $this->innerLayout->/** @scrutinizer ignore-call */ 
127
                            addClass('app-loading-box'); // javascript can add the app-loading-on class to this container to show ajax loading progress in the form

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...
127
128
        $layout = $W->VBoxItems(
129
            $this->innerLayout,
130
            $this->buttonsLayout
131
        );
132
//        $layout->setVerticalSpacing(2, 'em');
133
//		parent::__construct($id, $layout);
134
135
        // We simulate inheritance from Widget_Form.
136
        $this->setInheritedItem($W->Form($id, $layout));
137
        if (isset($this->tmp_values)) {
138
            $this->setValues($this->tmp_values[0], $this->tmp_values[1]);
139
        }
140
141
        $this->setFormStyles();
142
        $this->prependFields();
143
144
    }
145
146
147
    /**
148
     * @return Widget_Layout
149
     */
150
    protected function buttonsLayout()
151
    {
152
        $W = bab_Widgets();
153
        return $W->HBoxLayout()
154
            ->setSizePolicy('widget-form-buttons')
155
            ->setHorizontalSpacing(1, 'em');
156
    }
157
158
159
160
    protected function setFormStyles()
161
    {
162
        $this->addClass('app-editor');
163
    }
164
165
166
    /**
167
     * Set record and values to editor
168
     * @see app_Editor::getRecord()
169
     *
170
     * @param app_Record $record
171
     *
172
     * @return self
173
     */
174
    public function setRecord(app_Record $record)
175
    {
176
        $this->record = $record;
177
178
        if ($name = $this->getName()) {
179
            $values = $record->getFormOutputValues();
180
181
            $this->setValues($values, array($name));
182
        }
183
184
        return $this;
185
    }
186
187
    /**
188
     * @return app_Record
189
     */
190
    protected function getRecord()
191
    {
192
        return $this->record;
193
    }
194
195
196
    /**
197
     * @return app_RecordSet
198
     */
199
    protected function getRecordSet()
200
    {
201
        if ($this->record) {
202
            return $this->record->getParentSet();
203
        }
204
        return null;
205
    }
206
207
208
    public function setValues($row, $namePathBase = array())
209
    {
210
        if (isset($this->item)) {
211
            return $this->item->setValues($row, $namePathBase);
212
        } else {
213
            $this->tmp_values = array($row, $namePathBase);
214
            return true;
215
        }
216
    }
217
218
    /**
219
     *
220
     * @return app_Editor
221
     */
222
    public function setController($ctrl)
223
    {
224
        $this->ctrl = $ctrl;
225
        return $this;
226
    }
227
228
229
    /**
230
     *
231
     * @return app_Editor
232
     */
233
    public function setSaveAction($saveAction, $saveLabel = null)
234
    {
235
        $this->saveAction = $saveAction;
236
        $this->saveLabel = $saveLabel;
237
        return $this;
238
    }
239
240
    public function setSuccessAction($successAction)
241
    {
242
        $this->successAction = $successAction;
243
        return $this;
244
    }
245
246
    public function setFailedAction($failedAction)
247
    {
248
        $this->failedAction = $failedAction;
249
        return $this;
250
    }
251
252
    /**
253
     *
254
     * @return app_Editor
255
     */
256
    public function setCancelAction($cancelAction, $cancelLabel = null)
257
    {
258
        $this->cancelAction = $cancelAction;
259
        $this->cancelLabel = $cancelLabel;
260
        return $this;
261
    }
262
263
264
265
266
    /**
267
     * Adds a button to button box of the form.
268
     *
269
     * @return app_Editor
270
     */
271
    public function addButton(Widget_Item $button, Widget_Action $action = null)
272
    {
273
        $this->buttonsLayout->addItem($button);
274
        if (isset($action)) {
275
            $button->setAction($action);
0 ignored issues
show
Bug introduced by
The method setAction() does not exist on Widget_Item. It seems like you code against a sub-type of Widget_Item such as Widget_SimpleTreeView or Widget_Link or Widget_Dockable or Widget_SubmitButton or Widget_Tab. ( Ignorable by Annotation )

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

275
            $button->/** @scrutinizer ignore-call */ 
276
                     setAction($action);
Loading history...
276
        }
277
278
        return $this;
279
    }
280
281
282
    /**
283
     * Adds an item to the top part of the form.
284
     *
285
     * @return app_Editor
286
     */
287
    public function addItem(Widget_Displayable_Interface $item = null, $position = null)
288
    {
289
        $this->innerLayout->addItem($item, $position);
0 ignored issues
show
Unused Code introduced by
The call to Widget_Layout::addItem() has too many arguments starting with $position. ( Ignorable by Annotation )

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

289
        $this->innerLayout->/** @scrutinizer ignore-call */ 
290
                            addItem($item, $position);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
290
291
        return $this;
292
    }
293
294
    /**
295
     *
296
     *
297
     */
298
    protected function appendButtons()
299
    {
300
        $W = $this->widgets;
301
        $App = $this->App();
302
303
        if (isset($this->saveAction)) {
304
            $saveLabel = isset($this->saveLabel) ? $this->saveLabel : $App->translate('Save');
305
            $submitButton = $W->SubmitButton();
306
            $submitButton->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

306
            $submitButton->validate(/** @scrutinizer ignore-type */ true)
Loading history...
307
                ->setAction($this->saveAction)
308
                ->setFailedAction($this->failedAction)
309
                ->setSuccessAction($this->successAction)
310
                ->setLabel($saveLabel);
311
            if ($this->isAjax) {
312
                $submitButton->setAjaxAction();
313
            }
314
            $this->addButton($submitButton);
315
        }
316
317
        if (isset($this->cancelAction)) {
318
            $cancelLabel = isset($this->cancelLabel) ? $this->cancelLabel : $App->translate('Cancel');
319
            $this->addButton(
320
                $W->SubmitButton(/*'cancel'*/)
321
                     ->addClass('widget-close-dialog')
322
                    ->setAction($this->cancelAction)
323
                      ->setLabel($cancelLabel)
324
            );
325
        }
326
327
    }
328
329
    /**
330
     * Fields that will appear at the beginning of the form.
331
     *
332
     */
333
    protected function prependFields()
334
    {
335
336
    }
337
338
339
    /**
340
     * Fields that will appear at the end of the form.
341
     *
342
     */
343
    protected function appendFields()
344
    {
345
346
    }
347
348
349
    /**
350
     * Adds an item with a label and a description to the form.
351
     *
352
     * @param string                       $labelText
353
     * @param Widget_Displayable_Interface $item
354
     * @param string                       $fieldName
355
     * @param string                       $description
356
     * @param string						$suffix		 suffix for input field, example : unit
357
     *
358
     * @return Widget_LabelledWidget
359
     */
360
    public function labelledField($labelText, Widget_Displayable_Interface $item, $fieldName = null, $description = null, $suffix = null)
361
    {
362
        $W = $this->widgets;
363
        return $W->LabelledWidget($labelText, $item, $fieldName, $description, $suffix);
364
    }
365
366
367
    /**
368
     * Creates section in the editor.
369
     * If a section with the same header text (title) was already
370
     * created it is replaced by an empty section.
371
     *
372
     * @param string $headerText
373
     * @return Widget_Section
374
     */
375
    protected function createSection($headerText, $layout = null)
376
    {
377
        $W = bab_Widgets();
378
        if (!isset($layout)) {
379
            $layout = $W->VBoxLayout()->setVerticalSpacing(1, 'em');
380
        }
381
        $this->sections[$headerText] = $W->Section(
382
            $headerText,
383
            $layout
384
        )->setFoldable(true);
385
386
        return $this->sections[$headerText];
387
    }
388
389
    /**
390
     * Retrieves a section in the editor.
391
     *
392
     * @param string $headerText
393
     * @return Widget_Section
394
     */
395
    protected function getSection($headerText)
396
    {
397
        if (!isset($this->sections[$headerText])) {
398
            return null;
399
        }
400
        return $this->sections[$headerText];
401
    }
402
403
404
    /**
405
     * Returns the list of sections created via createSection().
406
     *
407
     * @return Widget_Section[]
408
     */
409
    public function getSections()
410
    {
411
        return $this->sections;
412
    }
413
414
415
    /**
416
     *
417
     */
418
    public function display(Widget_Canvas $canvas)
419
    {
420
        $this->appendFields();
421
        $this->appendButtons();
422
423
//         if (false === $this->getReadOnly())
424
//         {
425
//             if ($bItem = app_BreadCrumbs::getLastest())
426
//             {
427
//                 $this->setHiddenValue('_ctrl_previous', $bItem->uid);
428
//             }
429
//         }
430
431
432
        return parent::display($canvas);
433
    }
434
}
435
436
437
class app_Toolbar extends Widget_Frame
438
{
439
    public $local = false;
440
441
    public function __construct($id = null, Widget_Layout $layout = null)
442
    {
443
        if (!isset($layout)) {
444
            $W = bab_Widgets();
445
            $layout = $W->FlowLayout()->setHorizontalSpacing(1, 'em');
446
            $layout->setVerticalAlign('top');
447
        }
448
449
        parent::__construct($id, $layout);
450
    }
451
452
    /**
453
     *
454
     * @param string		 	$labelText
455
     * @param string			$iconName
456
     * @param Widget_Action		$action
457
     * @param string			$id
458
     * @return app_Toolbar
459
     */
460
    public function addButton($labelText = null, $iconName = null, $action = null, $id = null)
461
    {
462
        $W = bab_Widgets();
463
        $button = $W->Link($labelText, $action, $id);
464
        if (isset($iconName)) {
465
            $button->addClass('icon', $iconName);
466
        }
467
468
        $this->addItem($button);
469
470
        return $this;
471
    }
472
473
474
    public function addInstantForm(Widget_Displayable_Interface $form, $labelText = null, $iconName = null, $action = null, $id = null)
475
    {
476
        $W = bab_Widgets();
477
        if (isset($iconName)) {
478
            $content = $W->Icon($labelText, $iconName);
479
        } else {
480
            $content = $labelText;
481
        }
482
        $button = $W->Link($content, $action, $id);
483
484
        $this->addItem(
485
            $W->VBoxItems(
486
                $button->addClass('widget-instant-button'),
487
                $form->addClass('widget-instant-form')
488
            )->addClass('widget-instant-container')
489
        );
490
491
        if ($form->getTitle() === null) {
492
            $form->setTitle($labelText);
493
        }
494
495
496
        return $this;
497
498
    }
499
500
501
    public function display(Widget_Canvas $canvas)
502
    {
503
        if (!$this->local) {
504
            $this->addClass('widget-toolbar');
505
        } else {
506
            $this->addClass('app-toolbar');
507
        }
508
        $this->addClass(Func_Icons::ICON_LEFT_16);
509
510
        return parent::display($canvas);
511
    }
512
513
}
514
515
516
517
518
class app_RecordEditor extends app_Editor
519
{
520
521
    /**
522
     * @var app_RecordSet
523
     */
524
    public $recordSet = null;
525
526
    /**
527
     * Creates section in the editor.
528
     * If a section with the same header text (title) was already
529
     * created it is replaced by an empty section.
530
     *
531
     * @param string $headerText
532
     * @return Widget_Section
533
     */
534
    protected function addSection($id, $headerText, $layout = null)
535
    {
536
        $W = bab_Widgets();
537
        if (!isset($layout)) {
538
            $layout = $W->VBoxLayout()->setVerticalSpacing(1, 'em');
539
        }
540
        $this->sections[$id] = $W->Section(
541
            $headerText,
542
            $layout
543
            )->setFoldable(true);
544
545
            return $this->sections[$id];
546
    }
547
548
549
    /**
550
     * Retrieves a section in the editor.
551
     *
552
     * @param string $headerText
553
     * @return Widget_Section
554
     */
555
    protected function getSection($id)
556
    {
557
        if (!isset($this->sections[$id])) {
558
            return null;
559
        }
560
        return $this->sections[$id];
561
    }
562
563
564
    /**
565
     * @param string $textLabel
566
     * @param mixed $value
567
     * @return Widget_Displayable_Interface
568
     */
569
    function labelledWidget($textLabel, $value, app_CustomSection $section = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
570
    {
571
        $W = bab_Widgets();
572
573
        if ($value instanceof Widget_Displayable_Interface) {
574
            $widget = $value;
575
        } else {
576
            $widget = $W->Label($value);
577
        }
578
579
        if (isset($section)) {
580
            if ($textLabel === '__') {
581
                $fieldLayout = app_CustomSection::FIELDS_LAYOUT_NO_LABEL;
582
            } else {
583
                $fieldLayout = $section->fieldsLayout;
584
            }
585
            switch ($fieldLayout) {
586
                case app_CustomSection::FIELDS_LAYOUT_HORIZONTAL_LABEL:
587
                    return $W->FlowItems(
588
                    $W->Label($textLabel)->addClass('crm-horizontal-display-label', 'widget-strong')
589
                    ->setSizePolicy('widget-25pc'),
590
                    $widget->setSizePolicy('widget-75pc')
591
                    )->setHorizontalSpacing(1, 'ex')
592
                    ->setVerticalAlign('top');
593
594
                case app_CustomSection::FIELDS_LAYOUT_WIDE_HORIZONTAL_LABEL:
595
                    return $W->FlowItems(
596
                    $W->Label($textLabel)->addClass('crm-horizontal-display-label', 'widget-strong')
597
                    ->setSizePolicy('widget-50pc'),
598
                    $widget
599
                    )->setHorizontalSpacing(1, 'ex')
600
                    ->setVerticalAlign('top');
601
602
                case app_CustomSection::FIELDS_LAYOUT_NO_LABEL:
603
                    return $widget;
604
605
                case app_CustomSection::FIELDS_LAYOUT_VERTICAL_LABEL:
606
                default:
607
                    return $W->LabelledWidget($textLabel, $widget);
608
            }
609
        }
610
    }
611
612
613
    function labelledWidgetOptional($textLabel, $displayedValue, $value = null, app_CustomSection $section = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
614
    {
615
        if (!isset($value)) {
616
            $value = $displayedValue;
617
        }
618
        if (!isset($value) || is_numeric($value) && $value == 0 || is_string($value) && trim($value) == '' || ($value instanceof Widget_Layout && count($value->getItems()) <= 0)) {
619
            return null;
620
        }
621
        return $this->labelledWidget($textLabel, $displayedValue, $section);
622
    }
623
624
625
626
    protected function fieldOutput($field, $record, $fieldName)
627
    {
628
        if ($field instanceof ORM_CurrencyField) {
629
            $App = $this->App();
630
            $value = $App->shortFormatWithUnit($this->record->$fieldName, $App->translate('_euro_'));
631
        } else {
632
            $value = $field->output($this->record->$fieldName);
633
        }
634
        if (is_array($value)) {
635
            $value = implode(', ', $value);
636
        }
637
        return $value;
638
    }
639
640
641
    protected function getValueItem($customSection, $displayField, $label, $value)
642
    {
643
        return $this->labelledWidget(
644
            $label,
645
            //            $item,
646
            $value,
647
            $customSection
648
        );
649
    }
650
651
652
    protected function addSections($view)
653
    {
654
        $App = $this->App();
655
        $W = bab_Widgets();
656
657
658
        $this->addItem($W->Hidden()->setName('id'));
659
660
        $recordClassName = $this->record->getClassName();
661
662
        $customSectionSet = $App->CustomSectionSet();
663
        $conditions = array(
664
            $customSectionSet->object->is($recordClassName),
665
            $customSectionSet->view->is($view)
666
        );
667
668
        $customSections = $customSectionSet->select(
669
            $customSectionSet->all($conditions)
670
            );
671
        $customSections->orderAsc($customSectionSet->rank);
672
673
        $currentColumn = 0;
674
        $nbCol = 0;
675
676
        $row = $W->Items()->setSizePolicy('row');
677
        foreach ($customSections as $customSection) {
678
679
            if (isset($this->record) && !$customSection->isVisibleForRecord($this->record)) {
680
                continue;
681
            }
682
683
            list(, , $nbCol) = explode('-', $customSection->sizePolicy);
684
685
            if ($currentColumn + $nbCol > 12) {
686
                $this->addItem($row);
687
                $row = $W->Items()->setSizePolicy('row');
688
                $currentColumn = 0;
689
            }
690
            $currentColumn += $nbCol;
691
692
            $section = $this->getSection($customSection->id);
693
            if (!isset($section)) {
694
                $section = $this->addSection($customSection->id, $customSection->name);
695
                $section->addClass($customSection->classname);
696
                $section->setSizePolicy($customSection->sizePolicy);
697
698
                $section->setFoldable($customSection->foldable, $customSection->folded);
699
                $row->addItem($section);
700
            }
701
702
            $displayFields = $customSection->getFields();
703
704
            foreach ($displayFields as $displayField) {
705
                $widget = null;
706
                $item = null;
707
                $displayFieldName = $displayField['fieldname'];
708
                $parameters = $displayField['parameters'];
709
                $classname = isset($parameters['classname']) ? $parameters['classname'] : '';
710
                $label = isset($parameters['label']) && $parameters['label'] !== '__' ? $parameters['label'] : '';
711
                $displayFieldMethod = '_' . $displayFieldName;
712
713
                if (method_exists($this, $displayFieldMethod)) {
714
                    $widget = $this->$displayFieldMethod($customSection);
715
                    $item = $widget;
716
                } elseif ($this->recordSet->fieldExist($displayFieldName)) {
717
                    $field = $this->recordSet->getField($displayFieldName);
718
                    if ($label === '') {
719
                        $label = $field->getDescription();
720
                        if (substr($displayFieldName, 0, 1) !== '_') {
721
                            $label = $App->translate($label);
722
                        }
723
                    }
724
                    $widget = $field->getWidget();
725
                    if ($widget instanceof Widget_TextEdit || $widget instanceof Widget_Select) {
726
                        $widget->addClass('widget-100pc');
727
                    }
728
                    $item = $this->getValueItem($customSection, $displayField, $label, $widget);
729
                }
730
731
                if ($item) {
732
                    $item->addClass($classname);
733
                    $section->addItem($item);
734
                }
735
            }
736
        }
737
738
        if ($currentColumn + $nbCol > 0) {
739
            $this->addItem($row);
740
        }
741
    }
742
743
    public function sectionContent($customSectionId)
744
    {
745
        $App = $this->App();
746
747
        $customSectionSet = $App->CustomSectionSet();
748
        $customSection = $customSectionSet->get($customSectionId);
749
750
        if (isset($this->record) && !$customSection->isVisibleForRecord($this->record)) {
751
            return null;
752
        }
753
754
        $W = bab_Widgets();
755
756
        $section = $W->VBoxLayout()->setVerticalSpacing(1, 'em');
757
758
        $displayFields = $customSection->getFields();
759
760
        $customSection->fieldsLayout = app_CustomSection::FIELDS_LAYOUT_HORIZONTAL_LABEL;
761
762
        foreach ($displayFields as $displayField) {
763
            $widget = null;
764
            $item = null;
765
            $displayFieldName = $displayField['fieldname'];
766
            $parameters = $displayField['parameters'];
767
            $classname = isset($parameters['classname']) ? $parameters['classname'] : '';
768
            $label = isset($parameters['label']) && $parameters['label'] !== '__' ? $parameters['label'] : '';
769
            $displayFieldMethod = '_' . $displayFieldName;
770
771
            if (method_exists($this, $displayFieldMethod)) {
772
                $widget = $this->$displayFieldMethod($customSection);
773
                $item = $widget;
774
            } elseif ($this->recordSet->fieldExist($displayFieldName)) {
775
                $field = $this->recordSet->getField($displayFieldName);
776
                if ($label === '') {
777
                    $label = $field->getDescription();
778
                    if (substr($displayFieldName, 0, 1) !== '_') {
779
                        $label = $App->translate($label);
780
                    }
781
                }
782
                $widget = $field->getWidget();
783
                if ($widget instanceof Widget_TextEdit) {
784
                    $widget->addClass('widget-100pc widget-autoresize');
785
786
                }
787
                if ($widget instanceof Widget_Select) {
788
                    $widget->addClass('widget-100pc');
789
                }
790
                $item = $this->getValueItem($customSection, $displayField, $label, $widget);
791
            }
792
793
            if ($item) {
794
                $item->addClass($classname);
795
                $section->addItem($item);
796
            }
797
        }
798
799
        return $section;
800
    }
801
}
802
803
804
805
806
807
/**
808
 * A record frame is a frame displaying information about a app record.
809
 *
810
 * @since 1.0.40
811
 */
812
class app_RecordFrame extends app_UiObject // extends Widget_Frame
813
{
814
    /**
815
     * @var Widget_Section[]
816
     */
817
    protected $sections = array();
818
819
    /**
820
     * @var app_Record
821
     */
822
    protected $record = null;
823
824
    public function __construct(Func_App $app, app_Record $record, $id = null, Widget_Layout $layout = null)
825
    {
826
        parent::__construct($app);
827
828
        $W = bab_Widgets();
829
        $this->setInheritedItem($W->Frame($id, $layout));
830
    }
831
832
833
    /**
834
     * Creates section in the editor.
835
     * If a section with the same header text (title) was already
836
     * created it is replaced by an empty section.
837
     *
838
     * @param string $headerText
839
     * @return Widget_Section
840
     */
841
    protected function addSection($id, $headerText, $layout = null)
842
    {
843
        $W = bab_Widgets();
844
        if (!isset($layout)) {
845
            $layout = $W->VBoxLayout()->setVerticalSpacing(0.6, 'em');
0 ignored issues
show
Bug introduced by
0.6 of type double is incompatible with the type integer expected by parameter $spacing of Widget_Layout::setVerticalSpacing(). ( Ignorable by Annotation )

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

845
            $layout = $W->VBoxLayout()->setVerticalSpacing(/** @scrutinizer ignore-type */ 0.6, 'em');
Loading history...
846
        }
847
        $this->sections[$id] = $W->Section(
848
            $headerText,
849
            $layout
850
        )->setFoldable(true);
851
852
        return $this->sections[$id];
853
    }
854
855
856
    /**
857
     * Retrieves a section in the editor.
858
     *
859
     * @param string $headerText
860
     * @return Widget_Section
861
     */
862
    protected function getSection($id)
863
    {
864
        if (!isset($this->sections[$id])) {
865
            return null;
866
        }
867
        return $this->sections[$id];
868
    }
869
870
871
    /**
872
     * @param string $textLabel
873
     * @param Widget_Displayable_Interface|string $value
874
     * @param app_CustomSection $section
875
     * @return Widget_Displayable_Interface
876
     */
877
    public function labelledWidget($textLabel, $value, app_CustomSection $section = null)
878
    {
879
        $W = bab_Widgets();
880
881
        if ($value instanceof Widget_Displayable_Interface) {
882
            $widget = $value;
883
        } else {
884
            $widget = $W->Label($value);
885
        }
886
887
        if (isset($section)) {
888
            if ($textLabel === '__') {
889
                $fieldLayout = app_CustomSection::FIELDS_LAYOUT_NO_LABEL;
890
            } else {
891
                $fieldLayout = $section->fieldsLayout;
892
            }
893
            switch ($fieldLayout) {
894
                case app_CustomSection::FIELDS_LAYOUT_HORIZONTAL_LABEL:
895
                    return $W->FlowItems(
896
                    $W->Label($textLabel)->addClass('app-horizontal-display-label', 'widget-strong')
897
                        ->setSizePolicy('widget-25pc'),
898
                    $widget->setSizePolicy('widget-75pc')
899
                )->setHorizontalSpacing(1, 'ex')
900
                ->setVerticalAlign('top');
901
902
                case app_CustomSection::FIELDS_LAYOUT_WIDE_HORIZONTAL_LABEL:
903
                    return $W->FlowItems(
904
                    $W->Label($textLabel)->addClass('app-horizontal-display-label', 'widget-strong')
905
                        ->setSizePolicy('widget-50pc'),
906
                    $widget->setSizePolicy('widget-50pc')
907
                )->setHorizontalSpacing(1, 'ex')
908
                ->setVerticalAlign('top');
909
910
                case app_CustomSection::FIELDS_LAYOUT_VERY_WIDE_HORIZONTAL_LABEL:
911
                    return $W->FlowItems(
912
                    $W->Label($textLabel)->addClass('app-horizontal-display-label', 'widget-strong')
913
                        ->setSizePolicy('widget-75pc'),
914
                    $widget->setSizePolicy('widget-25pc')
915
                )->setHorizontalSpacing(1, 'ex')
916
                ->setVerticalAlign('top');
917
918
919
                case app_CustomSection::FIELDS_LAYOUT_NO_LABEL:
920
                    return $widget;
921
922
                case app_CustomSection::FIELDS_LAYOUT_VERTICAL_LABEL:
923
                default:
924
                    return $W->LabelledWidget($textLabel, $widget);
925
            }
926
        }
927
    }
928
929
930
    /**
931
     *
932
     * @param string $textLabel
933
     * @param mixed $displayedValue
934
     * @param mixed $value
935
     * @param app_CustomSection $section
936
     * @return Widget_Displayable_Interface|null
937
     */
938
    public function labelledWidgetOptional($textLabel, $displayedValue, $value = null, app_CustomSection $section = null)
939
    {
940
        if (!isset($value)) {
941
            $value = $displayedValue;
942
        }
943
        if (!isset($value) || is_numeric($value) && $value == 0 || is_string($value) && trim($value) == '' || ($value instanceof Widget_Layout && count($value->getItems()) <= 0)) {
944
            return null;
945
        }
946
        $labelledWidget = $this->labelledWidget($textLabel, $displayedValue, $section);
947
        return $labelledWidget;
948
    }
949
950
951
    /**
952
     *
953
     * @param ORM_Field $field
954
     * @param ORM_Record $record
955
     * @param string $fieldName
956
     * @return mixed
957
     */
958
    protected function fieldOutput($field, $record, $fieldName)
959
    {
960
        if ($field instanceof ORM_CurrencyField) {
961
            $App = $this->App();
962
            $value = $App->shortFormatWithUnit($this->record->$fieldName, $App->translate('_euro_'));
963
        } else {
964
            $value = $field->output($this->record->$fieldName);
965
        }
966
        if (is_array($value)) {
967
            $value = implode(', ', $value);
968
        }
969
        return $value;
970
    }
971
972
973
    /**
974
     *
975
     * @param app_CustomSection $customSection
976
     * @param array $displayField
977
     * @param string $textLabel
978
     * @param string $value
979
     * @return Widget_Displayable_Interface
980
     */
981
    protected function getValueItem(app_CustomSection $customSection, $displayField, $textLabel, $value)
982
    {
983
        if ($value instanceof Widget_Displayable_Interface) {
0 ignored issues
show
introduced by
$value is never a sub-type of Widget_Displayable_Interface.
Loading history...
984
            return $value;
985
        }
986
        $W = bab_Widgets();
987
        $parameters = $displayField['parameters'];
988
        if (isset($parameters['type'])) {
989
            if ($parameters['type'] === 'title1') {
990
                $item = $W->Title($value, 1);
991
            } elseif ($parameters['type'] === 'title2') {
992
                $item = $W->Title($value, 2);
993
            } elseif ($parameters['type'] === 'title3') {
994
                $item = $W->Title($value, 3);
995
            } else {
996
                $item = $W->Label($value);
997
            }
998
        } else {
999
            $item = $W->Label($value);
1000
        }
1001
        $labelledWidgetOptional = $this->labelledWidget( //Optional(
1002
            $textLabel,
1003
            $item,
1004
//            $value,
1005
            $customSection
1006
        );
1007
1008
        if (isset($labelledWidgetOptional)) {
1009
            $labelledWidgetOptional->addClass($displayField['fieldname']);
1010
        }
1011
1012
        return $labelledWidgetOptional;
1013
    }
1014
1015
1016
    /**
1017
     * @param string $view
1018
     */
1019
    protected function addSections($view)
1020
    {
1021
        $App = $this->App();
1022
        $W = bab_Widgets();
1023
1024
        $recordClassName = $this->record->getClassName();
1025
1026
        $customSectionSet = $App->CustomSectionSet();
1027
        $customSections = $customSectionSet->select(
1028
            $customSectionSet->object->is($recordClassName)->_AND_(
1029
                $customSectionSet->view->is($view)
1030
            )
1031
        );
1032
        $customSections->orderAsc($customSectionSet->rank);
1033
1034
        $currentColumn = 0;
1035
        $nbCol = 0;
1036
        $row = $W->Items()->setSizePolicy('row');
1037
        foreach ($customSections as $customSection) {
1038
1039
            list(, , $nbCol) = explode('-', $customSection->sizePolicy);
1040
1041
            if ($currentColumn + $nbCol > 12) {
1042
                $this->addItem($row);
0 ignored issues
show
Bug introduced by
The method addItem() does not exist on app_RecordFrame. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1042
                $this->/** @scrutinizer ignore-call */ 
1043
                       addItem($row);
Loading history...
1043
                $row = $W->Items()->setSizePolicy('row');
1044
                $currentColumn = 0;
1045
            }
1046
            $currentColumn += $nbCol;
1047
1048
            $section = $this->getSection($customSection->id);
1049
            if (!isset($section)) {
1050
                $section = $this->addSection($customSection->id, $customSection->name);
1051
                $section->addClass($customSection->classname);
1052
                $section->setSizePolicy($customSection->sizePolicy);
1053
1054
                $section->setFoldable($customSection->foldable, $customSection->folded);
1055
                $row->addItem($section);
1056
            }
1057
1058
            $displayFields = $customSection->getFields();
1059
1060
            foreach ($displayFields as $displayField) {
1061
                $value = null;
1062
                $displayFieldName = $displayField['fieldname'];
1063
                $parameters = $displayField['parameters'];
1064
                $classname = isset($parameters['classname']) ? $parameters['classname'] : '';
1065
                $label = isset($parameters['label']) ? $parameters['label'] : '';
1066
                $displayFieldMethod = '_' . $displayFieldName;
1067
1068
                if (method_exists($this, $displayFieldMethod)) {
1069
                    $value = $this->$displayFieldMethod($customSection);
1070
                } elseif ($this->recordSet->fieldExist($displayFieldName)) {
0 ignored issues
show
Bug introduced by
The property recordSet does not exist on app_RecordFrame. Did you mean record?
Loading history...
1071
                    $field = $this->recordSet->getField($displayFieldName);
1072
                    if ($label === '') {
1073
                        $label = $field->getDescription();
1074
                        if (substr($displayFieldName, 0, 1) !== '_') {
1075
                            $label = $App->translate($label);
1076
                        }
1077
                    }
1078
                    $value = $this->fieldOutput($field, $this->record, $displayFieldName);
1079
                }
1080
1081
                if (isset($value)) {
1082
                    $item = $this->getValueItem($customSection, $displayField, $label, $value);
1083
                    if ($item) {
1084
                        $item->addClass($classname);
1085
                        $section->addItem($item);
1086
                    }
1087
                }
1088
            }
1089
        }
1090
1091
        if ($currentColumn + $nbCol > 0) {
1092
            $this->addItem($row);
1093
        }
1094
    }
1095
}
1096
1097
1098
1099
1100
1101
1102
1103
1104
/**
1105
 * Table model view with App() method
1106
 *
1107
 *
1108
 */
1109
class app_TableModelView extends widget_TableModelView
1110
{
1111
1112
    /**
1113
     * Filter form reset button
1114
     */
1115
    protected $reset = null;
1116
1117
1118
    /**
1119
     * @var Func_App
1120
     */
1121
    private $app = null;
1122
1123
    /**
1124
     * @param Func_App $app
1125
     * @param string $id
1126
     */
1127
    public function __construct(Func_App $app = null, $id = null)
1128
    {
1129
        parent::__construct($id);
1130
        $this->setApp($app);
1131
    }
1132
1133
    /**
1134
     * Forces the Func_App object to which this object is 'linked'.
1135
     *
1136
     * @param Func_App	$app
1137
     * @return self
1138
     */
1139
    public function setApp(Func_App $app = null)
1140
    {
1141
        $this->app = $app;
1142
        return $this;
1143
    }
1144
1145
    /**
1146
     * Get App object to use with this SET
1147
     *
1148
     * @return Func_App
1149
     */
1150
    public function App()
1151
    {
1152
        if (!isset($this->app)) {
1153
            // If the app object was not specified (through the setApp() method)
1154
            // we try to select one according to the classname prefix.
1155
            list($prefix) = explode('_', get_class($this));
1156
            $functionalityName = ucwords($prefix);
1157
            $this->app = @bab_functionality::get('App/' . $functionalityName);
0 ignored issues
show
Documentation Bug introduced by
@bab_functionality::get(...' . $functionalityName) is of type bab_functionality, but the property $app was declared to be of type Func_App. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
1158
            if (!$this->app) {
1159
                $this->app = @bab_functionality::get('App');
1160
            }
1161
        }
1162
        return $this->app;
1163
    }
1164
1165
1166
1167
	protected function addCustomFields(app_RecordSet $recordSet)
1168
	{
1169
		$customFields = $recordSet->getCustomFields();
1170
1171
        foreach ($customFields as $customField) {
1172
            $fieldname = $customField->fieldname;
1173
            $this->addColumn(
1174
                widget_TableModelViewColumn($recordSet->$fieldname, $customField->name)
1175
                  ->setSortable(true)
1176
                  ->setExportable(true)
1177
                  ->setSearchable($customField->searchable)
0 ignored issues
show
Bug Best Practice introduced by
The property searchable does not exist on app_CustomField. Since you implemented __get, consider adding a @property annotation.
Loading history...
1178
				  ->setVisible($customField->visible)
0 ignored issues
show
Bug Best Practice introduced by
The property visible does not exist on app_CustomField. Since you implemented __get, consider adding a @property annotation.
Loading history...
1179
            );
1180
        }
1181
	}
1182
1183
1184
    /**
1185
     * Get a generic filter panel
1186
     * Use setPageLength to define the default number of items per page
1187
     *
1188
     * @param	array	$filter		Filter values
1189
     * @param	string	$name		filter form name
1190
     * @param	string	$anchor		anchor in destination page of filter, the can be a Widget_Tab id
1191
     *
1192
     * @return Widget_Filter
1193
     */
1194
    public function filterPanel($filter = null, $name = null)
1195
    {
1196
        $W = bab_Widgets();
1197
1198
        $filterPanel = $W->Filter();
1199
        $filterPanel->setLayout($W->VBoxLayout());
1200
        if (isset($name)) {
1201
            $filterPanel->setName($name);
1202
        }
1203
1204
        $pageLength = $this->getPageLength();
1205
        if (null === $pageLength) {
0 ignored issues
show
introduced by
The condition null === $pageLength is always false.
Loading history...
1206
            $pageLength = 15;
1207
        }
1208
1209
        $pageNumber = $this->getCurrentPage();
1210
        if (null === $pageNumber) {
0 ignored issues
show
introduced by
The condition null === $pageNumber is always false.
Loading history...
1211
            $pageNumber = 0;
1212
        }
1213
1214
        $this->setPageLength(isset($filter['pageSize']) ? $filter['pageSize'] : $pageLength);
1215
        $this->setCurrentPage(isset($filter['pageNumber']) ? $filter['pageNumber'] : $pageNumber);
1216
1217
        if (isset($name)) {
1218
            $this->sortParameterName = $name . '[filter][sort]';
1219
        } else {
1220
            $this->sortParameterName = 'filter[sort]';
1221
        }
1222
1223
        if (isset($filter['sort'])) {
1224
            $this->setSortField($filter['sort']);
1225
        } elseif (!isset($this->sortField)) {
1226
1227
            if (method_exists($this, 'getDefaultSortField')) {
1228
                $this->setSortField($this->getDefaultSortField());
1229
            } else {
1230
                $columns = $this->getVisibleColumns();
1231
                $sortField = key($columns);
1232
                $this->setSortField($sortField);
1233
            }
1234
        }
1235
1236
        $form = $this->getFilterForm();
1237
1238
        if (isset($filter)) {
1239
            if (isset($name)) {
1240
                $path = array($name, 'filter');
1241
            } else {
1242
                $path = array('filter');
1243
            }
1244
            $form->setValues($filter, $path);
1245
        }
1246
1247
1248
        $filterPanel->setFilter($form);
1249
        $filterPanel->setFiltered($this);
1250
1251
        return $filterPanel;
1252
    }
1253
1254
1255
    /**
1256
     * Add the filter fields to the filter form
1257
     * @param Widget_Form $form
1258
     *
1259
     */
1260
    protected function handleAdvancedFilterFields(Widget_Item $form)
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed. ( Ignorable by Annotation )

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

1260
    protected function handleAdvancedFilterFields(/** @scrutinizer ignore-unused */ Widget_Item $form)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1261
    {
1262
1263
    }
1264
1265
1266
1267
1268
    protected function isFilterFieldSpecified($filter, $fieldPath)
1269
    {
1270
        if (isset($filter[$fieldPath])) {
1271
            $value = $filter[$fieldPath];
1272
            if (is_array($value)) {
1273
                foreach ($value as $val) {
1274
                    $val = trim($val);
1275
                    if (!empty($val)) {
1276
                        return true;
1277
                    }
1278
                }
1279
                return false;
1280
            }
1281
1282
            if (trim($value) !== '') {
1283
                return true;
1284
            }
1285
        }
1286
1287
        return false;
1288
    }
1289
1290
1291
1292
    /**
1293
     * Handle label and input widget merge in one item before adding to the filter form
1294
     * default is a vertical box layout
1295
     *
1296
     * @param Widget_Label                  $label
1297
     * @param Widget_Displayable_Interface  $input
1298
     * @return Widget_Item
1299
     */
1300
    protected function handleFilterLabel(Widget_Label $label, Widget_Displayable_Interface $input)
1301
    {
1302
        $W = bab_Widgets();
1303
        if ($input instanceof Widget_CheckBox) {
1304
            return $W->HBoxItems(
1305
                $input,
1306
                $label
1307
            )->setVerticalAlign('middle')
1308
            ->setHorizontalSpacing(1, 'ex');
1309
        }
1310
        return $W->VBoxItems(
1311
            $label,
1312
            $input
1313
        );
1314
    }
1315
1316
    protected function getSearchItem()
1317
    {
1318
        $W = bab_Widgets();
1319
1320
        return $W->LineEdit()->setName('search')->addClass('widget-100pc');
1321
    }
1322
1323
    /**
1324
     * Get an advanced form filter
1325
     * @see Widget_Filter
1326
     *
1327
     * @param   string          $id
1328
     * @param   array           $filter
1329
     *
1330
     * @return Widget_Form
1331
     */
1332
    public function getAdvancedFilterForm($id = null, $filter = null)
1333
    {
1334
        $App = $this->App();
1335
1336
        $W = bab_Widgets();
1337
1338
        $formItem = $this->getSearchItem();
1339
1340
        $activeFiltersFrame = $W->Frame(
1341
            null,
1342
            $W->FlowItems(
1343
                $W->VBoxItems(
1344
                    $W->Label($App->translate('Search'))->setAssociatedWidget($formItem),
1345
                    $formItem
1346
                )->setSizePolicy('col-lg-2 col-md-3 col-sm-6 col-xs-12')
1347
            )
1348
            ->setHorizontalSpacing(1, 'em')
1349
            ->setVerticalSpacing(1, 'em')
1350
            ->setVerticalAlign('bottom')
1351
        );
1352
1353
        $advancedFiltersFrame = $W->Section(
1354
            $App->translate('Advanced filters'),
1355
            $W->FlowLayout()
1356
                ->setHorizontalSpacing(1, 'em')
1357
                ->setVerticalSpacing(1, 'em')
1358
                ->setVerticalAlign('bottom'),
1359
            7
1360
        )->setFoldable(true, true)
1361
1362
        ->setSizePolicy('row');
1363
1364
1365
        $columns = $this->getVisibleColumns();
1366
1367
        foreach ($columns as $fieldName => $column) {
1368
            $field = $column->getField();
1369
            if (! $column->isSearchable()) {
1370
                continue;
1371
            }
1372
1373
            if (! ($field instanceof ORM_Field)) {
1374
                $field = null;
1375
            }
1376
1377
            $label = $this->handleFilterLabelWidget($fieldName, $field);
1378
            $input = $this->handleFilterInputWidget($fieldName, $field);
1379
1380
1381
            if (isset($input) && isset($label)) {
1382
1383
                $input->setName($fieldName);
1384
                $label->setAssociatedWidget($input);
1385
1386
                $input->addClass('widget-100pc');
1387
1388
                $formItem = $this->handleFilterLabel($label, $input);
1389
                $formItem->setSizePolicy('col-lg-2 col-md-3 col-sm-6 col-xs-12');
1390
                $formItem->addClass('field_' . $fieldName);
1391
1392
                $mainSearch = (method_exists($column, 'isMainSearch') && $column->isMainSearch());
1393
1394
                if ($mainSearch || $this->isFilterFieldSpecified($filter, $column->getFieldPath())) {
1395
                    $activeFiltersFrame->addItem($formItem);
1396
                } else {
1397
                    $advancedFiltersFrame->addItem($formItem);
1398
                }
1399
            }
1400
        }
1401
1402
1403
        if (! $this->submit) {
1404
            $this->submit = $W->SubmitButton();
1405
            $this->submit->setLabel(widget_translate('Filter'));
1406
        }
1407
1408
1409
        $form = $W->Form($id);
1410
        $form->setReadOnly(true);
1411
        $form->setName('filter');
1412
        $form->colon();
1413
1414
        $form->setLayout(
1415
            $W->VBoxItems(
1416
                $activeFiltersFrame
1417
                    ->setSizePolicy('row'),
1418
                $buttonsBox = $W->FlowItems(
1419
                    $advancedFiltersFrame,
1420
                    $this->submit,
1421
                    $this->reset
1422
                )->setHorizontalSpacing(2, 'em')
1423
                ->setSizePolicy('row')
1424
            )->setVerticalSpacing(1, 'em')
1425
            ->setVerticalAlign('bottom')
1426
        );
1427
1428
        $form->setHiddenValue('tg', $App->controllerTg);
1429
        $form->setAnchor($this->getAnchor());
1430
1431
1432
1433
        if ($this->isColumnSelectionAllowed()) {
1434
            $columnSelectionMenu = $this->columnSelectionMenu($this->columns);
1435
            $buttonsBox->addItem($columnSelectionMenu);
1436
        }
1437
1438
        return $form;
1439
    }
1440
1441
1442
    /**
1443
     * Get a advanced filter panel
1444
     * Use setPageLength to define the default number of items per page
1445
     *
1446
     * @param	array	$filter		Filter values
1447
     * @param	string	$name		filter form name
1448
     * @param	string	$anchor		anchor in destination page of filter, the can be a Widget_Tab id
1449
     *
1450
     * @return Widget_Filter
1451
     */
1452
    public function advancedFilterPanel($filter = null, $name = null)
1453
    {
1454
        $W = bab_Widgets();
1455
1456
        $filterPanel = $W->Filter();
1457
        $filterPanel->setLayout($W->VBoxLayout());
1458
        if (isset($name)) {
1459
            $filterPanel->setName($name);
1460
        }
1461
1462
        $pageLength = $this->getPageLength();
1463
        if (null === $pageLength) {
0 ignored issues
show
introduced by
The condition null === $pageLength is always false.
Loading history...
1464
            $pageLength = 15;
1465
        }
1466
1467
        $pageNumber = $this->getCurrentPage();
1468
        if (null === $pageNumber) {
0 ignored issues
show
introduced by
The condition null === $pageNumber is always false.
Loading history...
1469
            $pageNumber = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $pageNumber is dead and can be removed.
Loading history...
1470
        }
1471
1472
        $this->setPageLength(isset($filter['pageSize']) ? $filter['pageSize'] : $pageLength);
1473
//        $this->setCurrentPage(isset($filter['pageNumber']) ? $filter['pageNumber'] : $pageNumber);
1474
1475
        if (isset($name)) {
1476
            $this->sortParameterName = $name . '[filter][sort]';
1477
        } else {
1478
            $this->sortParameterName = 'filter[sort]';
1479
        }
1480
1481
        if (isset($filter['sort'])) {
1482
            $this->setSortField($filter['sort']);
1483
        } elseif (!isset($this->sortField)) {
1484
1485
            if (method_exists($this, 'getDefaultSortField'))
1486
            {
1487
                $this->setSortField($this->getDefaultSortField());
1488
            } else {
1489
                $columns = $this->getVisibleColumns();
1490
                $sortField = key($columns);
1491
                $this->setSortField($sortField);
1492
            }
1493
        }
1494
1495
        $form = $this->getAdvancedFilterForm(null, $filter);
1496
1497
        if (isset($filter)) {
1498
            if (isset($name)) {
1499
                $path = array($name, 'filter');
1500
            } else {
1501
                $path = array('filter');
1502
            }
1503
            $form->setValues($filter, $path);
1504
        }
1505
1506
        $filterPanel->setFilter($form);
1507
        $filterPanel->setFiltered($this);
1508
1509
        return $filterPanel;
1510
    }
1511
}
1512
1513
/**
1514
 * Creates a specific filter panel containing the table and an auto-generated form.
1515
 *
1516
 * @param Func_App                     $App
1517
 * @param app_TableModelView           $tableview
1518
 * @param Widget_Displayable_Interface $formContent
1519
 * @param array                        $filter
1520
 * @param string                       $name
1521
 * @return Widget_Filter
1522
 */
1523
function app_filteredTableView(Func_App $App, app_TableModelView $tableview, Widget_Displayable_Interface $formContent, $filterValues = null, $name = null)
1524
{
1525
    $W = bab_Widgets();
1526
1527
    $filterPanel = $W->Filter();
1528
    $filterPanel->setLayout($W->VBoxLayout());
1529
    if (isset($name)) {
1530
        $filterPanel->setName($name);
1531
    }
1532
1533
    if (isset($filterValues['pageSize'])) {
1534
        $tableview->setPageLength($filterValues['pageSize']);
1535
    } else if ($tableview->getPageLength() === null) {
0 ignored issues
show
introduced by
The condition $tableview->getPageLength() === null is always false.
Loading history...
1536
        $tableview->setPageLength(12);
1537
    }
1538
1539
    $tableview->setCurrentPage(isset($filterValues['pageNumber']) ? $filterValues['pageNumber'] : 0);
1540
1541
    $tableview->sortParameterName = $name . '[filter][sort]';
1542
1543
    if (isset($filterValues['sort'])) {
1544
        $tableview->setSortField($filterValues['sort']);
1545
    } else {
1546
        $columns = $tableview->getVisibleColumns();
1547
        foreach ($columns as $sort => $column) {
1548
            if ($sortField = $column->getField()) {
0 ignored issues
show
Unused Code introduced by
The assignment to $sortField is dead and can be removed.
Loading history...
1549
                $tableview->setSortField($sort);
1550
                break;
1551
            }
1552
        }
1553
    }
1554
1555
1556
    if ($formContent instanceof Widget_Form) {
1557
        $form = $formContent;
1558
    } else {
1559
        $form = $W->Form()->setLayout($W->VBoxLayout()->setVerticalSpacing(1, 'em'));
1560
        $form->setReadOnly(true);
1561
        $form->setName('filter');
1562
        $form->colon();
1563
        $form->addItem($formContent);
1564
1565
        $form->addItem($W->SubmitButton()->setLabel($App->translate('Filter')));
1566
    }
1567
1568
1569
    if (isset($filterValues) && is_array($filterValues)) {
1570
        $form->setValues($filterValues, array($name, 'filter'));
1571
    }
1572
    $form->setHiddenValue('tg', bab_rp('tg'));
1573
    $form->setHiddenValue('idx', bab_rp('idx'));
1574
1575
    $filterPanel->setFilter($form);
1576
    $filterPanel->setFiltered($tableview);
1577
1578
    return $filterPanel;
1579
}
1580
1581
1582
1583
/**
1584
 * @param string|ORM_Field $field
1585
 * @param string|null $description
1586
 *
1587
 * @return widget_TableModelViewColumn
1588
 */
1589
function app_TableModelViewColumn($field, $description = null)
1590
{
1591
    if (null === $field) {
0 ignored issues
show
introduced by
The condition null === $field is always false.
Loading history...
1592
        return null;
1593
    }
1594
1595
    if (!isset($description) && $field instanceof ORM_Field) {
1596
        $App = $field->getParentSet()->App();
1597
        $description = $App->translate($field->getDescription());
0 ignored issues
show
Bug introduced by
The method translate() does not exist on ORM_Field. It seems like you code against a sub-type of ORM_Field such as ORM_RecordSet. ( Ignorable by Annotation )

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

1597
        /** @scrutinizer ignore-call */ 
1598
        $description = $App->translate($field->getDescription());
Loading history...
1598
    }
1599
1600
    return new widget_TableModelViewColumn($field, $description);
1601
}
1602
1603
1604
class app_RecordView extends app_UiObject
1605
{
1606
    /**
1607
     * @var app_Record
1608
     */
1609
    protected $record = null;
1610
1611
    /**
1612
     * @var app_RecordSet
1613
     */
1614
    protected $recordSet = null;
1615
1616
    /**
1617
     * @var Widget_Section[]
1618
     */
1619
    protected $sections = array();
1620
1621
    protected $view = '';
1622
1623
    /**
1624
     * @var app_CtrlRecord
1625
     */
1626
    protected $recordController = null;
1627
1628
    /**
1629
     * @param Func_App $App
1630
     * @param string $id
1631
     * @param Widget_Layout $layout
1632
     */
1633
    public function __construct(Func_App $App, $id = null, Widget_Layout $layout = null)
1634
    {
1635
        parent::__construct($App);
1636
1637
        if (!isset($layout)) {
1638
            $W = bab_Widgets();
1639
            $layout = $W->Items();
1640
        }
1641
1642
        $this->setInheritedItem($layout);
1643
    }
1644
1645
1646
    /**
1647
     * @param app_Record $record
1648
     * @return self
1649
     */
1650
    public function setRecord(app_Record $record)
1651
    {
1652
        $this->record = $record;
1653
        $this->recordSet = $record->getParentSet();
1654
1655
1656
        return $this;
1657
    }
1658
1659
1660
    /**
1661
     * @param string $view
1662
     * @return self
1663
     */
1664
    public function setView($view)
1665
    {
1666
        $this->view = $view;
1667
        return $this;
1668
    }
1669
1670
1671
    /**
1672
     * @return string
1673
     */
1674
    public function getView()
1675
    {
1676
        return $this->view;
1677
    }
1678
1679
1680
1681
    /**
1682
     * Creates section in the editor.
1683
     * If a section with the same header text (title) was already
1684
     * created it is replaced by an empty section.
1685
     *
1686
     * @param string $headerText
1687
     * @return Widget_Section
1688
     */
1689
    protected function addSection($id, $headerText, $layout = null)
1690
    {
1691
        $W = bab_Widgets();
1692
        if (!isset($layout)) {
1693
            $layout = $W->VBoxLayout()->setVerticalSpacing(1, 'em');
1694
        }
1695
        $this->sections[$id] = $W->Section(
1696
            $headerText,
1697
            $layout
1698
            )->setFoldable(true);
1699
1700
            return $this->sections[$id];
1701
    }
1702
1703
    /**
1704
     * Creates section in the editor.
1705
     * If a section with the same header text (title) was already
1706
     * created it is replaced by an empty section.
1707
     *
1708
     * @param string $headerText
1709
     * @return Widget_Section
1710
     */
1711
    protected function createSection($headerText, $layout = null)
1712
    {
1713
        return $this->addSection($headerText, $headerText, $layout);
1714
    }
1715
1716
1717
    /**
1718
     * Retrieves a section in the editor.
1719
     *
1720
     * @param string $headerText
1721
     * @return Widget_Section
1722
     */
1723
    protected function getSection($id)
1724
    {
1725
        if (!isset($this->sections[$id])) {
1726
            return null;
1727
        }
1728
        return $this->sections[$id];
1729
    }
1730
1731
1732
    /**
1733
     * Returns the list of sections created via createSection().
1734
     *
1735
     * @return Widget_Section[]
1736
     */
1737
    public function getSections()
1738
    {
1739
        return $this->sections;
1740
    }
1741
1742
1743
1744
1745
    /*
1746
     * @param string $textLabel
1747
     * @param mixed $value
1748
     * @return Widget_Layout
1749
     */
1750
    function labelledWidget($textLabel, $value, app_CustomSection $section = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1751
    {
1752
        $W = bab_Widgets();
1753
1754
        if ($value instanceof Widget_Displayable_Interface) {
1755
            $widget = $value;
1756
        } else {
1757
            $widget = $W->Label($value);
1758
        }
1759
1760
        if (isset($section)) {
1761
            if ($textLabel === '__') {
1762
                $fieldLayout = app_CustomSection::FIELDS_LAYOUT_NO_LABEL;
1763
            } else {
1764
                $fieldLayout = $section->fieldsLayout;
1765
            }
1766
            switch ($fieldLayout) {
1767
                case app_CustomSection::FIELDS_LAYOUT_HORIZONTAL_LABEL:
1768
                    return $W->FlowItems(
1769
                    $W->Label($textLabel)->addClass('crm-horizontal-display-label', 'widget-strong')
1770
                    ->setSizePolicy('widget-25pc'),
1771
                    $widget
1772
                    )->setHorizontalSpacing(1, 'ex')
1773
                    ->setVerticalAlign('top');
1774
1775
                case app_CustomSection::FIELDS_LAYOUT_WIDE_HORIZONTAL_LABEL:
1776
                    return $W->FlowItems(
1777
                    $W->Label($textLabel)->addClass('crm-horizontal-display-label', 'widget-strong')
1778
                    ->setSizePolicy('widget-50pc'),
1779
                    $widget
1780
                    )->setHorizontalSpacing(1, 'ex')
1781
                    ->setVerticalAlign('top');
1782
1783
                case app_CustomSection::FIELDS_LAYOUT_NO_LABEL:
1784
                    return $widget;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $widget returns the type Widget_Label which is incompatible with the documented return type Widget_Layout.
Loading history...
1785
1786
                case app_CustomSection::FIELDS_LAYOUT_VERTICAL_LABEL:
1787
                default:
1788
                    return $W->LabelledWidget($textLabel, $widget);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $W->LabelledWidget($textLabel, $widget) returns the type Widget_LabelledWidget which is incompatible with the documented return type Widget_Layout.
Loading history...
1789
            }
1790
        }
1791
    }
1792
1793
1794
    function labelledWidgetOptional($textLabel, $displayedValue, $value = null, app_CustomSection $section = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1795
    {
1796
        if (!isset($value)) {
1797
            $value = $displayedValue;
1798
        }
1799
        if (!isset($value) || is_numeric($value) && $value == 0 || is_string($value) && trim($value) == '' || ($value instanceof Widget_Layout && count($value->getItems()) <= 0)) {
1800
            return null;
1801
        }
1802
        return $this->labelledWidget($textLabel, $displayedValue, $section);
1803
    }
1804
1805
1806
1807
    protected function fieldOutput($field, $record, $fieldName)
1808
    {
1809
        if ($field instanceof ORM_CurrencyField) {
1810
            $App = $this->App();
1811
            $value = $App->shortFormatWithUnit($this->record->$fieldName, $App->translate('_euro_'));
1812
        } else {
1813
            $value = $field->output($this->record->$fieldName);
1814
        }
1815
        return $value;
1816
    }
1817
1818
    protected function addSections($view = '')
1819
    {
1820
        $App = $this->App();
1821
        $W = bab_Widgets();
1822
1823
        $objectName = $this->record->getClassName();
1824
1825
        $customSectionSet = $App->CustomSectionSet();
1826
        $customSections = $customSectionSet->select(
1827
            $customSectionSet->object->is($objectName)->_AND_($customSectionSet->view->is($view))
1828
            );
1829
        $customSections->orderAsc($customSectionSet->rank);
1830
1831
        $currentColumn = 0;
1832
        $nbCol = 0;
1833
        $row = $W->Items()->setSizePolicy('row');
1834
        foreach ($customSections as $customSection) {
1835
1836
            if (isset($this->record) && !$customSection->isVisibleForRecord($this->record)) {
1837
                continue;
1838
            }
1839
1840
            list(, , $nbCol) = explode('-', $customSection->sizePolicy);
1841
1842
            if ($currentColumn + $nbCol > 12) {
1843
                $this->addItem($row);
0 ignored issues
show
Bug introduced by
The method addItem() does not exist on app_RecordView. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

1843
                $this->/** @scrutinizer ignore-call */ 
1844
                       addItem($row);
Loading history...
1844
                $row = $W->Items()->setSizePolicy('row');
1845
                $currentColumn = 0;
1846
            }
1847
            $currentColumn += $nbCol;
1848
1849
            $section = $this->getSection($customSection->id);
1850
            if (!isset($section)) {
1851
                $section = $this->addSection($customSection->id, $customSection->name);
1852
                $section->addClass($customSection->classname);
1853
                $section->setSizePolicy($customSection->sizePolicy);
1854
1855
                if ($customSection->editable) {
0 ignored issues
show
Bug Best Practice introduced by
The property editable does not exist on app_CustomSection. Since you implemented __get, consider adding a @property annotation.
Loading history...
1856
                    $menu = $section->addContextMenu('inline');
1857
                    $menu->addClass(Func_Icons::ICON_LEFT_16);
1858
                    $menu->addItem(
1859
                        $W->Link(
1860
                            '',
1861
                            $this->record->getController()->editSection($this->record->id, $customSection->id)
0 ignored issues
show
introduced by
The method editSection() does not exist on app_Controller. Maybe you want to declare this class abstract? ( Ignorable by Annotation )

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

1861
                            $this->record->getController()->/** @scrutinizer ignore-call */ editSection($this->record->id, $customSection->id)
Loading history...
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
1862
                        )->addClass('widget-actionbutton', 'section-button', 'icon', Func_Icons::ACTIONS_DOCUMENT_EDIT)
1863
                        ->setOpenMode(Widget_Link::OPEN_DIALOG)
1864
                    );
1865
                }
1866
1867
                $section->setFoldable($customSection->foldable, $customSection->folded);
1868
                $row->addItem($section);
1869
            }
1870
1871
1872
1873
            $displayFields = $customSection->getFields();
1874
1875
            foreach ($displayFields as $displayField) {
1876
                $item = null;
1877
                $displayFieldName = $displayField['fieldname'];
1878
                $parameters = $displayField['parameters'];
1879
                $classname = isset($parameters['classname']) ? $parameters['classname'] : '';
1880
                $label = isset($parameters['label']) ? $parameters['label'] : null;
1881
1882
                $displayFieldMethod = '_' . $displayFieldName;
1883
                if (method_exists($this, $displayFieldMethod)) {
1884
                    $item = $this->$displayFieldMethod($customSection, $label);
1885
                } else {
1886
                    try {
1887
                        $field = $this->recordSet->getField($displayFieldName);
1888
                        $value = $this->fieldOutput($field, $this->record, $displayFieldName);
1889
                        if (!isset($label) || empty($label)) {
1890
                            $label = $App->translate($field->getDescription());
1891
                        }
1892
1893
                        //$value = $field->output($this->record->$displayFieldName);
1894
                        $item = $this->labelledWidgetOptional(
1895
                            $label,
1896
                            $W->Label($value),
1897
                            $value,
1898
                            $customSection
1899
                            );
1900
                    } catch (ORM_Exception $e) {
1901
                        $item = null;
1902
                    }
1903
                }
1904
                if (isset($item)) {
1905
                    $item->addClass($classname);
1906
                    $section->addItem($item);
1907
                }
1908
            }
1909
        }
1910
1911
        if ($currentColumn + $nbCol> 0) {
1912
            $this->addItem($row);
1913
        }
1914
    }
1915
}
1916
1917
1918
1919
1920
/**
1921
 * A card frame is a frame with contact informations
1922
 *
1923
 */
1924
class app_CardFrame extends app_UiObject // extends Widget_Frame
1925
{
1926
1927
    const IMAGE_WIDTH = 40;
1928
    const IMAGE_HEIGHT = 40;
1929
1930
1931
    const MAPS_URL = '//maps.google.com/?q=%s';
1932
1933
    const MAPS_ROUTE_URL = '//maps.google.com/maps?saddr=%s&daddr=%s';
1934
1935
    const STATIC_MAPS_URL = '//maps.google.com/maps/api/staticmap?';
1936
1937
    /**
1938
     * @var Widget_Section[] $sections
1939
     */
1940
    protected $sections = array();
1941
1942
1943
    protected $menu = null;
1944
1945
1946
1947
1948
1949
    public function __construct(Func_App $App, $id = null, Widget_Layout $layout = null)
1950
    {
1951
        parent::__construct($App);
1952
1953
        $W = bab_Widgets();
1954
1955
        $this->setInheritedItem($W->Frame($id, $layout));
1956
    }
1957
1958
1959
    /**
1960
     * render a labeled string
1961
     *
1962
     * @param	string | Widget_Item	$label
1963
     * @param	string | Widget_Item	$value
1964
     *
1965
     * @return Widget_Item
1966
     */
1967
    protected function labelStr($label, $value)
1968
    {
1969
        $W = bab_Widgets();
1970
1971
        if (!($label instanceOf Widget_Item)) {
1972
            $label = $W->Label($label);
1973
        }
1974
1975
        if (!($value instanceOf Widget_Displayable_Interface)) {
1976
            $value = $W->Label($value);
1977
        }
1978
1979
1980
        return $W->VBoxItems(
1981
            $label->colon(false)->addClass('crm-display-label'),
0 ignored issues
show
Bug introduced by
The method colon() does not exist on Widget_Item. It seems like you code against a sub-type of Widget_Item such as Widget_Title or Widget_SimpleTreeView or Widget_LabelledWidget or Widget_Label or Widget_InputPassword or Widget_Section or Widget_Form. ( Ignorable by Annotation )

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

1981
            $label->/** @scrutinizer ignore-call */ 
1982
                    colon(false)->addClass('crm-display-label'),
Loading history...
1982
            $value->addClass('crm-display-value')
1983
            )->setVerticalAlign('middle')->setVerticalSpacing(3, 'px');
1984
    }
1985
1986
    /**
1987
     * render a labeled string only if value set
1988
     *
1989
     * @param	string | Widget_Item	$label
1990
     * @param	string | Widget_Item	$value
1991
     *
1992
     * @return Widget_Item | null
1993
     */
1994
    protected function labelStrSet($label, $value)
1995
    {
1996
        if (!isset($value) || '' === $value) {
1997
            return null;
1998
        }
1999
2000
        return $this->labelStr($label, $value);
2001
    }
2002
2003
2004
    /**
2005
     * render a labeled string with a field value, if the value is not set, return null
2006
     *
2007
     *
2008
     * @param string $label
2009
     * @param app_Record $record
2010
     * @param ORM_Field $field
2011
     */
2012
    protected function labeledField($label, app_Record $record, ORM_Field $field)
2013
    {
2014
        $W = bab_Widgets();
2015
2016
        $fieldName = $field->getName();
2017
        $value = $record->$fieldName;
2018
2019
        if (!$field->isValueSet($value)) {
2020
            return null;
2021
        }
2022
2023
        $displayable = $field->output($value);
2024
2025
        switch(true) {
2026
            case ($field instanceof ORM_TextField):
2027
                $displayable = $W->RichText($displayable)->setRenderingOptions(BAB_HTML_ALL ^ BAB_HTML_P);
2028
                break;
2029
2030
            case ($field instanceof ORM_UrlField):
2031
                $displayable = $W->Link($displayable, $displayable);
2032
                break;
2033
2034
            case ($field instanceof ORM_EmailField):
2035
                $displayable = $W->Link($displayable, 'mailto:'.$displayable);
2036
                break;
2037
2038
            case ($field instanceof ORM_FkField):
2039
                $record = $record->$fieldName();
2040
                if (!isset($record)) {
2041
                    return null;
2042
                }
2043
                $displayable = $record->getRecordTitle();
2044
                break;
2045
        }
2046
2047
        return $this->labelStr($label, $displayable);
2048
    }
2049
2050
2051
2052
2053
2054
    /**
2055
     * Creates section in the editor.
2056
     * If a section with the same header text (title) was already
2057
     * created it is replaced by an empty section.
2058
     *
2059
     * @param string $headerText
2060
     * @return Widget_Section
2061
     */
2062
    protected function addSection($id, $headerText, $layout = null)
2063
    {
2064
        $W = bab_Widgets();
2065
        if (!isset($layout)) {
2066
            $layout = $W->VBoxLayout()->setVerticalSpacing(1, 'em');
2067
        }
2068
        $this->sections[$id] = $W->Section(
2069
            $headerText,
2070
            $layout
2071
            )->setFoldable(true);
2072
2073
            return $this->sections[$id];
2074
    }
2075
2076
    /**
2077
     * Creates section in the editor.
2078
     * If a section with the same header text (title) was already
2079
     * created it is replaced by an empty section.
2080
     *
2081
     * @param string $headerText
2082
     * @return Widget_Section
2083
     */
2084
    protected function createSection($headerText, $layout = null)
2085
    {
2086
        return $this->addSection($headerText, $headerText, $layout);
2087
    }
2088
2089
2090
    /**
2091
     * Retrieves a section in the editor.
2092
     *
2093
     * @param string $headerText
2094
     * @return Widget_Section
2095
     */
2096
    protected function getSection($id)
2097
    {
2098
        if (!isset($this->sections[$id])) {
2099
            return null;
2100
        }
2101
        return $this->sections[$id];
2102
    }
2103
2104
2105
    /**
2106
     * Returns the list of sections created via createSection().
2107
     *
2108
     * @return Widget_Section[]
2109
     */
2110
    public function getSections()
2111
    {
2112
        return $this->sections;
2113
    }
2114
2115
2116
    /**
2117
     * @param Widget_Displayable_Interface $item
2118
     * @return self
2119
     */
2120
    public function addAction(Widget_Displayable_Interface $item)
2121
    {
2122
        if (!isset($this->menu)) {
2123
            $W = bab_Widgets();
2124
            $this->menu = $W->Menu()
2125
                ->setLayout($W->FlowLayout())
2126
                ->addClass(Func_Icons::ICON_LEFT_SYMBOLIC);
2127
2128
        }
2129
        $this->menu->addItem($item);
2130
        return $this;
2131
    }
2132
}
2133
2134