Completed
Push — master ( f3b14d...1fe4c3 )
by Damian
10s
created

WidgetAreaEditor::AvailableWidgets()   D

Complexity

Conditions 9
Paths 13

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 4.909
c 0
b 0
f 0
cc 9
eloc 16
nc 13
nop 0
1
<?php
2
3
namespace SilverStripe\Widgets\Forms;
4
5
use Exception;
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Forms\FormField;
10
use SilverStripe\ORM\ArrayList;
11
use SilverStripe\ORM\DataObjectInterface;
12
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
13
use SilverStripe\Widgets\Model\Widget;
14
use SilverStripe\View\Requirements;
15
16
/**
17
 * Special field type for selecting and configuring widgets on a page.
18
 *
19
 * @package widgets
20
 */
21
class WidgetAreaEditor extends FormField
22
{
23
    /**
24
     * @param string $name
25
     * @param array $widgetClasses
26
     * @param int $maxWidgets
27
     */
28
    public function __construct($name, $widgetClasses = array(Widget::class), $maxWidgets = 0)
29
    {
30
        $this->MaxWidgets = $maxWidgets;
0 ignored issues
show
Documentation introduced by
The property MaxWidgets does not exist on object<SilverStripe\Widg...Forms\WidgetAreaEditor>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
31
        $this->widgetClasses = $widgetClasses;
0 ignored issues
show
Documentation introduced by
The property widgetClasses does not exist on object<SilverStripe\Widg...Forms\WidgetAreaEditor>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
32
33
        parent::__construct($name);
34
    }
35
36
    /**
37
     * @param array $properties
38
     *
39
     * @return string - HTML
40
     */
41
    public function FieldHolder($properties = array())
42
    {
43
        Requirements::css('widgets/css/WidgetAreaEditor.css');
44
        Requirements::javascript('widgets/javascript/WidgetAreaEditor.js');
45
46
        return $this->renderWith(WidgetAreaEditor::class);
47
    }
48
49
    /**
50
     *
51
     * @return ArrayList
52
     */
53
    public function AvailableWidgets()
54
    {
55
        $widgets= new ArrayList();
56
57
        foreach ($this->widgetClasses as $widgetClass) {
0 ignored issues
show
Documentation introduced by
The property widgetClasses does not exist on object<SilverStripe\Widg...Forms\WidgetAreaEditor>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
58
            $classes = ClassInfo::subclassesFor($widgetClass);
59
60
            if (isset($classes[Widget::class])) {
61
                unset($classes[Widget::class]);
62
            } elseif (isset($classes[0]) && $classes[0] == Widget::class) {
63
                unset($classes[0]);
64
            }
65
66
            foreach ($classes as $class) {
67
                $available = Config::inst()->get($class, 'only_available_in');
68
69
                if (!empty($available) && is_array($available)) {
70
                    if (in_array($this->Name, $available)) {
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<SilverStripe\Widg...Forms\WidgetAreaEditor>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
71
                        $widgets->push(singleton($class));
72
                    }
73
                } else {
74
                    $widgets->push(singleton($class));
75
                }
76
            }
77
        }
78
79
        return $widgets;
80
    }
81
82
    /**
83
     * @return HasManyList
84
     */
85
    public function UsedWidgets()
86
    {
87
        // Call class_exists() to load Widget.php earlier and avoid a segfault
88
        class_exists(Widget::class);
89
90
        $relationName = $this->name;
91
        $widgets = $this->form->getRecord()->getComponent($relationName)->Items();
92
93
        return $widgets;
94
    }
95
96
    /**
97
     * @return string
98
     */
99
    public function IdxField()
100
    {
101
        return $this->id() . 'ID';
102
    }
103
104
    /**
105
     *
106
     * @return int
107
     */
108
    public function Value()
109
    {
110
        $relationName = $this->name;
111
112
        return $this->form->getRecord()->getComponent($relationName)->ID;
113
    }
114
115
    /**
116
     * @param DataObjectInterface $record
117
     * @throws Exception if no form could be retrieved
118
     */
119
    public function saveInto(DataObjectInterface $record)
120
    {
121
        $name = $this->name;
122
        $idName = $name . "ID";
123
124
        $widgetarea = $record->getComponent($name);
125
        $widgetarea->write();
126
127
        $record->$idName = $widgetarea->ID;
128
129
        $widgets = $widgetarea->Items();
130
131
        // store the field IDs and delete the missing fields
132
        // alternatively, we could delete all the fields and re add them
133
        $missingWidgets = array();
134
135
        if ($widgets) {
136
            foreach ($widgets as $existingWidget) {
137
                $missingWidgets[$existingWidget->ID] = $existingWidget;
138
            }
139
        }
140
141
        if (!$this->getForm()) {
142
            throw new Exception("no form");
143
        }
144
145
        $widgetData = $this->getForm()->getRequest()->requestVar('Widget');
146
        if ($widgetData && isset($widgetData[$this->getName()])) {
147
            $widgetAreaData = $widgetData[$this->getName()];
148
149
            foreach ($widgetAreaData as $newWidgetID => $newWidgetData) {
150
                // Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
151
                if (!is_numeric($newWidgetID)) {
152
                    $newWidgetID = 0;
153
                }
154
155
                $widget = null;
156
                if ($newWidgetID) {
157
                    // \"ParentID\" = '0' is for the new page
158
                    $widget = Widget::get()
159
                        ->filter('ParentID', array(0, $record->$name()->ID))
160
                        ->byID($newWidgetID);
161
162
                    // check if we are updating an existing widget
163
                    if ($widget && isset($missingWidgets[$widget->ID])) {
164
                        unset($missingWidgets[$widget->ID]);
165
                    }
166
                }
167
168
                // create a new object
169
                if (!$widget
170
                    && !empty($newWidgetData['Type'])
171
                    && class_exists($newWidgetData['Type'])
172
                    && is_subclass_of($newWidgetData['Type'], Widget::class)
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \SilverStripe\Widgets\Model\Widget::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
173
                ) {
174
                    $widget = Injector::inst()->create($newWidgetData['Type']);
175
                    $widget->ID = 0;
176
                    $widget->ParentID = $record->$name()->ID;
177
                }
178
179
                if ($widget) {
180
                    if ($widget->ParentID == 0) {
181
                        $widget->ParentID = $record->$name()->ID;
182
                    }
183
                    $widget->populateFromPostData($newWidgetData);
184
                }
185
            }
186
        }
187
188
        // remove the fields not saved
189
        if ($missingWidgets) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $missingWidgets of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
190
            foreach ($missingWidgets as $removedWidget) {
191
                if (isset($removedWidget) && is_numeric($removedWidget->ID)) {
192
                    $removedWidget->delete();
193
                }
194
            }
195
        }
196
    }
197
}
198