Passed
Push — master ( 01a58f...dd3b71 )
by Nicolaas
06:08 queued 04:12
created

AllLinks::sanitise_class_name()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Sunnysideup\TemplateOverview\Api;
4
5
use ReflectionClass;
6
7
8
9
10
11
use SilverStripe\Admin\CMSMenu;
12
use SilverStripe\CMS\Model\SiteTree;
13
use SilverStripe\Control\Director;
14
use SilverStripe\Core\ClassInfo;
15
use SilverStripe\Core\Config\Configurable;
16
use SilverStripe\Core\Extensible;
17
use SilverStripe\Core\Injector\Injectable;
18
use SilverStripe\Core\Injector\Injector;
19
20
21
use SilverStripe\ORM\DataObject;
22
use SilverStripe\ORM\DB;
23
use SilverStripe\Versioned\Versioned;
24
25
class AllLinks
26
{
27
    use Extensible;
28
    use Injectable;
29
    use Configurable;
30
31
    /**
32
     * @var array
33
     */
34
    protected $allNonCMSLinks = [];
35
36
    /**
37
     * @var array
38
     */
39
    protected $pagesOnFrontEnd = [];
40
41
    /**
42
     * @var array
43
     */
44
    protected $dataObjectsOnFrontEnd = [];
45
46
    /**
47
     * @var array
48
     */
49
    protected $otherControllerMethods = [];
50
51
    /**
52
     * @var array
53
     */
54
    protected $customNonCMSLinks = [];
55
56
    /**
57
     * @var array
58
     */
59
    protected $allCMSLinks = [];
60
61
    /**
62
     * @var array
63
     */
64
    protected $pagesInCMS = [];
65
66
    /**
67
     * @var array
68
     */
69
    protected $dataObjectsInCMS = [];
70
71
    /**
72
     * @var array
73
     */
74
    protected $modelAdmins = [];
75
76
    /**
77
     * @var array
78
     */
79
    protected $leftAndMainLnks = [];
80
81
    /**
82
     * @var array
83
     */
84
    protected $customCMSLinks = [];
85
86
    /**
87
     * @var array
88
     * Pages to check by class name. For example, for "ClassPage", will check the first instance of the cart page.
89
     */
90
    protected $siteTreeClassNames = [];
91
92
    /**
93
     * url snippets that if found in links should exclude the link altogether.
94
     * e.g. 'admin/registry'
95
     *
96
     * @var array
97
     */
98
    private static $exclude_list = [];
99
100
    /**
101
     * @var int
102
     */
103
    private static $number_of_examples = 1;
104
105
    /**
106
     * @var array
107
     */
108
    private static $custom_links = [];
109
110
    /**
111
     * @var array
112
     */
113
    private static $controller_name_space_filter = [];
114
115
    /**
116
     * @param  string  $link
117
     * @return bool
118
     */
119
    public static function is_admin_link($link): bool
120
    {
121
        return substr(ltrim($link, '/'), 0, 5) === 'admin' ? true : false;
122
    }
123
124
    /**
125
     * Sanitise a model class' name for inclusion in a link
126
     *
127
     * @param string $class
128
     * @return string
129
     */
130
    public static function sanitise_class_name($class)
131
    {
132
        return str_replace('\\', '-', $class);
133
    }
134
135
    /**
136
     * returns an array of allNonCMSLinks => [] , allCMSLinks => [], otherControllerMethods => []
137
     * @return array
138
     */
139
    public function getAllLinks()
140
    {
141
        $this->siteTreeClassNames = $this->listOfAllSiteTreeClasses();
142
143
        foreach ($this->Config()->get('custom_links') as $link) {
144
            $link = '/' . ltrim($link, '/') . '/';
145
            if (self::is_admin_link($link)) {
146
                $this->customCMSLinks[] = $link;
147
            } else {
148
                $this->customNonCMSLinks[] = $link;
149
            }
150
        }
151
        $this->pagesOnFrontEnd = $this->ListOfPagesLinks(false);
152
        $this->dataObjectsOnFrontEnd = $this->ListOfDataObjectsLinks(false);
153
154
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->pagesOnFrontEnd);
155
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->dataObjectsOnFrontEnd);
156
        $this->allNonCMSLinks = $this->addToArrayOfLinks($this->allNonCMSLinks, $this->customNonCMSLinks);
157
        sort($this->allNonCMSLinks);
158
159
        $this->pagesInCMS = $this->ListOfPagesLinks(true);
160
        $this->dataObjectsInCMS = $this->ListOfDataObjectsLinks(true);
161
        $this->modelAdmins = $this->ListOfAllModelAdmins();
162
        $this->leftAndMainLnks = $this->ListOfAllLeftAndMains();
163
164
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->pagesInCMS);
165
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->dataObjectsInCMS);
166
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->modelAdmins);
167
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->leftAndMainLnks);
168
        $this->allCMSLinks = $this->addToArrayOfLinks($this->allCMSLinks, $this->customCMSLinks);
169
        sort($this->allCMSLinks);
170
171
        $this->otherControllerMethods = $this->ListOfAllControllerMethods();
172
173
        return [
174
            'allNonCMSLinks' => $this->allNonCMSLinks,
175
            'allCMSLinks' => $this->allCMSLinks,
176
            'otherLinks' => $this->otherControllerMethods,
177
        ];
178
    }
179
180
    /**
181
     * returns a list of all model admin links
182
     * @return array
183
     */
184
    public function ListOfAllModelAdmins()
185
    {
186
        $obj = Injector::inst()->get(AllLinksModelAdmin::class);
187
        $obj->setNumberOfExamples($this->Config()->number_of_examples);
188
189
        return $obj->findModelAdminLinks();
190
    }
191
192
    /**
193
     * @return array
194
     */
195
    public function ListOfAllControllerMethods(): array
196
    {
197
        $finalFinalArray = [];
198
199
        $obj = Injector::inst()->get(AllLinksControllerInfo::class);
200
        $obj->setValidNameSpaces($this->Config()->controller_name_space_filter);
201
202
        $linksAndActions = $obj->getLinksAndActions();
203
        $allowedActions = $linksAndActions['Actions'];
204
        $controllerLinks = $linksAndActions['Links'];
205
        $finalArray = $linksAndActions['CustomLinks'];
206
207
        // die('---');
208
        //construct array!
209
        foreach ($allowedActions as $className => $methods) {
210
            $link = $controllerLinks[$className];
211
            if ($link) {
212
                $finalArray[$link] = $className;
213
            } else {
214
                $link = '???';
215
            }
216
            if (substr($link, -1) !== '/') {
217
                $link .= '/';
218
            }
219
            if (is_array($methods)) {
220
                foreach ($methods as $method) {
221
                    unset($allowedActions[$className][$method]);
222
                    $finalArray[$link . $method . '/'] = $className;
223
                }
224
            }
225
        }
226
227
        foreach ($finalArray as $link => $className) {
228
            $finalFinalArray[] = [
229
                'ClassName' => $className,
230
                'Link' => $link,
231
            ];
232
        }
233
        usort($finalFinalArray, function ($a, $b) {
234
            if ($a['ClassName'] !== $b['ClassName']) {
235
                return $a['ClassName'] <=> $b['ClassName'];
236
            }
237
238
            return $a['Link'] <=> $b['Link'];
239
        });
240
241
        return $finalFinalArray;
242
    }
243
244
    protected function isValidClass($class)
245
    {
246
        $obj = new ReflectionClass($class);
247
        if ($obj->isAbstract()) {
248
            return false;
249
        }
250
        return true;
251
    }
252
253
    /**
254
     * Takes {@link #$classNames}, gets the URL of the first instance of it
255
     * (will exclude extensions of the class) and
256
     * appends to the {@link #$urls} list to be checked
257
     *
258
     * @param bool $pageInCMS
259
     *
260
     * @return array
261
     */
262
    private function ListOfPagesLinks($pageInCMS = false)
263
    {
264
        //first() will return null or the object
265
        $return = [];
266
        foreach ($this->siteTreeClassNames as $class) {
267
            for ($i = 0; $i < $this->Config()->number_of_examples; $i++) {
268
                $excludedClasses = $this->arrayExcept($this->siteTreeClassNames, $class);
269
                $page = Versioned::get_by_stage($class, Versioned::LIVE)
270
                    ->exclude(['ClassName' => $excludedClasses])
271
                    ->sort(DB::get_conn()->random() . ' ASC')
272
                    ->first();
273
                if (! $page) {
274
                    $page = Versioned::get_by_stage($class, Versioned::DRAFT)
275
                        ->exclude(['ClassName' => $excludedClasses])
276
                        ->sort(DB::get_conn()->random() . ' ASC')
277
                        ->first();
278
                }
279
                if ($page) {
280
                    if ($pageInCMS) {
281
                        $url = $page->CMSEditLink();
282
                        $return[] = $url;
283
                        $return[] = str_replace('/edit/', '/settings/', $url);
284
                        $return[] = str_replace('/edit/', '/history/', $url);
285
                    } else {
286
                        $url = $page->Link();
287
                        $return[] = $url;
288
                    }
289
                }
290
            }
291
        }
292
293
        return $return;
294
    }
295
296
    /**
297
     * @param bool $inCMS
298
     *
299
     * @return array
300
     */
301
    private function ListOfDataObjectsLinks($inCMS = false)
302
    {
303
        //first() will return null or the object
304
        $return = [];
305
        $list = ClassInfo::subclassesFor(DataObject::class);
306
        foreach ($list as $class) {
307
            if (! in_array($class, array_merge($this->siteTreeClassNames, [DataObject::class]), true)) {
308
                if ($this->isValidClass($class)) {
309
                    for ($i = 0; $i < $this->Config()->number_of_examples; $i++) {
310
                        $obj = DataObject::get_one(
311
                            $class,
312
                            ['ClassName' => $class],
313
                            null,
314
                            DB::get_conn()->random() . ' ASC'
315
                        );
316
                        if ($obj) {
317
                            if ($inCMS) {
318
                                if ($obj->hasMethod('CMSEditLink')) {
319
                                    $return[] = $obj->CMSEditLink();
320
                                }
321
                                if ($obj->hasMethod('CMSAddLink')) {
322
                                    $return[] = $obj->CMSAddLink();
323
                                }
324
                                if ($obj->hasMethod('CMSListLink')) {
325
                                    $return[] = $obj->CMSListLink();
326
                                }
327
                                if ($obj->hasMethod('PreviewLink')) {
328
                                    $return[] = $obj->PreviewLink();
329
                                }
330
                            } else {
331
                                if ($obj->hasMethod('Link')) {
332
                                    $return[] = $obj->Link();
333
                                }
334
                                if ($obj->hasMethod('getLink')) {
335
                                    $return[] = $obj->getLink();
336
                                }
337
                            }
338
                        }
339
                    }
340
                }
341
            }
342
        }
343
344
        return $return;
345
    }
346
347
    /**
348
     * @return array
349
     */
350
    private function ListOfAllLeftAndMains()
351
    {
352
        //first() will return null or the object
353
        $return = [];
354
        $list = CMSMenu::get_cms_classes();
355
        foreach ($list as $class) {
356
            if ($this->isValidClass($class)) {
357
                $obj = Injector::inst()->get($class);
358
                if ($obj) {
359
                    $url = $obj->Link();
360
                    if ($url) {
361
                        $return[] = $url;
362
                    }
363
                }
364
            }
365
        }
366
367
        return $return;
368
    }
369
370
    /**
371
     * Pushes an array of items to an array
372
     * @param array $array Array to push items to (will overwrite)
373
     * @param array $pushArray Array of items to push to $array.
374
     *
375
     * @return array
376
     */
377
    private function addToArrayOfLinks($array, $pushArray): array
378
    {
379
        $excludeList = $this->Config()->exclude_list;
380
        foreach ($pushArray as $pushItem) {
381
            //clean
382
            if (self::is_admin_link($pushItem)) {
383
                $pushItem = str_replace('?stage=Stage', '', $pushItem);
384
            }
385
            $pushItem = self::sanitise_class_name($pushItem);
386
            $pushItem = '/' . Director::makeRelative($pushItem);
387
388
            if (is_array($excludeList) && count($excludeList)) {
389
                foreach ($excludeList as $excludeItem) {
390
                    if (stripos($pushItem, $excludeItem) !== false) {
391
                        continue 2;
392
                    }
393
                }
394
            }
395
            if (! in_array($pushItem, $array, true)) {
396
                array_push($array, $pushItem);
397
            }
398
        }
399
        return $array;
400
    }
401
402
    /**
403
     * returns a list of all SiteTree Classes
404
     * @return array
405
     */
406
    private function listOfAllSiteTreeClasses()
407
    {
408
        $pages = [];
409
        $siteTreeDetails = Injector::inst()->get(SiteTreeDetails::class);
410
        $list = $siteTreeDetails->ListOfAllSiteTreeClasses();
411
        foreach ($list as $page) {
412
            $pages[] = $page->ClassName;
413
        }
414
415
        return $pages;
416
    }
417
418
    /**
419
     * Takes an array, takes one item out, and returns new array
420
     *
421
     * @param array $array Array which will have an item taken out of it.
422
     * @param string $exclusion Item to be taken out of $array
423
     *
424
     * @return array New array.
425
     */
426
    private function arrayExcept($array, $exclusion)
427
    {
428
        $newArray = $array;
429
        for ($i = 0; $i < count($newArray); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
430
            if ($newArray[$i] === $exclusion) {
431
                unset($newArray[$i]);
432
            }
433
        }
434
        return $newArray;
435
    }
436
}
437