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) { |
||
0 ignored issues
–
show
|
|||
119 | return false; |
||
0 ignored issues
–
show
|
|||
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 |
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.