Passed
Push — master ( bec2a0...9c6a4b )
by Nicolaas
02:11
created

AllLinksControllerInfo::findDataRecord()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 11
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 17
rs 9.9
1
<?php
2
3
namespace Sunnysideup\TemplateOverview\Api\Providers;
4
use Sunnysideup\TemplateOverview\Api\AllLinksProviderBase;
5
use Sunnysideup\TemplateOverview\Api\AllLinks;
6
7
use ReflectionClass;
8
use ReflectionMethod;
9
10
11
use SilverStripe\Admin\LeftAndMain;
12
use SilverStripe\CMS\Controllers\ContentController;
13
use SilverStripe\Control\Controller;
14
use SilverStripe\Control\Director;
15
use SilverStripe\Core\ClassInfo;
16
use SilverStripe\Core\Config\Config;
17
use SilverStripe\Core\Injector\Injector;
18
19
use SilverStripe\ORM\DataObject;
20
use SilverStripe\ORM\DB;
21
22
class AllLinksControllerInfo extends AllLinksProviderBase
23
{
24
25
    /**
26
     * @var array
27
     */
28
    protected $linksAndActions = [];
29
30
    /**
31
     * @var array
32
     */
33
    protected $reflectionClasses = [];
34
35
    /**
36
     * @var array
37
     */
38
    protected $classObjects = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $dataRecordClassNames = [];
44
45
    /**
46
     * @var array
47
     */
48
    protected $dataRecordClassObjects = [];
49
50
    /**
51
     * @var array|null
52
     */
53
    protected $routes = null;
54
55
    /**
56
     * @var array
57
     */
58
    protected $nameSpaces = [];
59
60
    /**
61
     * @param  array $nameSpaces
62
     *
63
     * @return AllLinksControllerInfo
64
     */
65
    public function setValidNameSpaces($nameSpaces): self
66
    {
67
        $this->nameSpaces = $nameSpaces;
68
69
        return $this;
70
    }
71
72
73
    /**
74
     * @return array
75
     */
76
    public function getAllLinksInner(): array
77
    {
78
        $finalFinalArray = [];
79
80
        $linksAndActions = $this->getLinksAndActions();
81
        $allowedActions = $linksAndActions['Actions'];
82
        $controllerLinks = $linksAndActions['Links'];
83
        $finalArray = $linksAndActions['CustomLinks'];
84
85
        // die('---');
86
        //construct array!
87
        foreach ($allowedActions as $className => $methods) {
88
            $link = $controllerLinks[$className];
89
            if ($link) {
90
                $finalArray[$link] = $className;
91
            } else {
92
                $link = '???';
93
            }
94
            if (substr($link, -1) !== '/') {
95
                $link .= '/';
96
            }
97
            if (is_array($methods)) {
98
                foreach ($methods as $method) {
99
                    unset($allowedActions[$className][$method]);
100
                    $finalArray[$link . $method . '/'] = $className;
101
                }
102
            }
103
        }
104
105
        foreach ($finalArray as $link => $className) {
106
            $finalFinalArray[] = [
107
                'ClassName' => $className,
108
                'Link' => $link,
109
            ];
110
        }
111
        usort($finalFinalArray, function ($a, $b) {
112
            if ($a['ClassName'] !== $b['ClassName']) {
113
                return $a['ClassName'] <=> $b['ClassName'];
114
            }
115
116
            return $a['Link'] <=> $b['Link'];
117
        });
118
119
        return $finalFinalArray;
120
    }
121
122
    /**
123
     * returns Array with Links and Actions
124
     * @return array
125
     */
126
    public function getLinksAndActions(): array
127
    {
128
        if (count($this->linksAndActions) === 0) {
129
            $this->linksAndActions['Links'] = [];
130
            $this->linksAndActions['Actions'] = [];
131
            $this->linksAndActions['CustomLinks'] = [];
132
            $classes = ClassInfo::subclassesFor(Controller::class);
133
            foreach ($classes as $className) {
134
                $isValid = $this->isValidController($className);
135
                if (! $isValid) {
136
                    continue;
137
                }
138
                $this->linksAndActions['Links'][$className] = '';
139
140
                //main links
141
142
                //custom links
143
                $customLinks = $this->findCustomLinks($className);
144
                foreach ($customLinks as $customLink) {
145
                    $this->linksAndActions['CustomLinks'][$customLink] = $className;
146
                }
147
                $link = $this->findLink($className);
148
                if ($link) {
149
                    $this->linksAndActions['Links'][$className] = $link;
150
                }
151
                $this->linksAndActions['Actions'][$className] = $this->findAllowedActions($className);
152
            }
153
        }
154
155
        return $this->linksAndActions;
156
    }
157
158
    /**
159
     * can it be used?
160
     * @param  string $className
161
     * @return bool
162
     */
163
    protected function isValidController($className): bool
164
    {
165
        return $this->controllerReflectionClass($className) ? true : false;
166
    }
167
168
    /**
169
     * @param  string $className
170
     * @return ReflectionClass|null
171
     */
172
    protected function controllerReflectionClass($className)
173
    {
174
        if (! isset($this->reflectionClasses[$className])) {
175
            $this->reflectionClasses[$className] = null;
176
            //skip base class
177
            if ($className === Controller::class) {
178
                return null;
179
            }
180
181
            //check for abstract ones
182
            $controllerReflectionClass = new ReflectionClass($className);
183
            if ($controllerReflectionClass->isAbstract()) {
184
                // echo '<hr />Ditching because of abstract: '.$className;
185
                return null;
186
            }
187
188
            //match to filter
189
            $filterMatch = count($this->nameSpaces) ? false : true;
190
            foreach ($this->nameSpaces as $filter) {
191
                if (strpos($className, $filter) !== false) {
192
                    $filterMatch = true;
193
                }
194
            }
195
            if ($filterMatch === false) {
196
                return null;
197
            }
198
199
            //check for ones that can not be constructed
200
            if ($controllerReflectionClass->isSubclassOf(LeftAndMain::class)) {
201
                return null;
202
            }
203
            $params = $controllerReflectionClass->getConstructor()->getParameters();
204
            if ($controllerReflectionClass->isSubclassOf(ContentController::class)) {
205
                //do nothing
206
            } elseif (count($params) > 0) {
207
                return null;
208
            }
209
210
            $this->reflectionClasses[$className] = $controllerReflectionClass;
211
212
            return $this->reflectionClasses[$className];
213
        }
214
        return $this->reflectionClasses[$className];
215
    }
216
217
    /**
218
     * @param  string $className
219
     * @return string
220
     */
221
    protected function findSingleton($className)
222
    {
223
        if ($this->controllerReflectionClass($className)) {
224
            $this->classObjects[$className] = null;
225
            if (! isset($this->classObjects[$className])) {
226
                try {
227
                    $this->classObjects[$className] = Injector::inst()->get($className);
228
                } catch (\Error $e) {
229
                    $this->classObjects[$className] = null;
230
                }
231
            }
232
        }
233
234
        return $this->classObjects[$className];
235
    }
236
237
    /**
238
     * @param  string $className
239
     * @return DataObject|null
240
     */
241
    protected function findDataRecord($className)
242
    {
243
        if (! isset($this->dataRecordClassObjects[$className])) {
244
            $this->dataRecordClassObjects[$className] = null;
245
            $dataRecordClassName = substr($className, 0, -1 * strlen('Controller'));
246
            if (class_exists($dataRecordClassName)) {
247
                $this->dataRecordClassNames[$className] = $dataRecordClassName;
248
                $this->dataRecordClassObjects[$className] = DataObject::get_one(
249
                    $dataRecordClassName,
250
                    null,
251
                    null,
252
                    DB::get_conn()->random() . ' ASC'
253
                );
254
            }
255
        }
256
257
        return $this->dataRecordClassObjects[$className];
258
    }
259
260
    /**
261
     * @param  string $className
262
     * @return array
263
     */
264
    protected function findCustomLinks($className): array
265
    {
266
        $array1 = [];
267
        $array2 = [];
268
        $classObject = $this->findSingleton($className);
269
        if ($classObject) {
270
            if ($classObject->hasMethod('templateOverviewTests')) {
271
                $array1 = $classObject->templateOverviewTests();
272
            }
273
        }
274
        $object = $this->findDataRecord($className);
275
        if ($object) {
276
            if ($object->hasMethod('templateOverviewTests')) {
277
                $array2 = $object->templateOverviewTests();
278
            }
279
        }
280
        return $array1 + $array2;
281
    }
282
283
    /**
284
     * @param  string $className
285
     * @return array
286
     */
287
    protected function findAllowedActions($className): array
288
    {
289
        $allowedActions = Config::inst()->get($className, 'allowed_actions', Config::UNINHERITED);
290
        if (is_array($allowedActions)) {
291
            return $allowedActions;
292
        }
293
        return [];
294
    }
295
296
    /**
297
     * @param  string $className
298
     * @return string
299
     */
300
    protected function findLink($className): string
301
    {
302
        $link = $this->findControllerLink($className);
303
        if (! $link) {
304
            $link = $this->findRouteLink($className);
305
            if (! $link) {
306
                $link = $this->findSegmentLink($className);
307
                if (! $link) {
308
                    $link = $this->findMethodLink($className);
309
                }
310
            }
311
        }
312
313
        return $link;
314
    }
315
316
    /**
317
     * @param  string $className
318
     * @return string
319
     */
320
    protected function findControllerLink($className): string
321
    {
322
        $object = $this->findDataRecord($className);
323
        if ($object) {
324
            $tmp = $object->Link();
325
            $tmpArray = explode('?', $tmp);
326
            return $tmpArray[0];
327
        }
328
329
        return '';
330
    }
331
332
    /**
333
     * @param  string $className
334
     * @return string
335
     */
336
    protected function findRouteLink($className): string
337
    {
338
        if ($this->routes === null) {
339
            $this->routes = Config::inst()->get(Director::class, 'rules');
340
        }
341
        $route = array_search($className, $this->routes, true);
342
        if ($route) {
343
            $routeArray = explode('//', $route);
344
            return $routeArray[0];
345
        }
346
347
        return '';
348
    }
349
350
    /**
351
     * @param  string $className
352
     * @return string
353
     */
354
    protected function findSegmentLink($className): string
355
    {
356
        //check if there is a link of some sort
357
        $urlSegment = Config::inst()->get($className, 'url_segment');
358
        if ($urlSegment) {
359
            $urlSegment .= '/';
360
        } else {
361
            $urlSegment = '';
362
        }
363
        return $urlSegment;
364
    }
365
366
    /**
367
     * @param  string $className
368
     * @return string
369
     */
370
    protected function findMethodLink($className): string
371
    {
372
        $controllerReflectionClass = $this->controllerReflectionClass($className);
373
        if ($controllerReflectionClass) {
374
            foreach ($controllerReflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
375
                if ($method->class === $className) {
376
                    if ($method->name === 'Link') {
377
                        $classObject = $this->findSingleton($className);
378
                        if ($classObject) {
379
                            return $classObject->Link();
380
                        }
381
                    }
382
                }
383
            }
384
        }
385
386
        return '';
387
    }
388
}
389