Passed
Push — master ( eb27fd...f091a3 )
by
unknown
02:36
created

ElementalArea::ElementControllers()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 0
dl 0
loc 20
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace DNADesign\Elemental\Models;
4
5
use DNADesign\Elemental\Extensions\ElementalAreasExtension;
6
use SilverStripe\CMS\Model\SiteTree;
7
use SilverStripe\Core\ClassInfo;
8
use SilverStripe\Core\Extensible;
9
use SilverStripe\Core\Injector\Injector;
10
use SilverStripe\Dev\TestOnly;
11
use SilverStripe\ORM\ArrayList;
12
use SilverStripe\ORM\DataList;
13
use SilverStripe\ORM\DataObject;
14
use SilverStripe\ORM\FieldType\DBField;
15
use SilverStripe\ORM\FieldType\DBHTMLText;
16
use SilverStripe\ORM\HasManyList;
17
use SilverStripe\ORM\UnsavedRelationList;
18
use SilverStripe\Versioned\Versioned;
19
20
/**
21
 * Class ElementalArea
22
 * @package DNADesign\Elemental\Models
23
 *
24
 * @property string $OwnerClassName
25
 *
26
 * @method HasManyList|BaseElement[] Elements()
27
 */
28
class ElementalArea extends DataObject
29
{
30
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
31
        'OwnerClassName' => 'Varchar(255)',
32
    ];
33
34
    private static $has_many = [
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
35
        'Elements' => BaseElement::class,
36
    ];
37
38
    private static $extensions = [
0 ignored issues
show
introduced by
The private property $extensions is not used, and could be removed.
Loading history...
39
        Versioned::class,
40
    ];
41
42
    private static $owns = [
0 ignored issues
show
introduced by
The private property $owns is not used, and could be removed.
Loading history...
43
        'Elements',
44
    ];
45
46
    private static $cascade_deletes = [
0 ignored issues
show
introduced by
The private property $cascade_deletes is not used, and could be removed.
Loading history...
47
        'Elements',
48
    ];
49
50
    private static $cascade_duplicates = [
0 ignored issues
show
introduced by
The private property $cascade_duplicates is not used, and could be removed.
Loading history...
51
        'Elements',
52
    ];
53
54
    private static $summary_fields = [
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
55
        'Title' => 'Title',
56
    ];
57
58
    private static $table_name = 'ElementalArea';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
59
60
    /**
61
     * Cache various data to improve CMS load time
62
     *
63
     * @internal
64
     * @var array
65
     */
66
    protected $cacheData = [];
67
68
    /**
69
     * @return array
70
     */
71
    public function supportedPageTypes()
72
    {
73
        $elementalClasses = [];
74
75
        foreach (ClassInfo::getValidSubClasses(SiteTree::class) as $class) {
76
            if (Extensible::has_extension($class, ElementalAreasExtension::class)) {
77
                $elementalClasses[] = $class;
78
            }
79
        }
80
81
        return $elementalClasses;
82
    }
83
84
    /**
85
     * @return DBHTMLText
86
     */
87
    public function forTemplate()
88
    {
89
        return $this->renderWith(static::class);
90
    }
91
92
    /**
93
     * @param ArrayList $elements
94
     * @return $this
95
     */
96
    public function setElementsCached(ArrayList $elements)
97
    {
98
        $this->cacheData['elements'] = $elements;
99
100
        return $this;
101
    }
102
103
    /**
104
     * @param DataObject $page
105
     * @return $this
106
     */
107
    public function setOwnerPageCached(DataObject $page)
108
    {
109
        $this->cacheData['owner_page'] = $page;
110
111
        return $this;
112
    }
113
114
    /**
115
     * A cache-aware accessor for the elements
116
     * @return ArrayList|DataList|BaseElement[]
117
     */
118
    public function Elements()
119
    {
120
        if (isset($this->cacheData['elements'])) {
121
            return $this->cacheData['elements'];
122
        }
123
124
        return parent::Elements();
0 ignored issues
show
introduced by
The method Elements() does not exist on SilverStripe\ORM\DataObject. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

124
        return parent::/** @scrutinizer ignore-call */ Elements();
Loading history...
125
    }
126
127
    /**
128
     * Necessary to display results in CMS site search.
129
     *
130
     * @return DBField
131
     */
132
    public function Breadcrumbs()
133
    {
134
        $ownerClassName = $this->OwnerClassName;
135
136
        if ($owner = $ownerClassName::get()->filter('ElementalAreaID', $this->ID)->first()) {
137
            return DBField::create_field('HTMLText', sprintf(
138
                '<a href="%s">%s</a>',
139
                $owner->CMSEditLink(),
140
                $owner->Title
141
            ));
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * Used in template instead of {@link Elements()} to wrap each element in
149
     * its' controller, making it easier to access and process form logic and
150
     * actions stored in {@link ElementController}.
151
     *
152
     * @return ArrayList
153
     * @throws \Exception
154
     */
155
    public function ElementControllers()
156
    {
157
        // Don't try and process unsaved lists
158
        if ($this->Elements() instanceof UnsavedRelationList) {
159
            return ArrayList::create();
160
        }
161
162
        $controllers = ArrayList::create();
163
        $items = $this->Elements()->filterByCallback(function (BaseElement $item) {
164
            return $item->canView();
165
        });
166
167
        if (!is_null($items)) {
168
            foreach ($items as $element) {
169
                $controller = $element->getController();
170
                $controllers->push($controller);
171
            }
172
        }
173
174
        return $controllers;
175
    }
176
177
    /**
178
     * @return null|DataObject
179
     * @throws \Psr\Container\NotFoundExceptionInterface
180
     * @throws \SilverStripe\ORM\ValidationException
181
     */
182
    public function getOwnerPage()
183
    {
184
        // Allow for repeated calls to read from cache
185
        if (isset($this->cacheData['owner_page'])) {
186
            return $this->cacheData['owner_page'];
187
        }
188
189
        if ($this->OwnerClassName) {
190
            $class = $this->OwnerClassName;
191
            $instance = Injector::inst()->get($class);
192
            if (!ClassInfo::hasMethod($instance, 'getElementalRelations')) {
193
                return null;
194
            }
195
            $elementalAreaRelations = $instance->getElementalRelations();
196
197
            foreach ($elementalAreaRelations as $eaRelationship) {
198
                $areaID = $eaRelationship . 'ID';
199
200
                $currentStage = Versioned::get_stage() ?: Versioned::DRAFT;
201
                $page = Versioned::get_by_stage($class, $currentStage)->filter($areaID, $this->ID)->first();
202
203
                if ($page) {
204
                    $this->setOwnerPageCached($page);
205
                    return $page;
206
                }
207
            }
208
        }
209
210
        foreach ($this->supportedPageTypes() as $class) {
211
            $instance = Injector::inst()->get($class);
212
            if (!ClassInfo::hasMethod($instance, 'getElementalRelations')) {
213
                return null;
214
            }
215
            $elementalAreaRelations = $instance->getElementalRelations();
216
217
            foreach ($elementalAreaRelations as $eaRelationship) {
218
                $areaID = $eaRelationship . 'ID';
219
                try {
220
                    $page = Versioned::get_by_stage($class, Versioned::DRAFT)->filter($areaID, $this->ID)->first();
221
                } catch (\Exception $ex) {
222
                    // Usually this is catching cases where test stubs from other modules are trying to be loaded
223
                    // and failing in unit tests.
224
                    if (in_array(TestOnly::class, class_implements($class))) {
225
                        continue;
226
                    }
227
                    // Continue as normal...
228
                    throw $ex;
229
                }
230
231
                if ($page) {
232
                    if ($this->OwnerClassName !== $class) {
233
                        $this->OwnerClassName = $class;
234
235
                        // Avoid recursion: only write if it's already in the database
236
                        if ($this->isInDB()) {
237
                            $this->write();
238
                        }
239
                    }
240
241
                    $this->cacheData['area_relation_name'] = $page;
242
                    return $page;
243
                }
244
            }
245
        }
246
247
        return null;
248
    }
249
250
    /**
251
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
252
     * @return bool
253
     * @throws \Psr\Container\NotFoundExceptionInterface
254
     * @throws \SilverStripe\ORM\ValidationException
255
     */
256
    public function canEdit($member = null)
257
    {
258
        if (parent::canEdit($member)) {
259
            return true;
260
        }
261
262
        $ownerPage = $this->getOwnerPage();
263
        if ($ownerPage !== null) {
264
            return $this->getOwnerPage()->canEdit($member);
265
        }
266
267
        return false;
268
    }
269
270
    /**
271
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
272
     * @return bool
273
     * @throws \Psr\Container\NotFoundExceptionInterface
274
     * @throws \SilverStripe\ORM\ValidationException
275
     */
276
    public function canView($member = null)
277
    {
278
        if (parent::canEdit($member)) {
279
            return true;
280
        }
281
282
        $ownerPage = $this->getOwnerPage();
283
        if ($ownerPage !== null) {
284
            return $this->getOwnerPage()->canView($member);
285
        }
286
287
        return false;
288
    }
289
}
290