Passed
Push — master ( a1de92...1a465b )
by Robbie
03:52
created

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