Completed
Push — master ( cd60cf...f7a4c7 )
by
unknown
22s
created

ElementalAreaField   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 23
eloc 89
dl 0
loc 239
rs 10
c 0
b 0
f 0

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 36 5
A setSubmittedValue() 0 4 1
A getReadOnlyBlockReducer() 0 42 4
A getSchemaDataDefaults() 0 27 5
A performReadonlyTransformation() 0 14 1
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\GridField\GridField;
17
use SilverStripe\Forms\TabSet;
18
use SilverStripe\ORM\DataObjectInterface;
19
use Symbiote\GridFieldExtensions\GridFieldAddNewMultiClass;
20
21
class ElementalAreaField extends GridField
22
{
23
    /**
24
     * @var ElementalArea $area
25
     */
26
    protected $area;
27
28
    /**
29
     * @var array $type
30
     */
31
    protected $types = [];
32
33
    /**
34
     * @var null
35
     */
36
    protected $inputType = null;
37
38
    protected $modelClassName = BaseElement::class;
39
40
    /**
41
     * @param string $name
42
     * @param ElementalArea $area
43
     * @param string[] $blockTypes
44
     */
45
    public function __construct($name, ElementalArea $area, array $blockTypes)
46
    {
47
        $this->setTypes($blockTypes);
48
49
        $config = new ElementalAreaConfig();
50
51
        if (!empty($blockTypes)) {
52
            /** @var GridFieldAddNewMultiClass $adder */
53
            $adder = Injector::inst()->create(GridFieldAddNewMultiClass::class);
54
            $adder->setClasses($blockTypes);
55
            $config->addComponent($adder);
56
        }
57
58
        // By default, no need for a title on the editor. If there is more than one area then use `setTitle` to describe
59
        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

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