Completed
Push — master ( 2ee2df...59c799 )
by Will
03:20
created

ElementPageExtension::updateCMSFields()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 54
Code Lines 33

Duplication

Lines 7
Ratio 12.96 %

Importance

Changes 14
Bugs 2 Features 3
Metric Value
c 14
b 2
f 3
dl 7
loc 54
rs 7.8331
cc 7
eloc 33
nc 6
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package elemental
5
 */
6
class ElementPageExtension extends DataExtension
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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';
0 ignored issues
show
Unused Code introduced by
The property $elements_title is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
15
16
    /**
17
     * @config
18
     *
19
     * @var array $ignored_classes Classes to ignore adding elements too.
20
     */
21
    private static $ignored_classes = array();
0 ignored issues
show
Unused Code introduced by
The property $ignored_classes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
22
23
    /**
24
     * @var array $db
25
     */
26
    private static $db = array();
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
27
28
    private static $has_one = array(
0 ignored issues
show
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
29
        'ElementArea' => 'ElementalArea'
30
    );
31
32
    /**
33
     * Setup the CMS Fields
34
     *
35
     * @param FieldList
36
     */
37
    public function updateCMSFields(FieldList $fields)
38
    {
39
        // redirector pages should not have elements
40
        if (is_a($this->owner, 'RedirectorPage')) {
41
            return;
42 View Code Duplication
        } else if ($ignored = Config::inst()->get('ElementPageExtension', 'ignored_classes')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
            foreach ($ignored as $check) {
0 ignored issues
show
Bug introduced by
The expression $ignored of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
44
                if (is_a($this->owner, $check)) {
45
                    return;
46
                }
47
            }
48
        }
49
50
        // add an empty holder for content as some module explicitly use insert
51
        // after content.
52
        $fields->replaceField('Content', new LiteralField('Content', ''));
53
54
        $adder = new ElementalGridFieldAddNewMultiClass();
55
56
        $list = $this->getAvailableTypes();
57
        $adder->setClasses($list);
58
59
        $area = $this->owner->ElementArea();
60
61
        if (!$area->exists() || !$area->isInDB()) {
62
            $area->write();
63
64
            $this->owner->ElementAreaID = $area->ID;
65
            $this->owner->write();
66
        }
67
68
        $gridField = GridField::create('ElementArea',
69
            Config::inst()->get("ElementPageExtension", 'elements_title'),
70
            $this->owner->ElementArea()->Elements(),
71
            GridFieldConfig_RelationEditor::create()
72
                ->removeComponentsByType('GridFieldAddNewButton')
73
                ->removeComponentsByType('GridFieldAddExistingAutocompleter')
74
                ->removeComponentsByType('GridFieldDeleteAction')
75
                ->addComponent(new GridFieldDeleteAction(false))
76
                ->addComponent($adder)
77
                ->addComponent(new GridFieldSortableRows('Sort'))
78
        );
79
80
        $config = $gridField->getConfig();
81
        $paginator = $config->getComponentByType('GridFieldPaginator');
82
        $paginator->setItemsPerPage(100);
83
84
        $config->removeComponentsByType('GridFieldDetailForm');
85
        $config->addComponent(new VersionedDataObjectDetailsForm());
86
87
        $fields->addFieldToTab('Root.Main', $gridField, 'Metadata');
88
89
        return $fields;
90
    }
91
92
    public function getAvailableTypes() {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
93
        if (is_array($this->owner->config()->get('allowed_elements'))) {
94
            $list = $this->owner->config()->get('allowed_elements');
95 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96
            $classes = ClassInfo::subclassesFor('BaseElement');
97
            $list = array();
98
            unset($classes['BaseElement']);
99
100
            foreach ($classes as $class) {
0 ignored issues
show
Bug introduced by
The expression $classes of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
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 incase 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 View Code Duplication
        if ($ignored = Config::inst()->get('ElementPageExtension', 'ignored_classes')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
            foreach ($ignored as $check) {
0 ignored issues
show
Bug introduced by
The expression $ignored of type array|integer|double|string|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
129
                if (is_a($this->owner, $check)) {
130
                    return;
131
                }
132
            }
133
        }
134
135
        if ($this->owner->hasMethod('ElementArea')) {
136
            $elements = $this->owner->ElementArea();
137
138
            if (!$elements->isInDB()) {
139
                $elements->write();
140
                $this->owner->ElementAreaID = $elements->ID;
141
            } else {
142
                // Copy widgets content to Content to enable search
143
                $searchableContent = array();
144
145
                foreach ($elements->Items() as $element) {
146
                    if ($element->config()->exclude_from_content) {
147
                        continue;
148
                    }
149
150
                    $controller = $element->getController();
151
                    $controller->init();
152
153
                    array_push($searchableContent, $controller->WidgetHolder());
154
                }
155
156
                $this->owner->Content = trim(implode(' ', $searchableContent));
157
            }
158
        }
159
160
161
        // set theme_enabled back to what it was
162
        Config::inst()->update('SSViewer', 'theme_enabled', $originalThemeEnabled);
163
164
        parent::onBeforeWrite();
165
    }
166
167
    /**
168
     * If the page is duplicated, copy the widgets across too.
169
     * Gets called twice from either direction, due to bad DataObject
170
     * and SiteTree code, hence the weird if statement
171
     *
172
     * @return Page The duplicated page
0 ignored issues
show
Documentation introduced by
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...
173
     */
174
    public function onAfterDuplicate($duplicatePage)
175
    {
176
        if ($this->owner->ID != 0 && $this->owner->ID < $duplicatePage->ID) {
177
            $originalWidgetArea = $this->owner->getComponent('ElementArea');
178
            $duplicateWidgetArea = $originalWidgetArea->duplicate(false);
179
            $duplicateWidgetArea->write();
180
            $duplicatePage->ElementAreaID = $duplicateWidgetArea->ID;
181
            $duplicatePage->write();
182
183 View Code Duplication
            foreach ($originalWidgetArea->Items() as $originalWidget) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
                $duplicateWidget = $originalWidget->duplicate(true);
185
186
                // manually set the ParentID of each widget, so we don't get versioning issues
187
                DB::query(sprintf("UPDATE Widget SET ParentID = %d WHERE ID = %d", $duplicateWidgetArea->ID, $duplicateWidget->ID));
188
            }
189
        }
190
    }
191
192
    /**
193
     * If the page is duplicated across subsites, copy the widgets across too.
194
     *
195
     * @return Page The duplicated page
0 ignored issues
show
Documentation introduced by
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...
196
     */
197
    public function onAfterDuplicateToSubsite($originalPage)
198
    {
199
        $originalWidgetArea = $originalPage->getComponent('ElementArea');
200
        $duplicateWidgetArea = $originalWidgetArea->duplicate(false);
201
        $duplicateWidgetArea->write();
202
        $this->owner->ElementAreaID = $duplicateWidgetArea->ID;
203
        $this->owner->write();
204
205 View Code Duplication
        foreach ($originalWidgetArea->Items() as $originalWidget) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

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