Passed
Pull Request — master (#650)
by Loz
04:12
created

src/Models/ElementalArea.php (6 issues)

1
<?php
2
3
namespace DNADesign\Elemental\Models;
4
5
use DNADesign\Elemental\Extensions\ElementalAreasExtension;
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Extensible;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Dev\TestOnly;
10
use SilverStripe\ORM\ArrayList;
11
use SilverStripe\ORM\DataList;
12
use SilverStripe\ORM\DataObject;
13
use SilverStripe\ORM\FieldType\DBField;
14
use SilverStripe\ORM\FieldType\DBHTMLText;
15
use SilverStripe\ORM\UnsavedRelationList;
16
use SilverStripe\Versioned\Versioned;
17
18
/**
19
 * Class ElementalArea
20
 * @package DNADesign\Elemental\Models
21
 *
22
 * @property string $OwnerClassName
23
 */
24
class ElementalArea extends DataObject
25
{
26
    private static $db = [
0 ignored issues
show
The private property $db is not used, and could be removed.
Loading history...
27
        'OwnerClassName' => 'Varchar(255)',
28
    ];
29
30
    private static $has_many = [
0 ignored issues
show
The private property $has_many is not used, and could be removed.
Loading history...
31
        'Elements' => BaseElement::class,
32
    ];
33
34
    private static $extensions = [
0 ignored issues
show
The private property $extensions is not used, and could be removed.
Loading history...
35
        Versioned::class,
36
    ];
37
38
    private static $owns = [
0 ignored issues
show
The private property $owns is not used, and could be removed.
Loading history...
39
        'Elements',
40
    ];
41
42
    private static $cascade_deletes = [
0 ignored issues
show
The private property $cascade_deletes is not used, and could be removed.
Loading history...
43
        'Elements',
44
    ];
45
46
    private static $cascade_duplicates = [
47
        'Elements',
48
    ];
49
50
    private static $summary_fields = [
0 ignored issues
show
The private property $summary_fields is not used, and could be removed.
Loading history...
51
        'Title' => 'Title',
52
    ];
53
54
    private static $table_name = 'ElementalArea';
55
56
    /**
57
     * Don't show this model in campaign admin as part of implicit change sets
58
     *
59
     * @config
60
     * @var bool
61
     */
62
    private static $hide_in_campaigns = true;
63
64
    /**
65
     * Cache various data to improve CMS load time
66
     *
67
     * @internal
68
     * @var array
69
     */
70
    protected $cacheData = [];
71
72
    /**
73
     * @return array
74
     */
75
    public function supportedPageTypes()
76
    {
77
        $elementalClasses = [];
78
79
        foreach (ClassInfo::getValidSubClasses(DataObject::class) as $class) {
80
            if (Extensible::has_extension($class, ElementalAreasExtension::class)) {
81
                $elementalClasses[] = $class;
82
            }
83
        }
84
85
        return $elementalClasses;
86
    }
87
88
    /**
89
     * @return DBHTMLText
90
     */
91
    public function forTemplate()
92
    {
93
        return $this->renderWith(static::class);
94
    }
95
96
    /**
97
     * @param ArrayList $elements
98
     * @return $this
99
     */
100
    public function setElementsCached(ArrayList $elements)
101
    {
102
        $this->cacheData['elements'] = $elements;
103
104
        return $this;
105
    }
106
107
    /**
108
     * @param DataObject $page
109
     * @return $this
110
     */
111
    public function setOwnerPageCached(DataObject $page)
112
    {
113
        $this->cacheData['owner_page'] = $page;
114
115
        return $this;
116
    }
117
118
    /**
119
     * A cache-aware accessor for the elements
120
     * @return ArrayList|DataList|BaseElement[]
121
     */
122
    public function Elements()
123
    {
124
        if (isset($this->cacheData['elements'])) {
125
            return $this->cacheData['elements'];
126
        }
127
128
        return parent::Elements();
129
    }
130
131
    /**
132
     * Necessary to display results in CMS site search.
133
     *
134
     * @return DBField
135
     */
136
    public function Breadcrumbs()
137
    {
138
        $ownerClassName = $this->OwnerClassName;
139
140
        if ($owner = $ownerClassName::get()->filter('ElementalAreaID', $this->ID)->first()) {
141
            return DBField::create_field('HTMLText', sprintf(
142
                '<a href="%s">%s</a>',
143
                $owner->CMSEditLink(),
144
                $owner->Title
145
            ));
146
        }
147
148
        return null;
149
    }
150
151
    /**
152
     * Used in template instead of {@link Elements()} to wrap each element in
153
     * its' controller, making it easier to access and process form logic and
154
     * actions stored in {@link ElementController}.
155
     *
156
     * @return ArrayList
157
     * @throws \Exception
158
     */
159
    public function ElementControllers()
160
    {
161
        // Don't try and process unsaved lists
162
        if ($this->Elements() instanceof UnsavedRelationList) {
163
            return ArrayList::create();
164
        }
165
166
        $controllers = ArrayList::create();
167
        $items = $this->Elements()->filterByCallback(function (BaseElement $item) {
168
            return $item->canView();
169
        });
170
171
        if (!is_null($items)) {
172
            foreach ($items as $element) {
173
                $controller = $element->getController();
174
                $controllers->push($controller);
175
            }
176
        }
177
178
        return $controllers;
179
    }
180
181
    /**
182
     * @return null|DataObject
183
     * @throws \Psr\Container\NotFoundExceptionInterface
184
     * @throws \SilverStripe\ORM\ValidationException
185
     */
186
    public function getOwnerPage()
187
    {
188
        // You can't find the owner page of a area that hasn't been save yet
189
        if (!$this->isInDB()) {
190
            return null;
191
        }
192
193
        // Allow for repeated calls to read from cache
194
        if (isset($this->cacheData['owner_page'])) {
195
            return $this->cacheData['owner_page'];
196
        }
197
198
        if ($this->OwnerClassName) {
199
            $class = $this->OwnerClassName;
200
            $instance = Injector::inst()->get($class);
201
            if (!ClassInfo::hasMethod($instance, 'getElementalRelations')) {
202
                return null;
203
            }
204
            $elementalAreaRelations = $instance->getElementalRelations();
205
206
            foreach ($elementalAreaRelations as $eaRelationship) {
207
                $areaID = $eaRelationship . 'ID';
208
209
                $currentStage = Versioned::get_stage() ?: Versioned::DRAFT;
210
                $page = Versioned::get_by_stage($class, $currentStage)->filter($areaID, $this->ID)->first();
211
212
                if ($page) {
213
                    $this->setOwnerPageCached($page);
214
                    return $page;
215
                }
216
            }
217
        }
218
219
        foreach ($this->supportedPageTypes() as $class) {
220
            $instance = Injector::inst()->get($class);
221
            if (!ClassInfo::hasMethod($instance, 'getElementalRelations')) {
222
                return null;
223
            }
224
225
            $areaIDFilters = [];
226
            foreach ($instance->getElementalRelations() as $eaRelationship) {
227
                $areaIDFilters[$eaRelationship . 'ID'] = $this->ID;
228
            }
229
230
            try {
231
                $page = Versioned::get_by_stage($class, Versioned::DRAFT)->filterAny($areaIDFilters)->first();
232
            } catch (\Exception $ex) {
233
                // Usually this is catching cases where test stubs from other modules are trying to be loaded
234
                // and failing in unit tests.
235
                if (in_array(TestOnly::class, class_implements($class))) {
236
                    continue;
237
                }
238
                // Continue as normal...
239
                throw $ex;
240
            }
241
242
            if ($page) {
243
                if ($this->OwnerClassName !== $class) {
244
                    $this->OwnerClassName = $class;
245
246
                    // Avoid recursion: only write if it's already in the database
247
                    if ($this->isInDB()) {
248
                        $this->write();
249
                    }
250
                }
251
252
                $this->cacheData['area_relation_name'] = $page;
253
                return $page;
254
            }
255
        }
256
257
        return null;
258
    }
259
260
    /**
261
     * @param null $member
262
     * @return bool
263
     * @throws \Psr\Container\NotFoundExceptionInterface
264
     * @throws \SilverStripe\ORM\ValidationException
265
     */
266
    public function canEdit($member = null)
267
    {
268
        if (parent::canEdit($member)) {
269
            return true;
270
        }
271
272
        $ownerPage = $this->getOwnerPage();
273
        if ($ownerPage !== null) {
274
            return $this->getOwnerPage()->canEdit($member);
275
        }
276
277
        return false;
278
    }
279
280
    /**
281
     * @param null $member
282
     * @return bool
283
     * @throws \Psr\Container\NotFoundExceptionInterface
284
     * @throws \SilverStripe\ORM\ValidationException
285
     */
286
    public function canView($member = null)
287
    {
288
        if (parent::canEdit($member)) {
289
            return true;
290
        }
291
292
        $ownerPage = $this->getOwnerPage();
293
        if ($ownerPage !== null) {
294
            return $this->getOwnerPage()->canView($member);
295
        }
296
297
        return false;
298
    }
299
}
300