Test Setup Failed
Pull Request — master (#197)
by Gorrie
01:07
created

src/Extensions/ElementalAreasExtension.php (1 issue)

1
<?php
2
3
namespace DNADesign\Elemental\Extensions;
4
5
use DNADesign\Elemental\Models\BaseElement;
6
use DNADesign\Elemental\Models\ElementalArea;
7
use DNADesign\Elemental\ElementalEditor;
8
use SilverStripe\CMS\Model\SiteTree;
9
use SilverStripe\Core\ClassInfo;
10
use SilverStripe\Core\Config\Config;
11
use SilverStripe\Forms\FieldList;
12
use SilverStripe\Forms\LiteralField;
13
use SilverStripe\CMS\Model\RedirectorPage;
14
use SilverStripe\CMS\Model\VirtualPage;
15
use SilverStripe\ORM\DataExtension;
16
17
/**
18
 * This extension handles most of the relationships between pages and element
19
 * area, it doesn't add an ElementArea to the page however. Because of this,
20
 * developers can add multiple {@link ElementArea} areas to to a page.
21
 *
22
 * If you want multiple ElementalAreas add them as has_ones, add this extensions
23
 * and MAKE SURE you don't forget to add ElementAreas to $owns, otherwise they
24
 * will never publish
25
 *
26
 * private static $has_one = array(
27
 *     'ElementalArea1' => ElementalArea::class,
28
 *     'ElementalArea2' => ElementalArea::class
29
 * );
30
 *
31
 * private static $owns = array(
32
 *     'ElementalArea1',
33
 *     'ElementalArea2'
34
 * );
35
 *
36
 * @package elemental
37
 */
38
class ElementalAreasExtension extends DataExtension
39
{
40
    /**
41
     * @config
42
     *
43
     * @var array $ignored_classes Classes to ignore adding elements too.
44
     */
45
    private static $ignored_classes = [];
46
47
    /**
48
     * @config
49
     *
50
     * On saving the element area, should Elemental reset the main website
51
     * `$Content` field.
52
     *
53
     * @var boolean
54
     */
55
    private static $clear_contentfield = false;
56
57
    /**
58
     * Get the available element types for this page type,
59
     *
60
     * Uses allowed_elements, stop_element_inheritance, disallowed_elements in
61
     * order to get to correct list.
62
     *
63
     * @return array
64
     */
65
    public function getElementalTypes()
66
    {
67
        $config = $this->owner->config();
68
69
        if (is_array($config->get('allowed_elements'))) {
70
            if ($config->get('stop_element_inheritance')) {
71
                $availableClasses = $config->get('allowed_elements', Config::UNINHERITED);
72
            } else {
73
                $availableClasses = $config->get('allowed_elements');
74
            }
75
        } else {
76
            $availableClasses = ClassInfo::subclassesFor(BaseElement::class);
77
        }
78
79
        $disallowedElements = (array) $config->get('disallowed_elements');
80
        $list = array();
81
82
        foreach ($availableClasses as $availableClass) {
83
            $inst = singleton($availableClass);
84
85
            if (!in_array($availableClass, $disallowedElements) && $inst->canCreate()) {
86
                if ($inst->hasMethod('canCreateElement') && !$inst->canCreateElement()) {
87
                    continue;
88
                }
89
90
                $list[$availableClass] = $inst->getType();
91
            }
92
        }
93
94
        if ($config->get('sort_types_alphabetically') !== false) {
95
            asort($list);
96
        }
97
98
        if (isset($list[BaseElement::class])) {
99
            unset($list[BaseElement::class]);
100
        }
101
102
        $this->owner->invokeWithExtensions('updateAvailableTypesForClass', $class, $list);
103
104
        return $list;
105
    }
106
107
    /**
108
     * Returns an array of the relation names to ElementAreas. Ignores any
109
     * has_one fields named `Parent` as that would indicate that this is child
110
     * of an existing area
111
     *
112
     * @return array
113
     */
114
    public function getElementalRelations()
115
    {
116
        $hasOnes = $this->owner->hasOne();
117
118
        if (!$hasOnes) {
119
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
120
        }
121
122
        $elementalAreaRelations = [];
123
124
        foreach ($hasOnes as $hasOneName => $hasOneClass) {
125
            if ($hasOneName === 'Parent' || $hasOneName === 'ParentID') {
126
                continue;
127
            }
128
129
            if ($hasOneClass == ElementalArea::class || is_subclass_of($hasOneClass, ElementalArea::class)) {
130
                $elementalAreaRelations[] = $hasOneName;
131
            }
132
        }
133
134
        return $elementalAreaRelations;
135
    }
136
137
    /**
138
     * Setup the CMS Fields
139
     *
140
     * @param FieldList
141
     */
142
    public function updateCMSFields(FieldList $fields)
143
    {
144
        if (!$this->supportsElemental()) {
145
            return;
146
        }
147
148
        // add an empty holder for content as some module explicitly use insert
149
        // after content.
150
        $fields->replaceField('Content', new LiteralField('Content', ''));
151
        $elementalAreaRelations = $this->owner->getElementalRelations();
152
153
        foreach ($elementalAreaRelations as $eaRelationship) {
154
            $key = $eaRelationship . 'ID';
155
156
            // remove the scaffold dropdown
157
            $fields->removeByName($key);
158
159
            // remove the field, but don't add anything.
160
            if (!$this->owner->isInDb()) {
161
                continue;
162
            }
163
164
            $area = $this->owner->$eaRelationship();
165
166
            // if area isn't in the database then force a write so the blocks have a parent ID.
167
            if (!$area->isInDb()) {
168
                $area->write();
169
170
                $this->owner->{$key} = $area->ID;
171
                $this->owner->write();
172
            }
173
174
            $editor = ElementalEditor::create($eaRelationship, $area);
175
            $editor->setTypes($this->getElementalTypes());
176
177
            if ($this->owner instanceof SiteTree && $fields->findOrMakeTab('Root.Main')->fieldByName('Metadata')) {
178
                $fields->addFieldToTab('Root.Main', $editor->getField(), 'Metadata');
179
            } else {
180
                $fields->addFieldToTab('Root.Main', $editor->getField());
181
            }
182
        }
183
184
        return $fields;
185
    }
186
187
    /**
188
     * Make sure there is always an ElementalArea for adding Elements
189
     *
190
     */
191
    public function onBeforeWrite()
192
    {
193
        parent::onBeforeWrite();
194
195
        if (!$this->supportsElemental()) {
196
            return;
197
        }
198
199
        $elementalAreaRelations = $this->owner->getElementalRelations();
200
201
        foreach ($elementalAreaRelations as $eaRelationship) {
202
            $areaID = $eaRelationship . 'ID';
203
204
            if (!$this->owner->$areaID) {
205
                $area = ElementalArea::create();
206
                $area->OwnerClassName = $this->owner->ClassName;
207
                $area->write();
208
                $this->owner->$areaID = $area->ID;
209
            } elseif ($area = ElementalArea::get()->filter('ID', $this->owner->$areaID)->first()) {
210
                $area->write();
211
            }
212
        }
213
214
        if (Config::inst()->get(self::class, 'clear_contentfield')) {
215
            $this->owner->Content = '';
216
        }
217
    }
218
219
    /**
220
     * @return boolean
221
     */
222
    public function supportsElemental()
223
    {
224
        if ($this->owner->hasMethod('includeElemental')) {
225
            $res = $this->owner->includeElemental();
226
227
            if ($res !== null) {
228
                return $res;
229
            }
230
        }
231
232
        if (is_a($this->owner, RedirectorPage::class) || is_a($this->owner, VirtualPage::class)) {
233
            return false;
234
        } elseif ($ignored = Config::inst()->get(ElementalPageExtension::class, 'ignored_classes')) {
235
            foreach ($ignored as $check) {
236
                if (is_a($this->owner, $check)) {
237
                    return false;
238
                }
239
            }
240
        }
241
242
        return true;
243
    }
244
}
245