Passed
Pull Request — master (#439)
by
unknown
02:14
created

ElementalAreaField::setSubmittedValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace DNADesign\Elemental\Forms;
4
5
use BlocksPage;
0 ignored issues
show
Bug introduced by
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\Convert;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\Forms\CompositeField;
15
use SilverStripe\Forms\FieldGroup;
16
use SilverStripe\Forms\FieldList;
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);
61
        $this->area = $area;
62
63
        $this->addExtraClass('element-editor__container');
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
        $pageId = ($this->getArea() && ($page = $this->getArea()->getOwnerPage())) ? $page->ID : null;
119
        $schemaData['page-id'] = $pageId;
120
121
        $blockTypes = [];
122
123
        // Use the internal (temporary) provider to get cached tab names.
124
        /** @var ElementTabProvider $tabProvider */
125
        $tabProvider = Injector::inst()->get(ElementTabProvider::class);
126
127
        foreach ($this->getTypes() as $className => $blockTitle) {
128
            $blockTypes[] = [
129
                'name' => str_replace('\\', '-', $className),
130
                'title' => $blockTitle,
131
                'icon' => Config::inst()->get($className, 'icon'),
132
                'tabs' => $tabProvider->getTabsForElement($className),
133
            ];
134
        }
135
136
        $schemaData['element-types'] = $blockTypes;
137
        $schemaData['base-add-href'] = Controller::join_links($this->Link(), 'add-multi-class');
138
        return $schemaData;
139
    }
140
141
    /**
142
     * A getter method that seems redundant in that it is a function that returns a function,
143
     * however the returned closure is used in an array map function to return a complete FieldList
144
     * representing a read only view of the element passed in (to the closure).
145
     *
146
     * @return callable
147
     */
148
    protected function getReadOnlyBlockReducer()
149
    {
150
        return function (BaseElement $element) {
151
            $parentName = 'Element' . $element->ID;
152
            $elementFields = $element->getCMSFields();
153
154
            // Obtain highest impact fields for a summary (e.g. Title & Content)
155
            foreach ($elementFields as $field) {
156
                if (is_object($field) && $field instanceof TabSet) {
157
                    // Assign the fields of the first Tab in the TabSet - most regularly 'Root.Main'
158
                    $elementFields = $field->FieldList()->first()->FieldList();
159
                    break;
160
                }
161
            }
162
163
            // Set values (before names don't match anymore)
164
            $elementFields->setValues($element->getQueriedDatabaseFields());
165
166
            // Ensure field names are unique between elements on parent form
167
            $elementFields->recursiveWalk(function ($field) use ($parentName) {
168
                $field->setName($parentName . '_' . $field->getName());
169
            });
170
171
            // Combine into an appropriately named group
172
            $elementGroup = FieldGroup::create($elementFields);
173
            $elementGroup->setForm($this->getForm());
174
            $elementGroup->setName($parentName);
175
            $elementGroup->addExtraClass('elemental-area__element--historic');
176
177
            // Also set the important data for the rendering Component
178
            $elementGroup->setSchemaData([
179
                'data' => [
180
                    'ElementID' => $element->ID,
181
                    'ElementType' => $element->getType(),
182
                    'ElementIcon' => $element->config()->icon,
183
                    'ElementTitle' => $element->Title,
184
                    // @todo: Change this to block history permalink when that functionality becomes available.
185
                    'ElementEditLink' => $element->CMSEditLink()
186
                ]
187
            ]);
188
189
            return $elementGroup;
190
        };
191
    }
192
193
    /**
194
     * Provides a readonly representation of the GridField (superclass) Uses a reducer
195
     * {@see ElementalAreaField::getReadOnlyBlockReducer()} to fetch a read only representation of the listed class
196
     * {@see GridField::getModelClass()}
197
     *
198
     * @return CompositeField
199
     */
200
    public function performReadonlyTransformation()
201
    {
202
        /** @var CompositeField $readOnlyField */
203
        $readOnlyField = $this->castedCopy(CompositeField::class);
204
        $blockReducer = $this->getReadOnlyBlockReducer();
205
        $readOnlyField->setChildren(
206
            FieldList::create(array_map($blockReducer, $this->getArea()->Elements()->toArray()))
207
        );
208
209
        $readOnlyField = $readOnlyField->performReadonlyTransformation();
210
        return $readOnlyField
211
            ->setReadOnly(true)
212
            ->setName($this->getName())
213
            ->addExtraClass('elemental-area--read-only');
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    public function setSubmittedValue($value, $data = null)
220
    {
221
        // Content comes through as a JSON encoded list through a hidden field.
222
        return $this->setValue(Convert::json2array($value));
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228
    public function saveInto(DataObjectInterface $dataObject)
229
    {
230
        /** @var BlocksPage $dataObject */
231
        parent::saveInto($dataObject);
232
233
        $elementData = $this->Value();
234
        $idPrefixLength = strlen(sprintf(ElementalAreaController::FORM_NAME_TEMPLATE, ''));
235
236
        foreach ($elementData as $form => $data) {
237
            // Extract the ID
238
            $elementId = (int) substr($form, $idPrefixLength);
239
240
            /** @var BaseElement $element */
241
            $element = $this->getArea()->Elements()->byID($elementId);
242
243
            if (!$element) {
244
                // Ignore invalid elements
245
                continue;
246
            }
247
248
            $fields = [];
249
250
            $fieldNamePrefix = sprintf(EditFormFactory::FIELD_NAMESPACE_TEMPLATE, $elementId, '');
251
            $prefixLength = strlen($fieldNamePrefix);
252
253
            foreach ($data as $field => $datum) {
254
                // Check that the field starts with a valid name
255
                if (strpos($field, $fieldNamePrefix) !== 0) {
256
                    continue;
257
                }
258
259
                $fields[substr($field, $prefixLength)] = $datum;
260
            }
261
262
            $element->update($fields);
263
            $element->write();
264
        }
265
    }
266
}
267