Completed
Push — master ( 59c799...149ad0 )
by Will
02:15
created

code/extensions/ElementPageExtension.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * @package elemental
5
 */
6
class ElementPageExtension extends DataExtension
7
{
8
9
    /**
10
     * @config
11
     *
12
     * @var string $elements_title Title of the element in the CMS.
13
     */
14
    private static $elements_title = 'Content Blocks';
15
16
    /**
17
     * @config
18
     *
19
     * @var array $ignored_classes Classes to ignore adding elements too.
20
     */
21
    private static $ignored_classes = array();
22
23
    /**
24
     * @var array $db
25
     */
26
    private static $db = array();
27
28
    /**
29
     * @var array $has_one
30
     */
31
    private static $has_one = array(
32
        'ElementArea' => 'ElementalArea'
33
    );
34
35
    /**
36
     * Setup the CMS Fields
37
     *
38
     * @param FieldList
39
     */
40
    public function updateCMSFields(FieldList $fields)
41
    {
42
        if(!$this->supportsElemental()) {
43
            return false;
44
        }
45
46
        // add an empty holder for content as some module explicitly use insert
47
        // after content.
48
        $fields->replaceField('Content', new LiteralField('Content', ''));
49
50
        $adder = new ElementalGridFieldAddNewMultiClass();
51
52
        $list = $this->getAvailableTypes();
53
        $adder->setClasses($list);
54
55
        $area = $this->owner->ElementArea();
56
57
        if (!$area->exists() || !$area->isInDB()) {
58
            $area->write();
59
60
            $this->owner->ElementAreaID = $area->ID;
61
            $this->owner->write();
62
        }
63
64
        $gridField = GridField::create('ElementArea',
65
            Config::inst()->get("ElementPageExtension", 'elements_title'),
66
            $this->owner->ElementArea()->Elements(),
67
            GridFieldConfig_RelationEditor::create()
68
                ->removeComponentsByType('GridFieldAddNewButton')
69
                ->removeComponentsByType('GridFieldDeleteAction')
70
                ->removeComponentsByType('GridFieldAddExistingAutocompleter')
71
                ->addComponent(new ElementalGridFieldAddExistingAutocompleter())
72
                ->addComponent(new ElementalGridFieldDeleteAction())
73
                ->addComponent($adder)
74
                ->addComponent(new GridFieldSortableRows('Sort'))
75
        );
76
77
        $config = $gridField->getConfig();
78
        $paginator = $config->getComponentByType('GridFieldPaginator');
79
        $paginator->setItemsPerPage(100);
80
81
        $config->removeComponentsByType('GridFieldDetailForm');
82
        $config->addComponent(new VersionedDataObjectDetailsForm());
83
84
        $fields->addFieldToTab('Root.Main', $gridField, 'Metadata');
85
86
        return $fields;
87
    }
88
89
    /**
90
     * @return array
91
     */
92
    public function getAvailableTypes() {
93
        if (is_array($this->owner->config()->get('allowed_elements'))) {
94
            $list = $this->owner->config()->get('allowed_elements');
95 View Code Duplication
        } else {
96
            $classes = ClassInfo::subclassesFor('BaseElement');
97
            $list = array();
98
            unset($classes['BaseElement']);
99
100
            foreach ($classes as $class) {
101
                $inst = singleton($class);
102
103
                if ($inst->canCreate()) {
104
                    $list[$class] = singleton($class)->i18n_singular_name();
105
                }
106
            }
107
        }
108
        if (method_exists($this->owner, 'sortElementalOptions')) {
109
            $this->owner->sortElementalOptions($list);
110
        } else {
111
            asort($list);
112
        }
113
114
        return $list;
115
    }
116
117
    /**
118
     * Make sure there is always a WidgetArea sidebar for adding widgets
119
     *
120
     */
121
    public function onBeforeWrite()
122
    {
123
        // enable theme in case elements are being rendered with templates stored in theme folder
124
        $originalThemeEnabled = Config::inst()->get('SSViewer', 'theme_enabled');
125
        Config::inst()->update('SSViewer', 'theme_enabled', true);
126
127
        if(!$this->supportsElemental()) {
128
            return;
129
        }
130
131
        if ($this->owner->hasMethod('ElementArea')) {
132
            $elements = $this->owner->ElementArea();
133
134
            if (!$elements->isInDB()) {
135
                $elements->write();
136
                $this->owner->ElementAreaID = $elements->ID;
137
            } else {
138
                // Copy widgets content to Content to enable search
139
                $searchableContent = array();
140
141
                foreach ($elements->Elements() as $element) {
142
                    if ($element->config()->exclude_from_content) {
143
                        continue;
144
                    }
145
146
                    $controller = $element->getController();
147
148
                    foreach ($elements->Items() as $element) {
149
                        $controller->init();
150
151
                        array_push($searchableContent, $controller->WidgetHolder());
152
                    }
153
                }
154
155
                $this->owner->Content = trim(implode(' ', $searchableContent));
156
            }
157
        }
158
159
160
        // set theme_enabled back to what it was
161
        Config::inst()->update('SSViewer', 'theme_enabled', $originalThemeEnabled);
162
163
        parent::onBeforeWrite();
164
    }
165
166
    /**
167
     * @return boolean
168
     */
169
    public function supportsElemental() {
170
        if (is_a($this->owner, 'RedirectorPage')) {
171
            return false;
172
        } else if ($ignored = Config::inst()->get('ElementPageExtension', 'ignored_classes')) {
173
            foreach ($ignored as $check) {
174
                if (is_a($this->owner, $check)) {
175
                    return false;
176
                }
177
            }
178
        }
179
180
        return true;
181
    }
182
183
    /**
184
     * If the page is duplicated, copy the widgets across too.
185
     *
186
     * Gets called twice from either direction, due to bad DataObject and SiteTree code, hence the weird if statement
187
     *
188
     * @return Page The duplicated page
0 ignored issues
show
Should the return type not be Page|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
189
     */
190
    public function onAfterDuplicate($duplicatePage)
191
    {
192
        if ($this->owner->ID != 0 && $this->owner->ID < $duplicatePage->ID) {
193
            $originalWidgetArea = $this->owner->getComponent('ElementArea');
194
            $duplicateWidgetArea = $originalWidgetArea->duplicate(false);
195
            $duplicateWidgetArea->write();
196
            $duplicatePage->ElementAreaID = $duplicateWidgetArea->ID;
197
            $duplicatePage->write();
198
199 View Code Duplication
            foreach ($originalWidgetArea->Items() as $originalWidget) {
200
                $duplicateWidget = $originalWidget->duplicate(true);
201
202
                // manually set the ParentID of each widget, so we don't get versioning issues
203
                DB::query(sprintf("UPDATE Widget SET ParentID = %d WHERE ID = %d", $duplicateWidgetArea->ID, $duplicateWidget->ID));
204
            }
205
        }
206
    }
207
208
    /**
209
     * If the page is duplicated across subsites, copy the widgets across too.
210
     *
211
     * @return Page The duplicated page
0 ignored issues
show
Should the return type not be Page|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
212
     */
213
    public function onAfterDuplicateToSubsite($originalPage)
214
    {
215
        $originalWidgetArea = $originalPage->getComponent('ElementArea');
216
        $duplicateWidgetArea = $originalWidgetArea->duplicate(false);
217
        $duplicateWidgetArea->write();
218
        $this->owner->ElementAreaID = $duplicateWidgetArea->ID;
219
        $this->owner->write();
220
221 View Code Duplication
        foreach ($originalWidgetArea->Items() as $originalWidget) {
222
            $duplicateWidget = $originalWidget->duplicate(true);
223
224
            // manually set the ParentID of each widget, so we don't get versioning issues
225
            DB::query(sprintf("UPDATE Widget SET ParentID = %d WHERE ID = %d", $duplicateWidgetArea->ID, $duplicateWidget->ID));
226
        }
227
    }
228
229
    /**
230
     * Publish
231
     */
232
    public function onAfterPublish()
233
    {
234
        if ($id = $this->owner->ElementAreaID) {
235
            $widgets = Versioned::get_by_stage('BaseElement', 'Stage', "ParentID = '$id'");
236
            $staged = array();
237
238
            foreach ($widgets as $widget) {
239
                $staged[] = $widget->ID;
240
241
                $widget->publish('Stage', 'Live');
242
            }
243
244
            // remove any elements that are on live but not in draft.
245
            $widgets = Versioned::get_by_stage('BaseElement', 'Live', "ParentID = '$id'");
246
247
            foreach ($widgets as $widget) {
248
                if (!in_array($widget->ID, $staged)) {
249
                    $widget->deleteFromStage('Live');
250
                }
251
            }
252
        }
253
    }
254
255
    /**
256
     * Roll back all changes if the parent page has a rollback event
257
     *
258
     * Only do rollback if it's the 'cancel draft changes' rollback, not a specific version
259
     * rollback.
260
     *
261
     * @param string $version
262
     * @return null
263
     */
264
    public function onBeforeRollback($version)
265
    {
266
        if ($version !== 'Live') {
267
            // we don't yet have a smart way of rolling back to a specific version
268
            return;
269
        }
270
        if ($id = $this->owner->ElementAreaID) {
271
            $widgets = Versioned::get_by_stage('BaseElement', 'Live', "ParentID = '$id'");
272
            $staged = array();
273
274
            foreach ($widgets as $widget) {
275
                $staged[] = $widget->ID;
276
277
                $widget->invokeWithExtensions('onBeforeRollback', $widget);
278
279
                $widget->publish("Live", "Stage", false);
280
281
                $widget->invokeWithExtensions('onAfterRollback', $widget);
282
            }
283
        }
284
    }
285
}
286