Passed
Push — master ( a1de92...1a465b )
by Robbie
03:52
created

ElementalAreaField   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 223
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 223
rs 10
c 0
b 0
f 0
wmc 22

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 2
A setTypes() 0 5 1
A getArea() 0 3 1
A getTypes() 0 7 1
A FieldHolder() 0 9 2
A saveInto() 0 28 4
A setSubmittedValue() 0 4 1
A getReadOnlyBlockReducer() 0 37 4
A getSchemaDataDefaults() 0 13 4
A performReadonlyTransformation() 0 25 2
1
<?php
2
3
namespace DNADesign\Elemental\Forms;
4
5
use BlocksPage;
0 ignored issues
show
Bug introduced by Guy Marriott
The type BlocksPage was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
6
use DNADesign\Elemental\Controllers\ElementalAreaController;
7
use DNADesign\Elemental\Models\BaseElement;
8
use DNADesign\Elemental\Models\ElementalArea;
9
use DNADesign\Elemental\Services\ElementTabProvider;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Core\Config\Config;
12
use SilverStripe\Core\Injector\Injector;
13
use SilverStripe\Forms\CompositeField;
14
use SilverStripe\Forms\FieldGroup;
15
use SilverStripe\Forms\FieldList;
16
use SilverStripe\Forms\FormField;
17
use SilverStripe\Forms\GridField\GridField;
18
use SilverStripe\Forms\TabSet;
19
use SilverStripe\ORM\DataObjectInterface;
20
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
21
22
class ElementalAreaField extends GridField
23
{
24
    /**
25
     * @var ElementalArea $area
26
     */
27
    protected $area;
28
29
    /**
30
     * @var array $type
31
     */
32
    protected $types = [];
33
34
    /**
35
     * @var null
36
     */
37
    protected $inputType = null;
38
39
    protected $modelClassName = BaseElement::class;
40
41
    /**
42
     * @param string $name
43
     * @param ElementalArea $area
44
     * @param string[] $blockTypes
45
     */
46
    public function __construct($name, ElementalArea $area, array $blockTypes)
47
    {
48
        $this->setTypes($blockTypes);
49
50
        $config = new ElementalAreaConfig();
51
52
        if (!empty($blockTypes)) {
53
            /** @var GridFieldAddNewMultiClass $adder */
54
            $adder = Injector::inst()->create(GridFieldAddNewMultiClass::class);
55
            $adder->setClasses($blockTypes);
56
            $config->addComponent($adder);
57
        }
58
59
        // By default, no need for a title on the editor. If there is more than one area then use `setTitle` to describe
60
        parent::__construct($name, '', $area->Elements(), $config);
0 ignored issues
show
Bug introduced by Guy Marriott
It seems like $area->Elements() can also be of type DNADesign\Elemental\Models\BaseElement[]; however, parameter $dataList of SilverStripe\Forms\GridF...ridField::__construct() does only seem to accept SilverStripe\ORM\SS_List|null, maybe add an additional type check? ( Ignorable by Annotation )

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

60
        parent::__construct($name, '', /** @scrutinizer ignore-type */ $area->Elements(), $config);
Loading history...
61
        $this->area = $area;
62
63
        $this->addExtraClass('element-editor__container no-change-track');
64
    }
65
66
    /**
67
     * @param array $types
68
     *
69
     * @return $this
70
     */
71
    public function setTypes($types)
72
    {
73
        $this->types = $types;
74
75
        return $this;
76
    }
77
78
    /**
79
     * @return array
80
     */
81
    public function getTypes()
82
    {
83
        $types = $this->types;
84
85
        $this->extend('updateGetTypes', $types);
86
87
        return $types;
88
    }
89
90
    /**
91
     * @return ElementalArea
92
     */
93
    public function getArea()
94
    {
95
        return $this->area;
96
    }
97
98
    /**
99
     * Overloaded to skip GridField implementation - this is copied from FormField.
100
     *
101
     * @param array $properties
102
     * @return \SilverStripe\ORM\FieldType\DBHTMLText|string
103
     */
104
    public function FieldHolder($properties = array())
105
    {
106
        $context = $this;
107
108
        if (count($properties)) {
109
            $context = $this->customise($properties);
110
        }
111
112
        return $context->renderWith($this->getFieldHolderTemplates());
113
    }
114
115
    public function getSchemaDataDefaults()
116
    {
117
        $schemaData = parent::getSchemaDataDefaults();
118
119
        $area = $this->getArea();
120
        $pageId = ($area && ($page = $area->getOwnerPage())) ? $page->ID : null;
121
        $schemaData['page-id'] = $pageId;
122
        $schemaData['elemental-area-id'] = $area ? (int) $area->ID : null;
0 ignored issues
show
introduced by Dylan Wagstaff
$area is of type DNADesign\Elemental\Models\ElementalArea, thus it always evaluated to true.
Loading history...
123
124
        $allowedTypes = $this->getTypes();
125
        $schemaData['allowed-elements'] = array_keys($allowedTypes);
126
127
        return $schemaData;
128
    }
129
130
    /**
131
     * A getter method that seems redundant in that it is a function that returns a function,
132
     * however the returned closure is used in an array map function to return a complete FieldList
133
     * representing a read only view of the element passed in (to the closure).
134
     *
135
     * @return callable
136
     */
137
    protected function getReadOnlyBlockReducer()
138
    {
139
        return function (BaseElement $element) {
140
            $parentName = 'Element' . $element->ID;
141
            $elementFields = $element->getCMSFields();
142
143
            // Obtain highest impact fields for a summary (e.g. Title & Content)
144
            foreach ($elementFields as $field) {
145
                if (is_object($field) && $field instanceof TabSet) {
146
                    // Assign the fields of the first Tab in the TabSet - most regularly 'Root.Main'
147
                    $elementFields = $field->FieldList()->first()->FieldList();
148
                    break;
149
                }
150
            }
151
152
            // Set values (before names don't match anymore)
153
            $elementFields->setValues($element->getQueriedDatabaseFields());
154
155
            // Combine into an appropriately named group
156
            $elementGroup = FieldGroup::create($elementFields);
157
            $elementGroup->setForm($this->getForm());
158
            $elementGroup->setName($parentName);
159
            $elementGroup->addExtraClass('elemental-area__element--historic');
160
161
            // Also set the important data for the rendering Component
162
            $elementGroup->setSchemaData([
163
                'data' => [
164
                    'ElementID' => $element->ID,
165
                    'ElementType' => $element->getType(),
166
                    'ElementIcon' => $element->config()->icon,
167
                    'ElementTitle' => $element->Title,
168
                    // @todo: Change this to block history permalink when that functionality becomes available.
169
                    'ElementEditLink' => $element->CMSEditLink()
170
                ]
171
            ]);
172
173
            return $elementGroup;
174
        };
175
    }
176
177
    /**
178
     * Provides a readonly representation of the GridField (superclass) Uses a reducer
179
     * {@see ElementalAreaField::getReadOnlyBlockReducer()} to fetch a read only representation of the listed class
180
     * {@see GridField::getModelClass()}
181
     *
182
     * @return CompositeField
183
     */
184
    public function performReadonlyTransformation()
185
    {
186
        /** @var CompositeField $readOnlyField */
187
        $readOnlyField = $this->castedCopy(CompositeField::class);
188
        $blockReducer = $this->getReadOnlyBlockReducer();
189
        $readOnlyField->setChildren(
190
            FieldList::create(array_map($blockReducer, $this->getArea()->Elements()->toArray()))
191
        );
192
193
        $readOnlyField = $readOnlyField->performReadonlyTransformation();
194
195
        // Ensure field names are unique between elements on parent form but only after transformations have been
196
        // performed
197
        /** @var FieldGroup $elementForm */
198
        foreach ($readOnlyField->getChildren() as $elementForm) {
199
            $parentName = $elementForm->getName();
200
            $elementForm->getChildren()->recursiveWalk(function (FormField $field) use ($parentName) {
201
                $field->setName($parentName . '_' . $field->getName());
202
            });
203
        }
204
205
        return $readOnlyField
206
            ->setReadOnly(true)
207
            ->setName($this->getName())
208
            ->addExtraClass('elemental-area--read-only');
209
    }
210
211
    public function setSubmittedValue($value, $data = null)
212
    {
213
        // Content comes through as a JSON encoded list through a hidden field.
214
        return $this->setValue(json_decode($value, true));
215
    }
216
217
    public function saveInto(DataObjectInterface $dataObject)
218
    {
219
        /** @var BlocksPage $dataObject */
220
        parent::saveInto($dataObject);
221
222
        $elementData = $this->Value();
223
        $idPrefixLength = strlen(sprintf(ElementalAreaController::FORM_NAME_TEMPLATE, ''));
224
225
        if (!$elementData) {
226
            return;
227
        }
228
229
        foreach ($elementData as $form => $data) {
230
            // Extract the ID
231
            $elementId = (int) substr($form, $idPrefixLength);
232
233
            /** @var BaseElement $element */
234
            $element = $this->getArea()->Elements()->byID($elementId);
235
236
            if (!$element) {
237
                // Ignore invalid elements
238
                continue;
239
            }
240
241
            $data = ElementalAreaController::removeNamespacesFromFields($data, $element->ID);
242
243
            $element->updateFromFormData($data);
244
            $element->write();
245
        }
246
    }
247
}
248